Skip to content

Commit 68526c8

Browse files
autocomplete changes
1 parent 77c6779 commit 68526c8

File tree

9 files changed

+394
-163
lines changed

9 files changed

+394
-163
lines changed

ydb/core/viewer/json_autocomplete.h

Lines changed: 108 additions & 109 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,33 @@ 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+
std::optional<NKikimrSchemeOp::EPathType> PathType;
33+
std::optional<NKikimrViewerSyntax2::ETableComponentType> TableComponentType;
3134
TString Table;
32-
SchemaWordData() {}
33-
SchemaWordData(const TString& name, const TString& type, const TString& table = "")
35+
TSchemaWordData() {}
36+
TSchemaWordData(const TString& name, const NKikimrSchemeOp::EPathType type, const TString& table = "")
3437
: Name(name)
35-
, Type(type)
38+
, PathType(type)
39+
, Table(table)
40+
{}
41+
TSchemaWordData(const TString& name, const NKikimrViewerSyntax2::ETableComponentType type, const TString& table = "")
42+
: Name(name)
43+
, TableComponentType(type)
3644
, Table(table)
3745
{}
3846
};
39-
THashMap<TString, SchemaWordData> Dictionary;
47+
THashMap<TString, TSchemaWordData> Dictionary;
4048
TString Database;
49+
TVector<TString> Tables;
4150
TVector<TString> Paths;
4251
TString Prefix;
43-
TString PrefixPath;
52+
TString SearchWord;
4453
ui32 Limit = 10;
4554
NKikimrViewer::TQueryAutocomplete Result;
4655

@@ -63,6 +72,7 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
6372
TStringBuf content = Event->Get()->Request.GetPostContent();
6473
ParsePostContent(content);
6574
}
75+
PrepareParameters();
6676
}
6777

6878
// proxied request
@@ -72,43 +82,49 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
7282
auto& request = ViewerRequest->Get()->Record.GetAutocompleteRequest();
7383

7484
Database = request.GetDatabase();
75-
for (auto& path: request.GetTables()) {
76-
Paths.emplace_back(path);
85+
for (auto& table: request.GetTables()) {
86+
Tables.emplace_back(table);
7787
}
7888
Prefix = request.GetPrefix();
7989

8090
Timeout = ViewerRequest->Get()->Record.GetTimeout();
8191
Direct = true;
92+
PrepareParameters();
8293
}
8394

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;
95+
void PrepareParameters() {
96+
if (Database) {
97+
TString prefixUpToLastSlash = "";
9998
auto splitPos = Prefix.find_last_of('/');
10099
if (splitPos != std::string::npos) {
101-
path += "/" + Prefix.substr(0, splitPos);
102-
Prefix = Prefix.substr(splitPos + 1);
100+
prefixUpToLastSlash += Prefix.substr(0, splitPos);
101+
SearchWord = Prefix.substr(splitPos + 1);
102+
} else {
103+
SearchWord = Prefix;
103104
}
105+
106+
if (Tables.size() == 0) {
107+
Paths.emplace_back(Database);
108+
} else {
109+
for (TString& table: Tables) {
110+
TString path = table;
111+
if (!table.StartsWith(Database)) {
112+
path = Database + "/" + path;
113+
}
114+
path += "/" + prefixUpToLastSlash;
115+
Paths.emplace_back(path);
116+
}
117+
}
118+
} else {
119+
SearchWord = Prefix;
104120
}
105121
}
106122

