Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix autocomplete to work with directories, optimize a little #9074

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading