Skip to content

Commit b1098e1

Browse files
autocomplete changes
1 parent 77c6779 commit b1098e1

File tree

5 files changed

+376
-103
lines changed

5 files changed

+376
-103
lines changed

ydb/core/viewer/json_autocomplete.h

Lines changed: 132 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <ydb/core/base/tablet.h>
55
#include <ydb/core/base/tablet_pipe.h>
66
#include <ydb/library/services/services.pb.h>
7+
#include <ydb/core/viewer/protos/viewer.pb.h>
78
#include <ydb/core/viewer/json/json.h>
89
#include "viewer.h"
910
#include "query_autocomplete_helper.h"
@@ -22,25 +23,27 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
2223
TJsonSettings JsonSettings;
2324
ui32 Timeout = 0;
2425

26+
TAutoPtr<TEvViewer::TEvViewerResponse> ProxyResult;
2527
TAutoPtr<NConsole::TEvConsole::TEvListTenantsResponse> ConsoleResult;
2628
TAutoPtr<TEvTxProxySchemeCache::TEvNavigateKeySetResult> CacheResult;
2729

28-
struct SchemaWordData {
30+
struct TSchemaWordData {
2931
TString Name;
30-
TString Type;
32+
NKikimrViewer::EAutocompleteType Type;
3133
TString Table;
32-
SchemaWordData() {}
33-
SchemaWordData(const TString& name, const TString& type, const TString& table = "")
34+
TSchemaWordData() {}
35+
TSchemaWordData(const TString& name, const NKikimrViewer::EAutocompleteType type, const TString& table = "")
3436
: Name(name)
3537
, Type(type)
3638
, Table(table)
3739
{}
3840
};
39-
THashMap<TString, SchemaWordData> Dictionary;
41+
THashMap<TString, TSchemaWordData> Dictionary;
4042
TString Database;
43+
TVector<TString> Tables;
4144
TVector<TString> Paths;
4245
TString Prefix;
43-
TString PrefixPath;
46+
TString SearchWord;
4447
ui32 Limit = 10;
4548
NKikimrViewer::TQueryAutocomplete Result;
4649

@@ -63,6 +66,7 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
6366
TStringBuf content = Event->Get()->Request.GetPostContent();
6467
ParsePostContent(content);
6568
}
69+
PrepareParameters();
6670
}
6771

6872
// proxied request
@@ -72,43 +76,49 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
7276
auto& request = ViewerRequest->Get()->Record.GetAutocompleteRequest();
7377

7478
Database = request.GetDatabase();
75-
for (auto& path: request.GetTables()) {
76-
Paths.emplace_back(path);
79+
for (auto& table: request.GetTables()) {
80+
Tables.emplace_back(table);
7781
}
7882
Prefix = request.GetPrefix();
7983

8084
Timeout = ViewerRequest->Get()->Record.GetTimeout();
8185
Direct = true;
86+
PrepareParameters();
8287
}
8388