107123
void ParseCgiParameters(const TCgiParameters& params) {
108124
JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool>(params.Get("enums"), true);
109125
JsonSettings.UI64AsString = !FromStringWithDefault<bool>(params.Get("ui64"), false);
110126
Database = params.Get("database");
111-
StringSplitter(params.Get("table")).Split(',').SkipEmpty().Collect(&Paths);
127+
StringSplitter(params.Get("table")).Split(',').SkipEmpty().Collect(&Tables);
112128
Prefix = params.Get("prefix");
113129
Limit = FromStringWithDefault<ui32>(params.Get("limit"), Limit);
114130
Direct = FromStringWithDefault<bool>(params.Get("direct"), Direct);
@@ -121,8 +137,10 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
121137
bool success = NJson::ReadJsonTree(content, &JsonConfig, &requestData);
122138
if (success) {
123139
Database = Database.empty() ? requestData["database"].GetStringSafe({}) : Database;
124-
for (auto& table: requestData["tables"].GetArraySafe()) {
125-
Paths.emplace_back(table.GetStringSafe());
140+
if (requestData["table"].IsArray()) {
141+
for (auto& table: requestData["table"].GetArraySafe()) {
142+
Tables.emplace_back(table.GetStringSafe());
143+
}
126144
}
127145
Prefix = Prefix.empty() ? requestData["prefix"].GetStringSafe({}) : Prefix;
128146
}
@@ -157,38 +175,41 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
157175

158176
void Bootstrap() {
159177
if (ViewerRequest) {
160-
// proxied request
161-
PreparePaths();
178+
// handle proxied request
162179
SendSchemeCacheRequest();
163180
} else if (!Database) {
164-
// autocomplete databases via console request
181+
// autocomplete database list via console request
165182
RequestConsoleListTenants();
166183
} else {
167184
if (!Direct) {
168-
// autocomplete with proxy
169-
RequestStateStorageEndpointsLookup(Database); // to find some dynamic node and redirect there
185+
// proxy request to a dynamic node of the specified database
186+
RequestStateStorageEndpointsLookup(Database);
170187
}
171188
if (Requests == 0) {
172-
// autocomplete without proxy
173-
PreparePaths();
189+
// perform autocomplete without proxying
174190
SendSchemeCacheRequest();
175191
}
176192
}
177193

178-
179194
Become(&TThis::StateRequestedDescribe, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
180195
}
181196

182197
void Connected(TEvInterconnect::TEvNodeConnected::TPtr &) {}
183198

184199
void Undelivered(TEvents::TEvUndelivered::TPtr &ev) {
185-
if (ev->Get()->SourceType == NViewer::TEvViewer::EvViewerRequest) {
186-
SendSchemeCacheRequest();
200+
if (!Direct && ev->Get()->SourceType == NViewer::TEvViewer::EvViewerRequest) {
201+
Direct = true;
202+
SendSchemeCacheRequest(); // fallback
203+
RequestDone();
187204
}
188205
}
189206

190207
void Disconnected(TEvInterconnect::TEvNodeDisconnected::TPtr &) {
191-
SendSchemeCacheRequest();
208+
if (!Direct) {
209+
Direct = true;
210+
SendSchemeCacheRequest(); // fallback
211+
RequestDone();
212+
}
192213
}
193214

194215
void Handle(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
@@ -203,6 +224,7 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
203224
} else {
204225
SendDynamicNodeAutocompleteRequest();
205226
}
227+
RequestDone();
206228
}
207229

208230
void SendSchemeCacheRequest() {
@@ -253,90 +275,61 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
253275
}
254276
}
255277

256-
TString ConvertType(TNavigate::EKind navigate) {
257-
switch (navigate) {
258-
case TNavigate::KindSubdomain:
259-
return "subdomain";
260-
case TNavigate::KindPath:
261-
return "directory";
262-
case TNavigate::KindExtSubdomain:
263-
return "database";
264-
case TNavigate::KindTable:
265-
return "table";
266-
case TNavigate::KindOlapStore:
267-
return "columnStore";
268-
case TNavigate::KindColumnTable:
269-
return "columnTable";
270-
case TNavigate::KindRtmr:
271-
return "rtmrVolume";
272-
case TNavigate::KindKesus:
273-
return "kesus";
274-
case TNavigate::KindSolomon:
275-
return "solomonVolume";
276-
case TNavigate::KindTopic:
277-
return "persQueueGroup";
278-
case TNavigate::KindCdcStream:
279-
return "cdcStream";
280-
case TNavigate::KindSequence:
281-
return "sequence";
282-
case TNavigate::KindReplication:
283-
return "replication";
284-
case TNavigate::KindBlobDepot:
285-
return "blobDepot";
286-
case TNavigate::KindExternalTable:
287-
return "externalTable";
288-
case TNavigate::KindExternalDataSource:
289-
return "externalDataSource";
290-
case TNavigate::KindBlockStoreVolume:
291-
return "blockStoreVolume";
292-
case TNavigate::KindFileStore:
293-
return "fileStore";
294-
case TNavigate::KindView:
295-
return "view";
296-
default:
297-
return "directory";
278+
void ParseProxyResult() {
279+
if (ProxyResult == nullptr) {
280+
Result.add_error("Failed to collect information from ProxyResult");
281+
return;
282+
}
283+
if (ProxyResult->Record.HasAutocompleteResponse()) {
284+
Result = ProxyResult->Record.GetAutocompleteResponse();
285+
} else {
286+
Result.add_error("Proxying return empty response");
298287
}
288+
299289
}
300290

301291
void ParseConsoleResult() {
302292
if (ConsoleResult == nullptr) {
303-
Result.add_error("Failed to collect information");
293+
Result.add_error("Failed to collect information from ConsoleResult");
304294
return;
305295
}
306296

307297
Ydb::Cms::ListDatabasesResult listTenantsResult;
308298
ConsoleResult->Record.GetResponse().operation().result().UnpackTo(&listTenantsResult);
309299
for (const TString& path : listTenantsResult.paths()) {
310-
Dictionary[path] = SchemaWordData(path, "database");
300+
Dictionary[path] = TSchemaWordData(path, NKikimrSchemeOp::EPathType::EPathTypeExtSubDomain);
311301
}
312-
RequestDone();
313302
}
314303

315304
void ParseCacheResult() {
316305
if (CacheResult == nullptr) {
317-
Result.add_error("Failed to collect information");
306+
Result.add_error("Failed to collect information from CacheResult");
318307
return;
319308
}
320309
NSchemeCache::TSchemeCacheNavigate *navigate = CacheResult->Request.Get();
321310
if (navigate->ErrorCount > 0) {
322-
Result.add_error("Inner errors while collected information");
311+
for (auto& entry: CacheResult->Request.Get()->ResultSet) {
312+
if (entry.Status != TSchemeCacheNavigate::EStatus::Ok) {
313+
Result.add_error(TStringBuilder() << "Error receiving Navigate response: `" << CanonizePath(entry.Path) << "` has <" << ToString(entry.Status) << "> status");
314+
}
315+
}
323316
return;
324317
}
325318
for (auto& entry: CacheResult->Request.Get()->ResultSet) {
326319
TString path = CanonizePath(entry.Path);
327320
if (entry.ListNodeEntry) {
328321
for (const auto& child : entry.ListNodeEntry->Children) {
329-
Dictionary[child.Name] = SchemaWordData(child.Name, ConvertType(child.Kind), path);
322+
Dictionary[child.Name] = TSchemaWordData(child.Name, ConvertType(child.Kind), path);
330323
}
331324
};
332325
for (const auto& [id, column] : entry.Columns) {
333-
Dictionary[column.Name] = SchemaWordData(column.Name, path, "column");
326+
Dictionary[column.Name] = TSchemaWordData(column.Name, NKikimrViewerSyntax2::Column, path);
334327
}
335328
for (const auto& index : entry.Indexes) {
336-
Dictionary[index.GetName()] = SchemaWordData(index.GetName(), path, "index");
329+
Dictionary[index.GetName()] = TSchemaWordData(index.GetName(), NKikimrViewerSyntax2::Index, path);
337330
}
338331
for (const auto& cdcStream : entry.CdcStreams) {
339-
Dictionary[cdcStream.GetName()] = SchemaWordData(cdcStream.GetName(), path, "cdcstream");
332+
Dictionary[cdcStream.GetName()] = TSchemaWordData(cdcStream.GetName(), NKikimrViewerSyntax2::CdcStream, path);
340333
}
341334
}
342335
}
@@ -364,23 +357,31 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
364357
}
365358

366359
void ReplyAndPassAway() {
367-
if (!Database) {
368-
ParseConsoleResult();
369-
} else {
360+
if (ProxyResult) {
361+
ParseProxyResult();
362+
} else if (Database) {
370363
ParseCacheResult();
364+
} else {
365+
ParseConsoleResult();
371366
}
372367

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);
368+
if (!ProxyResult) {
369+
Result.set_success(Result.error_size() == 0);
370+
if (Result.error_size() == 0) {
371+
auto fuzzy = FuzzySearcher<TSchemaWordData>(Dictionary);
372+
auto autocomplete = fuzzy.Search(SearchWord, Limit);
373+
Result.MutableResult()->SetTotal(autocomplete.size());
374+
for (TSchemaWordData& wordData: autocomplete) {
375+
auto entity = Result.MutableResult()->AddEntities();
376+
entity->SetName(wordData.Name);
377+
if (wordData.PathType) {
378+
entity->SetPathType(wordData.PathType.value());
379+
} else {
380+
entity->SetTableComponentType(wordData.TableComponentType.value());
381+
}
382+
if (wordData.Table) {
383+
entity->SetParent(wordData.Table);
384+
}
384385
}
385386
}
386387
}
@@ -390,10 +391,8 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
390391
}
391392

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

399398
void HandleTimeout() {

0 commit comments

Comments
 (0)