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