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