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"
@@ -13,6 +14,7 @@ namespace NViewer {
1314
1415using namespace NActors ;
1516using TNavigate = NSchemeCache::TSchemeCacheNavigate;
17+ using TEntity = NKikimrViewer::TQueryAutocomplete_TResult_TEntity;
1618
1719class TJsonAutocomplete : public TViewerPipeClient <TJsonAutocomplete> {
1820 using TBase = TViewerPipeClient<TJsonAutocomplete>;
@@ -22,25 +24,27 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
2224 TJsonSettings JsonSettings;
2325 ui32 Timeout = 0 ;
2426
27+ TAutoPtr<TEvViewer::TEvViewerResponse> ProxyResult;
2528 TAutoPtr<NConsole::TEvConsole::TEvListTenantsResponse> ConsoleResult;
2629 TAutoPtr<TEvTxProxySchemeCache::TEvNavigateKeySetResult> CacheResult;
2730
28- struct SchemaWordData {
31+ struct TSchemaWordData {
2932 TString Name;
30- TString Type;
33+ TEntity::EType Type;
3134 TString Table;
32- SchemaWordData () {}
33- SchemaWordData (const TString& name, const TString& type, const TString& table = " " )
35+ TSchemaWordData () {}
36+ TSchemaWordData (const TString& name, const TEntity::EType type, const TString& table = " " )
3437 : Name(name)
3538 , Type(type)
3639 , Table(table)
3740 {}
3841 };
39- THashMap<TString, SchemaWordData > Dictionary;
42+ THashMap<TString, TSchemaWordData > Dictionary;
4043 TString Database;
44+ TVector<TString> Tables;
4145 TVector<TString> Paths;
4246 TString Prefix;
43- TString PrefixPath ;
47+ TString SearchWord ;
4448 ui32 Limit = 10 ;
4549 NKikimrViewer::TQueryAutocomplete Result;
4650
@@ -63,6 +67,7 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
6367 TStringBuf content = Event->Get ()->Request .GetPostContent ();
6468 ParsePostContent (content);
6569 }
70+ PrepareParameters ();
6671 }
6772
6873 // proxied request
@@ -72,43 +77,49 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
7277 auto & request = ViewerRequest->Get ()->Record .GetAutocompleteRequest ();
7378
7479 Database = request.GetDatabase ();
75- for (auto & path : request.GetTables ()) {
76- Paths .emplace_back (path );
80+ for (auto & table : request.GetTables ()) {
81+ Tables .emplace_back (table );
7782 }
7883 Prefix = request.GetPrefix ();
7984
8085 Timeout = ViewerRequest->Get ()->Record .GetTimeout ();
8186 Direct = true ;
87+ PrepareParameters ();
8288 }
8389
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;
90+ void PrepareParameters () {
91+ if (Database) {
92+ TString prefixUpToLastSlash = " " ;
9993 auto splitPos = Prefix.find_last_of (' /' );
10094 if (splitPos != std::string::npos) {
101- path += " /" + Prefix.substr (0 , splitPos);
102- Prefix = Prefix.substr (splitPos + 1 );
95+ prefixUpToLastSlash += Prefix.substr (0 , splitPos);
96+ SearchWord = Prefix.substr (splitPos + 1 );
97+ } else {
98+ SearchWord = Prefix;
10399 }
100+
101+ if (Tables.size () == 0 ) {
102+ Paths.emplace_back (Database);
103+ } else {
104+ for (TString& table: Tables) {
105+ TString path = table;
106+ if (!table.StartsWith (Database)) {
107+ path = Database + " /" + path;
108+ }
109+ path += " /" + prefixUpToLastSlash;
110+ Paths.emplace_back (path);
111+ }
112+ }
113+ } else {
114+ SearchWord = Prefix;
104115 }
105116 }
106117
107118 void ParseCgiParameters (const TCgiParameters& params) {
108119 JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool >(params.Get (" enums" ), true );
109120 JsonSettings.UI64AsString = !FromStringWithDefault<bool >(params.Get (" ui64" ), false );
110121 Database = params.Get (" database" );
111- StringSplitter (params.Get (" table" )).Split (' ,' ).SkipEmpty ().Collect (&Paths );
122+ StringSplitter (params.Get (" table" )).Split (' ,' ).SkipEmpty ().Collect (&Tables );
112123 Prefix = params.Get (" prefix" );
113124 Limit = FromStringWithDefault<ui32>(params.Get (" limit" ), Limit);
114125 Direct = FromStringWithDefault<bool >(params.Get (" direct" ), Direct);
@@ -121,8 +132,10 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
121132 bool success = NJson::ReadJsonTree (content, &JsonConfig, &requestData);
122133 if (success) {
123134 Database = Database.empty () ? requestData[" database" ].GetStringSafe ({}) : Database;
124- for (auto & table: requestData[" tables" ].GetArraySafe ()) {
125- Paths.emplace_back (table.GetStringSafe ());
135+ if (requestData[" table" ].IsArray ()) {
136+ for (auto & table: requestData[" table" ].GetArraySafe ()) {
137+ Tables.emplace_back (table.GetStringSafe ());
138+ }
126139 }
127140 Prefix = Prefix.empty () ? requestData[" prefix" ].GetStringSafe ({}) : Prefix;
128141 }
@@ -157,38 +170,41 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
157170
158171 void Bootstrap () {
159172 if (ViewerRequest) {
160- // proxied request
161- PreparePaths ();
173+ // handle proxied request
162174 SendSchemeCacheRequest ();
163175 } else if (!Database) {
164- // autocomplete databases via console request
176+ // autocomplete database list via console request
165177 RequestConsoleListTenants ();
166178 } else {
167179 if (!Direct) {
168- // autocomplete with proxy
169- RequestStateStorageEndpointsLookup (Database); // to find some dynamic node and redirect there
180+ // proxy request to a dynamic node of the specified database
181+ RequestStateStorageEndpointsLookup (Database);
170182 }
171183 if (Requests == 0 ) {
172- // autocomplete without proxy
173- PreparePaths ();
184+ // perform autocomplete without proxying
174185 SendSchemeCacheRequest ();
175186 }
176187 }
177188
178-
179189 Become (&TThis::StateRequestedDescribe, TDuration::MilliSeconds (Timeout), new TEvents::TEvWakeup ());
180190 }
181191
182192 void Connected (TEvInterconnect::TEvNodeConnected::TPtr &) {}
183193
184194 void Undelivered (TEvents::TEvUndelivered::TPtr &ev) {
185- if (ev->Get ()->SourceType == NViewer::TEvViewer::EvViewerRequest) {
186- SendSchemeCacheRequest ();
195+ if (!Direct && ev->Get ()->SourceType == NViewer::TEvViewer::EvViewerRequest) {
196+ Direct = true ;
197+ SendSchemeCacheRequest (); // fallback
198+ RequestDone ();
187199 }
188200 }
189201
190202 void Disconnected (TEvInterconnect::TEvNodeDisconnected::TPtr &) {
191- SendSchemeCacheRequest ();
203+ if (!Direct) {
204+ Direct = true ;
205+ SendSchemeCacheRequest (); // fallback
206+ RequestDone ();
207+ }
192208 }
193209
194210 void Handle (TEvStateStorage::TEvBoardInfo::TPtr& ev) {
@@ -203,6 +219,7 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
203219 } else {
204220 SendDynamicNodeAutocompleteRequest ();
205221 }
222+ RequestDone ();
206223 }
207224
208225 void SendSchemeCacheRequest () {
@@ -253,90 +270,106 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
253270 }
254271 }
255272
256- TString ConvertType (TNavigate::EKind navigate) {
273+ TEntity::EType ConvertType (TNavigate::EKind navigate) {
257274 switch (navigate) {
258275 case TNavigate::KindSubdomain:
259- return " subdomain " ;
276+ return TEntity::Subdomain ;
260277 case TNavigate::KindPath:
261- return " directory " ;
278+ return TEntity::Directory ;
262279 case TNavigate::KindExtSubdomain:
263- return " database " ;
280+ return TEntity::Database ;
264281 case TNavigate::KindTable:
265- return " table " ;
282+ return TEntity::Table ;
266283 case TNavigate::KindOlapStore:
267- return " columnStore " ;
284+ return TEntity::ColumnStore ;
268285 case TNavigate::KindColumnTable:
269- return " columnTable " ;
286+ return TEntity::ColumnTable ;
270287 case TNavigate::KindRtmr:
271- return " rtmrVolume " ;
288+ return TEntity::RtmrVolume ;
272289 case TNavigate::KindKesus:
273- return " kesus " ;
290+ return TEntity::Kesus ;
274291 case TNavigate::KindSolomon:
275- return " solomonVolume " ;
292+ return TEntity::SolomonVolume ;
276293 case TNavigate::KindTopic:
277- return " persQueueGroup " ;
294+ return TEntity::PersQueueGroup ;
278295 case TNavigate::KindCdcStream:
279- return " cdcStream " ;
296+ return TEntity::CdcStream ;
280297 case TNavigate::KindSequence:
281- return " sequence " ;
298+ return TEntity::Sequence ;
282299 case TNavigate::KindReplication:
283- return " replication " ;
300+ return TEntity::Replication ;
284301 case TNavigate::KindBlobDepot:
285- return " blobDepot " ;
302+ return TEntity::BlobDepot ;
286303 case TNavigate::KindExternalTable:
287- return " externalTable " ;
304+ return TEntity::ExternalTable ;
288305 case TNavigate::KindExternalDataSource:
289- return " externalDataSource " ;
306+ return TEntity::ExternalDataSource ;
290307 case TNavigate::KindBlockStoreVolume:
291- return " blockStoreVolume " ;
308+ return TEntity::BlockStoreVolume ;
292309 case TNavigate::KindFileStore:
293- return " fileStore " ;
310+ return TEntity::FileStore ;
294311 case TNavigate::KindView:
295- return " view " ;
312+ return TEntity::View ;
296313 default :
297- return " directory" ;
314+ return TEntity::Directory;
315+ }
316+ }
317+
318+ void ParseProxyResult () {
319+ if (ProxyResult == nullptr ) {
320+ Result.add_error (" Failed to collect information from ProxyResult" );
321+ return ;
322+ }
323+ if (ProxyResult->Record .HasAutocompleteResponse ()) {
324+ Result = ProxyResult->Record .GetAutocompleteResponse ();
325+ } else {
326+ Result.add_error (" Proxying return empty response" );
298327 }
328+
299329 }
300330
301331 void ParseConsoleResult () {
302332 if (ConsoleResult == nullptr ) {
303- Result.add_error (" Failed to collect information" );
333+ Result.add_error (" Failed to collect information from ConsoleResult " );
304334 return ;
305335 }
306336
307337 Ydb::Cms::ListDatabasesResult listTenantsResult;
308338 ConsoleResult->Record .GetResponse ().operation ().result ().UnpackTo (&listTenantsResult);
309339 for (const TString& path : listTenantsResult.paths ()) {
310- Dictionary[path] = SchemaWordData (path, " database " );
340+ Dictionary[path] = TSchemaWordData (path, TEntity::Database );
311341 }
312- RequestDone ();
313342 }
314343
315344 void ParseCacheResult () {
316345 if (CacheResult == nullptr ) {
317- Result.add_error (" Failed to collect information" );
346+ Result.add_error (" Failed to collect information from CacheResult " );
318347 return ;
319348 }
320349 NSchemeCache::TSchemeCacheNavigate *navigate = CacheResult->Request .Get ();
321350 if (navigate->ErrorCount > 0 ) {
322- Result.add_error (" Inner errors while collected information" );
351+ for (auto & entry: CacheResult->Request .Get ()->ResultSet ) {
352+ if (entry.Status != TSchemeCacheNavigate::EStatus::Ok) {
353+ Result.add_error (TStringBuilder () << " Error receiving Navigate response: `" << CanonizePath (entry.Path ) << " ` has <" << ToString (entry.Status ) << " > status" );
354+ }
355+ }
323356 return ;
324357 }
325358 for (auto & entry: CacheResult->Request .Get ()->ResultSet ) {
326359 TString path = CanonizePath (entry.Path );
327360 if (entry.ListNodeEntry ) {
328361 for (const auto & child : entry.ListNodeEntry ->Children ) {
329- Dictionary[child.Name ] = SchemaWordData (child.Name , ConvertType (child.Kind ), path);
362+ Dictionary[child.Name ] = TSchemaWordData (child.Name , ConvertType (child.Kind ), path);
330363 }
331364 };
332365 for (const auto & [id, column] : entry.Columns ) {
333- Dictionary[column.Name ] = SchemaWordData (column.Name , path, " column " );
366+ Dictionary[column.Name ] = TSchemaWordData (column.Name , TEntity::Column, path );
334367 }
335368 for (const auto & index : entry.Indexes ) {
336- Dictionary[index.GetName ()] = SchemaWordData (index.GetName (), path, " index " );
369+ Dictionary[index.GetName ()] = TSchemaWordData (index.GetName (), TEntity::Index, path );
337370 }
338371 for (const auto & cdcStream : entry.CdcStreams ) {
339- Dictionary[cdcStream.GetName ()] = SchemaWordData (cdcStream.GetName (), path, " cdcstream " );
372+ Dictionary[cdcStream.GetName ()] = TSchemaWordData (cdcStream.GetName (), TEntity::CdcStream, path );
340373 }
341374 }
342375 }
@@ -364,23 +397,27 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
364397 }
365398
366399 void ReplyAndPassAway () {
367- if (!Database ) {
368- ParseConsoleResult ();
369- } else {
400+ if (ProxyResult ) {
401+ ParseProxyResult ();
402+ } else if (Database) {
370403 ParseCacheResult ();
404+ } else {
405+ ParseConsoleResult ();
371406 }
372407
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 );
408+ if (!ProxyResult) {
409+ Result.set_success (Result.error_size () == 0 );
410+ if (Result.error_size () == 0 ) {
411+ auto fuzzy = FuzzySearcher<TSchemaWordData>(Dictionary);
412+ auto autocomplete = fuzzy.Search (SearchWord, Limit);
413+ Result.MutableResult ()->SetTotal (autocomplete.size ());
414+ for (TSchemaWordData& wordData: autocomplete) {
415+ auto entity = Result.MutableResult ()->AddEntities ();
416+ entity->SetName (wordData.Name );
417+ entity->SetType (wordData.Type );
418+ if (wordData.Table ) {
419+ entity->SetParent (wordData.Table );
420+ }
384421 }
385422 }
386423 }
@@ -390,10 +427,8 @@ class TJsonAutocomplete : public TViewerPipeClient<TJsonAutocomplete> {
390427 }
391428
392429 void Handle (TEvViewer::TEvViewerResponse::TPtr& ev) {
393- if (ev.Get ()->Get ()->Record .HasAutocompleteResponse ()) {
394- Result = ev.Get ()->Get ()->Record .GetAutocompleteResponse ();
395- }
396- SendAutocompleteResponse ();
430+ ProxyResult = ev.Release ()->Release ();
431+ RequestDone ();
397432 }
398433
399434 void HandleTimeout () {
0 commit comments