Skip to content

Commit

Permalink
fix autocomplete to work with directories, optimize a little (ydb-pla…
Browse files Browse the repository at this point in the history
  • Loading branch information
adameat authored Sep 11, 2024
1 parent a3d20f3 commit f7f9aba
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 245 deletions.
2 changes: 1 addition & 1 deletion ydb/core/viewer/json_handlers_viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ void InitViewerRenderJsonHandler(TJsonHandlers& handlers) {
}

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

void InitViewerCheckAccessJsonHandler(TJsonHandlers& jsonHandlers) {
Expand Down
56 changes: 32 additions & 24 deletions ydb/core/viewer/json_pipe_req.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,37 +122,45 @@ TString TViewerPipeClient::GetPath(TEvTxProxySchemeCache::TEvNavigateKeySetResul
}

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

TString TViewerPipeClient::GetError(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev) {
if (ev->Request->ResultSet.size() == 0) {
return "empty response";
}
switch (ev->Request->ResultSet.begin()->Status) {
case NSchemeCache::TSchemeCacheNavigate::EStatus::Ok:
return "Ok";
case NSchemeCache::TSchemeCacheNavigate::EStatus::Unknown:
return "Unknown";
case NSchemeCache::TSchemeCacheNavigate::EStatus::RootUnknown:
return "RootUnknown";
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathErrorUnknown:
return "PathErrorUnknown";
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotTable:
return "PathNotTable";
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotPath:
return "PathNotPath";
case NSchemeCache::TSchemeCacheNavigate::EStatus::TableCreationNotComplete:
return "TableCreationNotComplete";
case NSchemeCache::TSchemeCacheNavigate::EStatus::LookupError:
return "LookupError";
case NSchemeCache::TSchemeCacheNavigate::EStatus::RedirectLookupError:
return "RedirectLookupError";
case NSchemeCache::TSchemeCacheNavigate::EStatus::AccessDenied:
return "AccessDenied";
default:
return ::ToString(static_cast<int>(ev->Request->ResultSet.begin()->Status));
for (const auto& entry : ev->Request->ResultSet) {
if (entry.Status != NSchemeCache::TSchemeCacheNavigate::EStatus::Ok) {
switch (entry.Status) {
case NSchemeCache::TSchemeCacheNavigate::EStatus::Ok:
return "Ok";
case NSchemeCache::TSchemeCacheNavigate::EStatus::Unknown:
return "Unknown";
case NSchemeCache::TSchemeCacheNavigate::EStatus::RootUnknown:
return "RootUnknown";
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathErrorUnknown:
return "PathErrorUnknown";
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotTable:
return "PathNotTable";
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotPath:
return "PathNotPath";
case NSchemeCache::TSchemeCacheNavigate::EStatus::TableCreationNotComplete:
return "TableCreationNotComplete";
case NSchemeCache::TSchemeCacheNavigate::EStatus::LookupError:
return "LookupError";
case NSchemeCache::TSchemeCacheNavigate::EStatus::RedirectLookupError:
return "RedirectLookupError";
case NSchemeCache::TSchemeCacheNavigate::EStatus::AccessDenied:
return "AccessDenied";
default:
return ::ToString(static_cast<int>(ev->Request->ResultSet.begin()->Status));
}
}
}
return "no error";
}

bool TViewerPipeClient::IsSuccess(const std::unique_ptr<TEvStateStorage::TEvBoardInfo>& ev) {
Expand Down
3 changes: 2 additions & 1 deletion ydb/core/viewer/protos/viewer.proto
Original file line number Diff line number Diff line change
Expand Up @@ -729,13 +729,14 @@ message TQueryAutocomplete {
EAutocompleteType Type = 2;
string Parent = 3;
}
uint32 Total = 1;
optional uint32 Total = 1;
repeated TEntity Entities = 2;
}

bool Success = 1;
TResult Result = 2;
repeated string Error = 3;
uint32 Version = 4;
}

message TPDiskInfoWhiteboard {
Expand Down
94 changes: 20 additions & 74 deletions ydb/core/viewer/query_autocomplete_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,89 +33,35 @@ inline ui32 LevenshteinDistance(TString word1, TString word2) {
return dist[size1][size2];
}

template<typename Type>
class FuzzySearcher {
struct WordHit {
bool Contains;
ui32 LengthDifference;
ui32 LevenshteinDistance;
Type Data;

WordHit(bool contains, ui32 lengthDifference, ui32 levenshteinDistance, Type data)
: Contains(contains)
, LengthDifference(lengthDifference)
, LevenshteinDistance(levenshteinDistance)
, Data(data)
{}

bool operator<(const WordHit& other) const {
if (this->Contains && !other.Contains) {
return true;
}
if (this->Contains && other.Contains) {
return this->LengthDifference < other.LengthDifference;
}
return this->LevenshteinDistance < other.LevenshteinDistance;
}

bool operator>(const WordHit& other) const {
if (!this->Contains && other.Contains) {
return true;
}
if (this->Contains && other.Contains) {
return this->LengthDifference > other.LengthDifference;
}
return this->LevenshteinDistance > other.LevenshteinDistance;
}
};

static WordHit CalculateWordHit(TString searchWord, TString testWord, Type testData) {
searchWord = to_lower(searchWord);
testWord = to_lower(testWord);
if (testWord.Contains(searchWord)) {
return {1, static_cast<ui32>(testWord.length() - searchWord.length()), 0, testData};
static size_t CalculateWordHit(const TString& searchWord, const TString& testWord) {
size_t findPos = testWord.find(searchWord);
if (findPos != TString::npos) {
return testWord.size() - searchWord.size() + findPos;
} else {
ui32 levenshteinDistance = LevenshteinDistance(searchWord, testWord);
return {0, 0, levenshteinDistance, testData};
return 1000 * LevenshteinDistance(searchWord, testWord);
}
}

public:
THashMap<TString, Type> Dictionary;

FuzzySearcher(const THashMap<TString, Type>& dictionary)
: Dictionary(dictionary) {}

FuzzySearcher(const TVector<TString>& words) {
for (const auto& word : words) {
Dictionary[word] = word;
template<typename Type>
static std::vector<const Type*> Search(const std::vector<Type>& dictionary, const TString& searchWord, ui32 limit = 10) {
TString search = to_lower(searchWord);
std::vector<std::pair<size_t, size_t>> hits; // {distance, index}
hits.reserve(dictionary.size());
for (size_t index = 0; index < dictionary.size(); ++index) {
hits.emplace_back(CalculateWordHit(search, to_lower(TString(dictionary[index]))), index);
}
}

TVector<Type> Search(const TString& searchWord, ui32 limit = 10) {
auto cmp = [](const WordHit& left, const WordHit& right) {
return left < right;
};
std::priority_queue<WordHit, TVector<WordHit>, decltype(cmp)> queue(cmp);

for (const auto& [word, data]: Dictionary) {
auto wordHit = CalculateWordHit(searchWord, word, data);
if (queue.size() < limit) {
queue.emplace(wordHit);
} else if (queue.size() > 0 && wordHit < queue.top()) {
queue.pop();
queue.emplace(wordHit);
}
std::sort(hits.begin(), hits.end());
if (hits.size() > limit) {
hits.resize(limit);
}

TVector<Type> results;
while (!queue.empty()) {
results.emplace_back(queue.top().Data);
queue.pop();
std::vector<const Type*> result;
result.reserve(hits.size());
for (const auto& hit : hits) {
result.emplace_back(&dictionary[hit.second]);
}

std::reverse(results.begin(), results.end());
return results;
return result;
}
};

Expand Down
Loading

0 comments on commit f7f9aba

Please sign in to comment.