Skip to content

Commit f7f9aba

Browse files
authored
fix autocomplete to work with directories, optimize a little (#9074)
1 parent a3d20f3 commit f7f9aba

File tree

6 files changed

+172
-245
lines changed

6 files changed

+172
-245
lines changed

ydb/core/viewer/json_handlers_viewer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ void InitViewerRenderJsonHandler(TJsonHandlers& handlers) {
259259
}
260260

261261
void InitViewerAutocompleteJsonHandler(TJsonHandlers& jsonHandlers) {
262-
jsonHandlers.AddHandler("/viewer/autocomplete", new TJsonHandler<TJsonAutocomplete>(TJsonAutocomplete::GetSwagger()));
262+
jsonHandlers.AddHandler("/viewer/autocomplete", new TJsonHandler<TJsonAutocomplete>(TJsonAutocomplete::GetSwagger()), 2);
263263
}
264264

265265
void InitViewerCheckAccessJsonHandler(TJsonHandlers& jsonHandlers) {

ydb/core/viewer/json_pipe_req.cpp

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -122,37 +122,45 @@ TString TViewerPipeClient::GetPath(TEvTxProxySchemeCache::TEvNavigateKeySetResul
122122
}
123123

124124
bool TViewerPipeClient::IsSuccess(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev) {
125-
return (ev->Request->ResultSet.size() == 1) && (ev->Request->ResultSet.begin()->Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok);
125+
return (ev->Request->ResultSet.size() > 0) && (std::find_if(ev->Request->ResultSet.begin(), ev->Request->ResultSet.end(),
126+
[](const auto& entry) {
127+
return entry.Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok;
128+
}) != ev->Request->ResultSet.end());
126129
}
127130

128131
TString TViewerPipeClient::GetError(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev) {
129132
if (ev->Request->ResultSet.size() == 0) {
130133
return "empty response";
131134
}
132-
switch (ev->Request->ResultSet.begin()->Status) {
133-
case NSchemeCache::TSchemeCacheNavigate::EStatus::Ok:
134-
return "Ok";
135-
case NSchemeCache::TSchemeCacheNavigate::EStatus::Unknown:
136-
return "Unknown";
137-
case NSchemeCache::TSchemeCacheNavigate::EStatus::RootUnknown:
138-
return "RootUnknown";
139-
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathErrorUnknown:
140-
return "PathErrorUnknown";
141-
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotTable:
142-
return "PathNotTable";
143-
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotPath:
144-
return "PathNotPath";
145-
case NSchemeCache::TSchemeCacheNavigate::EStatus::TableCreationNotComplete:
146-
return "TableCreationNotComplete";
147-
case NSchemeCache::TSchemeCacheNavigate::EStatus::LookupError:
148-
return "LookupError";
149-
case NSchemeCache::TSchemeCacheNavigate::EStatus::RedirectLookupError:
150-
return "RedirectLookupError";
151-
case NSchemeCache::TSchemeCacheNavigate::EStatus::AccessDenied:
152-
return "AccessDenied";
153-
default:
154-
return ::ToString(static_cast<int>(ev->Request->ResultSet.begin()->Status));
135+
for (const auto& entry : ev->Request->ResultSet) {
136+
if (entry.Status != NSchemeCache::TSchemeCacheNavigate::EStatus::Ok) {
137+
switch (entry.Status) {
138+
case NSchemeCache::TSchemeCacheNavigate::EStatus::Ok:
139+
return "Ok";
140+
case NSchemeCache::TSchemeCacheNavigate::EStatus::Unknown:
141+
return "Unknown";
142+
case NSchemeCache::TSchemeCacheNavigate::EStatus::RootUnknown:
143+
return "RootUnknown";
144+
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathErrorUnknown:
145+
return "PathErrorUnknown";
146+
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotTable:
147+
return "PathNotTable";
148+
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotPath:
149+
return "PathNotPath";
150+
case NSchemeCache::TSchemeCacheNavigate::EStatus::TableCreationNotComplete:
151+
return "TableCreationNotComplete";
152+
case NSchemeCache::TSchemeCacheNavigate::EStatus::LookupError:
153+
return "LookupError";
154+
case NSchemeCache::TSchemeCacheNavigate::EStatus::RedirectLookupError:
155+
return "RedirectLookupError";
156+
case NSchemeCache::TSchemeCacheNavigate::EStatus::AccessDenied:
157+
return "AccessDenied";
158+
default:
159+
return ::ToString(static_cast<int>(ev->Request->ResultSet.begin()->Status));
160+
}
161+
}
155162
}
163+
return "no error";
156164
}
157165

158166
bool TViewerPipeClient::IsSuccess(const std::unique_ptr<TEvStateStorage::TEvBoardInfo>& ev) {

ydb/core/viewer/protos/viewer.proto

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,13 +729,14 @@ message TQueryAutocomplete {
729729
EAutocompleteType Type = 2;
730730
string Parent = 3;
731731
}
732-
uint32 Total = 1;
732+
optional uint32 Total = 1;
733733
repeated TEntity Entities = 2;
734734
}
735735

736736
bool Success = 1;
737737
TResult Result = 2;
738738
repeated string Error = 3;
739+
uint32 Version = 4;
739740
}
740741

741742
message TPDiskInfoWhiteboard {

ydb/core/viewer/query_autocomplete_helper.h

Lines changed: 20 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -33,89 +33,35 @@ inline ui32 LevenshteinDistance(TString word1, TString word2) {
3333
return dist[size1][size2];
3434
}
3535

36-
template<typename Type>
3736
class FuzzySearcher {
38-
struct WordHit {
39-
bool Contains;
40-
ui32 LengthDifference;
41-
ui32 LevenshteinDistance;
42-
Type Data;
43-
44-
WordHit(bool contains, ui32 lengthDifference, ui32 levenshteinDistance, Type data)
45-
: Contains(contains)
46-
, LengthDifference(lengthDifference)
47-
, LevenshteinDistance(levenshteinDistance)
48-
, Data(data)
49-
{}
50-
51-
bool operator<(const WordHit& other) const {
52-
if (this->Contains && !other.Contains) {
53-
return true;
54-
}
55-
if (this->Contains && other.Contains) {
56-
return this->LengthDifference < other.LengthDifference;
57-
}
58-
return this->LevenshteinDistance < other.LevenshteinDistance;
59-
}
60-
61-
bool operator>(const WordHit& other) const {
62-
if (!this->Contains && other.Contains) {
63-
return true;
64-
}
65-
if (this->Contains && other.Contains) {
66-
return this->LengthDifference > other.LengthDifference;
67-
}
68-
return this->LevenshteinDistance > other.LevenshteinDistance;
69-
}
70-
};
71-
72-
static WordHit CalculateWordHit(TString searchWord, TString testWord, Type testData) {
73-
searchWord = to_lower(searchWord);
74-
testWord = to_lower(testWord);
75-
if (testWord.Contains(searchWord)) {
76-
return {1, static_cast<ui32>(testWord.length() - searchWord.length()), 0, testData};
37+
static size_t CalculateWordHit(const TString& searchWord, const TString& testWord) {
38+
size_t findPos = testWord.find(searchWord);
39+
if (findPos != TString::npos) {
40+
return testWord.size() - searchWord.size() + findPos;
7741
} else {
78-
ui32 levenshteinDistance = LevenshteinDistance(searchWord, testWord);
79-
return {0, 0, levenshteinDistance, testData};
42+
return 1000 * LevenshteinDistance(searchWord, testWord);
8043
}
8144
}
8245

8346
public:
84-
THashMap<TString, Type> Dictionary;
85-
86-
FuzzySearcher(const THashMap<TString, Type>& dictionary)
87-
: Dictionary(dictionary) {}
88-
89-
FuzzySearcher(const TVector<TString>& words) {
90-
for (const auto& word : words) {
91-
Dictionary[word] = word;
47+
template<typename Type>
48+
static std::vector<const Type*> Search(const std::vector<Type>& dictionary, const TString& searchWord, ui32 limit = 10) {
49+
TString search = to_lower(searchWord);
50+
std::vector<std::pair<size_t, size_t>> hits; // {distance, index}
51+
hits.reserve(dictionary.size());
52+
for (size_t index = 0; index < dictionary.size(); ++index) {
53+
hits.emplace_back(CalculateWordHit(search, to_lower(TString(dictionary[index]))), index);
9254
}
93-
}
94-
95-
TVector<Type> Search(const TString& searchWord, ui32 limit = 10) {
96-
auto cmp = [](const WordHit& left, const WordHit& right) {
97-
return left < right;
98-
};
99-
std::priority_queue<WordHit, TVector<WordHit>, decltype(cmp)> queue(cmp);
100-
101-
for (const auto& [word, data]: Dictionary) {
102-
auto wordHit = CalculateWordHit(searchWord, word, data);
103-
if (queue.size() < limit) {
104-
queue.emplace(wordHit);
105-
} else if (queue.size() > 0 && wordHit < queue.top()) {
106-
queue.pop();
107-
queue.emplace(wordHit);
108-
}
55+
std::sort(hits.begin(), hits.end());
56+
if (hits.size() > limit) {
57+
hits.resize(limit);
10958
}
110-
111-
TVector<Type> results;
112-
while (!queue.empty()) {
113-
results.emplace_back(queue.top().Data);
114-
queue.pop();
59+
std::vector<const Type*> result;
60+
result.reserve(hits.size());
61+
for (const auto& hit : hits) {
62+
result.emplace_back(&dictionary[hit.second]);
11563
}
116-
117-
std::reverse(results.begin(), results.end());
118-
return results;
64+
return result;
11965
}
12066
};
12167

0 commit comments

Comments
 (0)