84-
void PreparePaths() {
85-
if (Paths.size() == 0) {
86-
Paths.emplace_back("");
87-
}
88-
TString prefixPath = "";
89-
auto splitPos = Prefix.find_last_of('/');
90-
if (splitPos != std::string::npos) {
91-
prefixPath += "/" + Prefix.substr(0, splitPos);
92-
Prefix = Prefix.substr(splitPos + 1);
93-
}
94-
for (TString& path: Paths) {
95-
if (!path.StartsWith(Database)) {
96-
path = Database + "/" + path;
97-
}
98-
path += prefixPath;
89+
void PrepareParameters() {
90+
if (Database) {
91+
TString prefixUpToLastSlash = "";
9992
auto splitPos = Prefix.find_last_of('/');
10093
if (splitPos != std::string::npos) {
101-
path += "/" + Prefix.substr(0, splitPos);
102-
Prefix = Prefix.substr(splitPos + 1);
94+
prefixUpToLastSlash += Prefix.substr(0, splitPos);
95+
SearchWord = Prefix.substr(splitPos + 1);
96+
} else {
97+
SearchWord = Prefix;
98+
}
99+
100+
if (Tables.size() == 0) {
101+
Paths.emplace_back(Database);
102+
} else {
103+
for (TString& table: Tables) {
104+
TString path = table;
105+
if (!table.StartsWith(Database)) {
106+
path = Database + "/" + path;
107+
}
108+
path += "/" + prefixUpToLastSlash;
109+
Paths.emplace_back(path);
110+
}
103111
}
112+
} else {
113+
SearchWord = Prefix;
104114
}
105115
}
106116

107117
void ParseCgiParameters(const TCgiParameters& params) {
108118
JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool>(params.Get("enums"), true);
109119
JsonSettings.UI64AsString = !FromStringWithDefault<bool>(params.Get("ui64"), false);
110120
Database = params.Get("database");
111-
StringSplitter(params.Get("table")).Split(',').SkipEmpty().Collect(&Paths);
121+
StringSplitter(params.Get("table")).Split(',').SkipEmpty().Collect(&Tables);
112122
Prefix = params.Get("prefix");
113123
Limit = FromStringWithDefault<ui32>(params.Get("limit"), Limit);
114124
Direct = FromStringWithDefault<bool>(params.Get("direct"), Direct);
@@ -121,8 +131,10 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
121131
bool success = NJson::ReadJsonTree(content, &JsonConfig, &requestData);
122132
if (success) {
123133
Database = Database.empty() ? requestData["database"].GetStringSafe({}) : Database;
124-
for (auto& table: requestData["tables"].GetArraySafe()) {
125-
Paths.emplace_back(table.GetStringSafe());
134+
if (requestData["table"].IsArray()) {
135+
for (auto& table: requestData["table"].GetArraySafe()) {
136+
Tables.emplace_back(table.GetStringSafe());
137+
}
126138
}
127139
Prefix = Prefix.empty() ? requestData["prefix"].GetStringSafe({}) : Prefix;
128140
}
@@ -157,38 +169,41 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
157169

158170
void Bootstrap() {
159171
if (ViewerRequest) {
160-
// proxied request
161-
PreparePaths();
172+
// handle proxied request
162173
SendSchemeCacheRequest();
163174
} else if (!Database) {
164-
// autocomplete databases via console request
175+
// autocomplete database list via console request
165176
RequestConsoleListTenants();
166177
} else {
167178
if (!Direct) {
168-
// autocomplete with proxy
169-
RequestStateStorageEndpointsLookup(Database); // to find some dynamic node and redirect there
179+
// proxy request to a dynamic node of the specified database
180+
RequestStateStorageEndpointsLookup(Database);
170181
}
171182
if (Requests == 0) {
172-
// autocomplete without proxy
173-
PreparePaths();
183+
// perform autocomplete without proxying
174184
SendSchemeCacheRequest();
175185
}
176186
}
177187

178-
179188
Become(&TThis::StateRequestedDescribe, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
180189
}
181190

182191
void Connected(TEvInterconnect::TEvNodeConnected::TPtr &) {}
183192

184193
void Undelivered(TEvents::TEvUndelivered::TPtr &ev) {
185-
if (ev->Get()->SourceType == NViewer::TEvViewer::EvViewerRequest) {
186-
SendSchemeCacheRequest();
194+
if (!Direct && ev->Get()->SourceType == NViewer::TEvViewer::EvViewerRequest) {
195+
Direct = true;
196+
SendSchemeCacheRequest(); // fallback
197+
RequestDone();
187198
}
188199
}
189200

190201
void Disconnected(TEvInterconnect::TEvNodeDisconnected::TPtr &) {
191-
SendSchemeCacheRequest();
202+
if (!Direct) {
203+
Direct = true;
204+
SendSchemeCacheRequest(); // fallback
205+
RequestDone();
206+
}
192207
}
193208

194209
void Handle(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
@@ -203,6 +218,7 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
203218
} else {
204219
SendDynamicNodeAutocompleteRequest();
205220
}
221+
RequestDone();
206222
}
207223

208224
void SendSchemeCacheRequest() {
@@ -253,90 +269,106 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
253269
}
254270
}
255271

256-
TString ConvertType(TNavigate::EKind navigate) {
272+
void ParseProxyResult() {
273+
if (ProxyResult == nullptr) {
274+
Result.add_error("Failed to collect information from ProxyResult");
275+
return;
276+
}
277+
if (ProxyResult->Record.HasAutocompleteResponse()) {
278+
Result = ProxyResult->Record.GetAutocompleteResponse();
279+
} else {
280+
Result.add_error("Proxying return empty response");
281+
}
282+
283+
}
284+
285+
void ParseConsoleResult() {
286+
if (ConsoleResult == nullptr) {
287+
Result.add_error("Failed to collect information from ConsoleResult");
288+
return;
289+
}
290+
291+
Ydb::Cms::ListDatabasesResult listTenantsResult;
292+
ConsoleResult->Record.GetResponse().operation().result().UnpackTo(&listTenantsResult);
293+
for (const TString& path : listTenantsResult.paths()) {
294+
Dictionary[path] = TSchemaWordData(path, NKikimrViewer::ext_sub_domain);
295+
}
296+
}
297+
298+
NKikimrViewer::EAutocompleteType ConvertType(TNavigate::EKind navigate) {
257299
switch (navigate) {
258300
case TNavigate::KindSubdomain:
259-
return "subdomain";
301+
return NKikimrViewer::sub_domain;
260302
case TNavigate::KindPath:
261-
return "directory";
303+
return NKikimrViewer::dir;
262304
case TNavigate::KindExtSubdomain:
263-
return "database";
305+
return NKikimrViewer::ext_sub_domain;
264306
case TNavigate::KindTable:
265-
return "table";
307+
return NKikimrViewer::table;
266308
case TNavigate::KindOlapStore:
267-
return "columnStore";
309+
return NKikimrViewer::column_store;
268310
case TNavigate::KindColumnTable:
269-
return "columnTable";
311+
return NKikimrViewer::column_table;
270312
case TNavigate::KindRtmr:
271-
return "rtmrVolume";
313+
return NKikimrViewer::rtmr_volume;
272314
case TNavigate::KindKesus:
273-
return "kesus";
315+
return NKikimrViewer::kesus;
274316
case TNavigate::KindSolomon:
275-
return "solomonVolume";
317+
return NKikimrViewer::solomon_volume;
276318
case TNavigate::KindTopic:
277-
return "persQueueGroup";
319+
return NKikimrViewer::pers_queue_group;
278320
case TNavigate::KindCdcStream:
279-
return "cdcStream";
321+
return NKikimrViewer::cdc_stream;
280322
case TNavigate::KindSequence:
281-
return "sequence";
323+
return NKikimrViewer::sequence;
282324
case TNavigate::KindReplication:
283-
return "replication";
325+
return NKikimrViewer::replication;
284326
case TNavigate::KindBlobDepot:
285-
return "blobDepot";
327+
return NKikimrViewer::blob_depot;
286328
case TNavigate::KindExternalTable:
287-
return "externalTable";
329+
return NKikimrViewer::external_table;
288330
case TNavigate::KindExternalDataSource:
289-
return "externalDataSource";
331+
return NKikimrViewer::external_data_source;
290332
case TNavigate::KindBlockStoreVolume:
291-
return "blockStoreVolume";
333+
return NKikimrViewer::block_store_volume;
292334
case TNavigate::KindFileStore:
293-
return "fileStore";
335+
return NKikimrViewer::file_store;
294336
case TNavigate::KindView:
295-
return "view";
337+
return NKikimrViewer::view;
296338
default:
297-
return "directory";
298-
}
299-
}
300-
301-
void ParseConsoleResult() {
302-
if (ConsoleResult == nullptr) {
303-
Result.add_error("Failed to collect information");
304-
return;
305-
}
306-
307-
Ydb::Cms::ListDatabasesResult listTenantsResult;
308-
ConsoleResult->Record.GetResponse().operation().result().UnpackTo(&listTenantsResult);
309-
for (const TString& path : listTenantsResult.paths()) {
310-
Dictionary[path] = SchemaWordData(path, "database");
339+
return NKikimrViewer::dir;
311340
}
312-
RequestDone();
313341
}
314342

315343
void ParseCacheResult() {
316344
if (CacheResult == nullptr) {
317-
Result.add_error("Failed to collect information");
345+
Result.add_error("Failed to collect information from CacheResult");
318346
return;
319347
}
320348
NSchemeCache::TSchemeCacheNavigate *navigate = CacheResult->Request.Get();
321349
if (navigate->ErrorCount > 0) {
322-
Result.add_error("Inner errors while collected information");
350+
for (auto& entry: CacheResult->Request.Get()->ResultSet) {
351+
if (entry.Status != TSchemeCacheNavigate::EStatus::Ok) {
352+
Result.add_error(TStringBuilder() << "Error receiving Navigate response: `" << CanonizePath(entry.Path) << "` has <" << ToString(entry.Status) << "> status");
353+
}
354+
}
323355
return;
324356
}
325357
for (auto& entry: CacheResult->Request.Get()->ResultSet) {
326358
TString path = CanonizePath(entry.Path);
327359
if (entry.ListNodeEntry) {
328360
for (const auto& child : entry.ListNodeEntry->Children) {
329-
Dictionary[child.Name] = SchemaWordData(child.Name, ConvertType(child.Kind), path);
361+
Dictionary[child.Name] = TSchemaWordData(child.Name, ConvertType(child.Kind), path);
330362
}
331363
};
332364
for (const auto& [id, column] : entry.Columns) {
333-
Dictionary[column.Name] = SchemaWordData(column.Name, path, "column");
365+
Dictionary[column.Name] = TSchemaWordData(column.Name, NKikimrViewer::column, path);
334366
}
335367
for (const auto& index : entry.Indexes) {
336-
Dictionary[index.GetName()] = SchemaWordData(index.GetName(), path, "index");
368+
Dictionary[index.GetName()] = TSchemaWordData(index.GetName(), NKikimrViewer::index, path);
337369
}
338370
for (const auto& cdcStream : entry.CdcStreams) {
339-
Dictionary[cdcStream.GetName()] = SchemaWordData(cdcStream.GetName(), path, "cdcstream");
371+
Dictionary[cdcStream.GetName()] = TSchemaWordData(cdcStream.GetName(), NKikimrViewer::cdc_stream, path);
340372
}
341373
}
342374
}
@@ -364,23 +396,27 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
364396
}
365397

366398
void ReplyAndPassAway() {
367-
if (!Database) {
368-
ParseConsoleResult();
369-
} else {
399+
if (ProxyResult) {
400+
ParseProxyResult();
401+
} else if (Database) {
370402
ParseCacheResult();
403+
} else {
404+
ParseConsoleResult();
371405
}
372406

373-
Result.set_success(Result.error_size() == 0);
374-
if (Result.error_size() == 0) {
375-
auto fuzzy = FuzzySearcher<SchemaWordData>(Dictionary);
376-
auto autocomplete = fuzzy.Search(Prefix, Limit);
377-
Result.MutableResult()->SetTotal(autocomplete.size());
378-
for (SchemaWordData& wordData: autocomplete) {
379-
auto entity = Result.MutableResult()->AddEntities();
380-
entity->SetName(wordData.Name);
381-
entity->SetType(wordData.Type);
382-
if (wordData.Table) {
383-
entity->SetParent(wordData.Table);
407+
if (!ProxyResult) {
408+
Result.set_success(Result.error_size() == 0);
409+
if (Result.error_size() == 0) {
410+
auto fuzzy = FuzzySearcher<TSchemaWordData>(Dictionary);
411+
auto autocomplete = fuzzy.Search(SearchWord, Limit);
412+
Result.MutableResult()->SetTotal(autocomplete.size());
413+
for (TSchemaWordData& wordData: autocomplete) {
414+
auto entity = Result.MutableResult()->AddEntities();
415+
entity->SetName(wordData.Name);
416+
entity->SetType(wordData.Type);
417+
if (wordData.Table) {
418+
entity->SetParent(wordData.Table);
419+
}
384420
}
385421
}
386422
}
@@ -390,10 +426,8 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
390426
}
391427

392428
void Handle(TEvViewer::TEvViewerResponse::TPtr& ev) {
393-
if (ev.Get()->Get()->Record.HasAutocompleteResponse()) {
394-
Result = ev.Get()->Get()->Record.GetAutocompleteResponse();
395-
}
396-
SendAutocompleteResponse();
429+
ProxyResult = ev.Release()->Release();
430+
RequestDone();
397431
}
398432

399433
void HandleTimeout() {

0 commit comments

Comments
 (0)