logic: Improved search result scoring
* Increase result count relative to query length * Only run best scoring on shortest 1000 results * Made NameHierarchy deserializing faster
This commit is contained in:
+2
-1
@@ -27,6 +27,7 @@ set(PROJECT_NAME_LOWER_CASE sourcetrail)
|
||||
# speed up recompiling on unix with ccache
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
MESSAGE(STATUS "ccache found")
|
||||
# Support Unix Makefiles and Ninja
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
@@ -759,7 +760,7 @@ if (WIN32)
|
||||
COMMAND cd $(ProjectDir)../../bin/test/\n$(OutDir)$(TargetName)$(TargetExt)
|
||||
COMMENT "Running unittest code"
|
||||
)
|
||||
|
||||
|
||||
set_target_properties(${TEST_PROJECT_NAME} PROPERTIES COMPILE_FLAGS "/bigobj")
|
||||
|
||||
elseif (UNIX)
|
||||
|
||||
@@ -309,12 +309,14 @@ std::string NodeType::getReadableTypeString() const
|
||||
|
||||
std::wstring NodeType::getUnderscoredTypeWString() const
|
||||
{
|
||||
return utility::decodeFromUtf8(getUnderscoredTypeString());
|
||||
std::string str = getUnderscoredTypeString();
|
||||
return std::wstring(str.begin(), str.end());
|
||||
}
|
||||
|
||||
std::wstring NodeType::getReadableTypeWString() const
|
||||
{
|
||||
return utility::decodeFromUtf8(getReadableTypeString());
|
||||
std::string str = getReadableTypeString();
|
||||
return std::wstring(str.begin(), str.end());
|
||||
}
|
||||
|
||||
int utility::nodeTypeToInt(NodeType::Type type)
|
||||
@@ -420,7 +422,8 @@ std::string utility::getReadableTypeString(NodeType::Type type)
|
||||
|
||||
std::wstring utility::getReadableTypeWString(NodeType::Type type)
|
||||
{
|
||||
return utility::decodeFromUtf8(getReadableTypeString(type));
|
||||
std::string str = getReadableTypeString(type);
|
||||
return std::wstring(str.begin(), str.end());
|
||||
}
|
||||
|
||||
NodeType::Type utility::getTypeForReadableTypeString(const std::wstring str)
|
||||
|
||||
@@ -3,33 +3,13 @@
|
||||
#include "utility/logging/logging.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
std::wstring NameElement::Signature::serialize(Signature signature)
|
||||
{
|
||||
return signature.m_prefix + L"\tp" + signature.m_postfix;
|
||||
}
|
||||
|
||||
NameElement::Signature NameElement::Signature::deserialize(const std::wstring& serialized)
|
||||
{
|
||||
if (serialized == L"\tp")
|
||||
{
|
||||
return Signature();
|
||||
}
|
||||
|
||||
std::vector<std::wstring> serializedElements = utility::splitToVector(serialized, L"\tp");
|
||||
if (serializedElements.size() != 2)
|
||||
{
|
||||
LOG_ERROR(L"unable to deserialize name signature: " + serialized); // todo: obfuscate serialized!
|
||||
}
|
||||
return Signature(serializedElements[0], serializedElements[1]);
|
||||
}
|
||||
|
||||
NameElement::Signature::Signature()
|
||||
: m_prefix(L"")
|
||||
, m_postfix(L"")
|
||||
{
|
||||
}
|
||||
|
||||
NameElement::Signature::Signature(std::wstring prefix, std::wstring postfix)
|
||||
NameElement::Signature::Signature(const std::wstring& prefix, const std::wstring& postfix)
|
||||
: m_prefix(prefix)
|
||||
, m_postfix(postfix)
|
||||
{
|
||||
@@ -91,7 +71,7 @@ NameElement::~NameElement()
|
||||
{
|
||||
}
|
||||
|
||||
std::wstring NameElement::getName() const
|
||||
const std::wstring& NameElement::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
@@ -111,7 +91,7 @@ bool NameElement::hasSignature() const
|
||||
return m_signature.isValid();
|
||||
}
|
||||
|
||||
NameElement::Signature NameElement::getSignature()
|
||||
const NameElement::Signature& NameElement::getSignature()
|
||||
{
|
||||
return m_signature;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,9 @@ public:
|
||||
class Signature
|
||||
{
|
||||
public:
|
||||
static std::wstring serialize(Signature signature);
|
||||
static Signature deserialize(const std::wstring& serialized);
|
||||
|
||||
Signature();
|
||||
Signature(std::wstring prefix, std::wstring postfix);
|
||||
Signature(const std::wstring& prefix, const std::wstring& postfix);
|
||||
|
||||
std::wstring qualifyName(const std::wstring& name) const;
|
||||
bool isValid() const;
|
||||
|
||||
@@ -26,23 +24,23 @@ public:
|
||||
std::wstring getParameterString() const;
|
||||
|
||||
private:
|
||||
std::wstring m_prefix;
|
||||
std::wstring m_postfix;
|
||||
const std::wstring m_prefix;
|
||||
const std::wstring m_postfix;
|
||||
};
|
||||
|
||||
NameElement(const std::wstring& name);
|
||||
NameElement(const std::wstring& name, const Signature& signature);
|
||||
~NameElement();
|
||||
|
||||
std::wstring getName() const;
|
||||
const std::wstring& getName() const;
|
||||
std::wstring getNameWithSignature() const;
|
||||
std::wstring getNameWithSignatureParameters() const;
|
||||
bool hasSignature() const;
|
||||
Signature getSignature();
|
||||
const Signature& getSignature();
|
||||
|
||||
private:
|
||||
std::wstring m_name;
|
||||
Signature m_signature;
|
||||
const std::wstring m_name;
|
||||
const Signature m_signature;
|
||||
};
|
||||
|
||||
#endif // NAME_ELEMENT_H
|
||||
|
||||
@@ -1,45 +1,87 @@
|
||||
#include "data/name/NameHierarchy.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "utility/logging/logging.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const std::wstring META_DELIMITER = L"\tm";
|
||||
const std::wstring NAME_DELIMITER = L"\tn";
|
||||
const std::wstring PART_DELIMITER = L"\ts";
|
||||
const std::wstring SIGNATURE_DELIMITER = L"\tp";
|
||||
}
|
||||
|
||||
std::wstring NameHierarchy::serialize(const NameHierarchy& nameHierarchy)
|
||||
{
|
||||
std::wstring serializedName = nameDelimiterTypeToString(nameHierarchy.getDelimiter()) + L"\tm";
|
||||
std::wstringstream ss;
|
||||
ss << nameDelimiterTypeToString(nameHierarchy.getDelimiter());
|
||||
ss << META_DELIMITER;
|
||||
for (size_t i = 0; i < nameHierarchy.size(); i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
serializedName += L"\tn";
|
||||
ss << NAME_DELIMITER;
|
||||
}
|
||||
serializedName += nameHierarchy[i]->getName() + L"\ts";
|
||||
serializedName += NameElement::Signature::serialize(nameHierarchy[i]->getSignature());
|
||||
|
||||
ss << nameHierarchy[i]->getName() << PART_DELIMITER;
|
||||
ss << nameHierarchy[i]->getSignature().getPrefix();
|
||||
ss << SIGNATURE_DELIMITER;
|
||||
ss << nameHierarchy[i]->getSignature().getPostfix();
|
||||
}
|
||||
return serializedName;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
NameHierarchy NameHierarchy::deserialize(const std::wstring& serializedName)
|
||||
{
|
||||
std::vector<std::wstring> serializedNameAndMetaElements = utility::splitToVector(serializedName, L"\tm");
|
||||
if (serializedNameAndMetaElements.size() != 2)
|
||||
size_t mpos = serializedName.find(META_DELIMITER);
|
||||
if (mpos == std::wstring::npos)
|
||||
{
|
||||
LOG_ERROR(L"unable to deserialize name hierarchy: " + serializedName); // todo: obfuscate serializedName!
|
||||
return NameHierarchy(NAME_DELIMITER_UNKNOWN);
|
||||
}
|
||||
|
||||
const NameDelimiterType delimiter = stringToNameDelimiterType(serializedNameAndMetaElements[0]);
|
||||
NameHierarchy nameHierarchy(delimiter);
|
||||
NameHierarchy nameHierarchy(stringToNameDelimiterType(serializedName.substr(0, mpos)));
|
||||
|
||||
std::vector<std::wstring> serializedNameElements = utility::splitToVector(serializedNameAndMetaElements[1], L"\tn");
|
||||
for (size_t i = 0; i < serializedNameElements.size(); i++)
|
||||
size_t npos = mpos + META_DELIMITER.size();
|
||||
while (npos != std::wstring::npos && npos < serializedName.size())
|
||||
{
|
||||
std::vector<std::wstring> nameParts = utility::splitToVector(serializedNameElements[i], L"\ts");
|
||||
if (nameParts.size() != 2)
|
||||
// name
|
||||
size_t spos = serializedName.find(PART_DELIMITER, npos);
|
||||
if (spos == std::wstring::npos)
|
||||
{
|
||||
LOG_ERROR(L"unable to deserialize name hierarchy: " + serializedName); // todo: obfuscate serializedName!
|
||||
return NameHierarchy(delimiter);
|
||||
return NameHierarchy(NAME_DELIMITER_UNKNOWN);
|
||||
}
|
||||
nameHierarchy.push(std::make_shared<NameElement>(nameParts[0], NameElement::Signature::deserialize(nameParts[1])));
|
||||
|
||||
std::wstring name = serializedName.substr(npos, spos - npos);
|
||||
spos += PART_DELIMITER.size();
|
||||
|
||||
// signature
|
||||
size_t ppos = serializedName.find(SIGNATURE_DELIMITER, spos);
|
||||
if (ppos == std::wstring::npos)
|
||||
{
|
||||
LOG_ERROR(L"unable to deserialize name hierarchy: " + serializedName); // todo: obfuscate serializedName!
|
||||
return NameHierarchy(NAME_DELIMITER_UNKNOWN);
|
||||
}
|
||||
|
||||
std::wstring prefix = serializedName.substr(spos, ppos - spos);
|
||||
ppos += SIGNATURE_DELIMITER.size();
|
||||
|
||||
std::wstring postfix;
|
||||
npos = serializedName.find(NAME_DELIMITER, ppos);
|
||||
if (npos == std::wstring::npos)
|
||||
{
|
||||
postfix = serializedName.substr(ppos, std::wstring::npos);
|
||||
}
|
||||
else
|
||||
{
|
||||
postfix = serializedName.substr(ppos, npos - ppos);
|
||||
npos += NAME_DELIMITER.size();
|
||||
}
|
||||
|
||||
nameHierarchy.push(std::make_shared<NameElement>(name, NameElement::Signature(prefix, postfix)));
|
||||
}
|
||||
|
||||
return nameHierarchy;
|
||||
@@ -148,16 +190,16 @@ size_t NameHierarchy::size() const
|
||||
|
||||
std::wstring NameHierarchy::getQualifiedName() const
|
||||
{
|
||||
std::wstring name;
|
||||
std::wstringstream ss;
|
||||
for (size_t i = 0; i < m_elements.size(); i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
name += nameDelimiterTypeToString(m_delimiter);
|
||||
ss << nameDelimiterTypeToString(m_delimiter);
|
||||
}
|
||||
name += m_elements[i]->getName();
|
||||
ss << m_elements[i]->getName();
|
||||
}
|
||||
return name;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::wstring NameHierarchy::getQualifiedNameWithSignature() const
|
||||
|
||||
@@ -112,12 +112,29 @@ std::vector<SearchResult> SearchIndex::search(
|
||||
// create scored search results
|
||||
std::multiset<SearchResult> searchResults = createScoredResults(paths, acceptedNodeTypes, maxResultCount * 3);
|
||||
|
||||
// find maximum length for best scores
|
||||
std::multiset<size_t> resultLengths;
|
||||
for (const SearchResult& result : searchResults)
|
||||
{
|
||||
resultLengths.insert(result.text.size());
|
||||
}
|
||||
size_t maxResultLength = 0;
|
||||
if (resultLengths.size() > 1000)
|
||||
{
|
||||
auto it = resultLengths.begin();
|
||||
std::advance(it, 1000);
|
||||
maxResultLength = *it;
|
||||
}
|
||||
|
||||
// find best scores
|
||||
std::map<std::wstring, SearchResult> scoresCache;
|
||||
std::multiset<SearchResult> bestResults;
|
||||
for (const SearchResult& result : searchResults)
|
||||
{
|
||||
bestResults.insert(bestScoredResult(result, &scoresCache, maxBestScoredResultsLength));
|
||||
if (!maxResultLength || result.text.size() <= maxResultLength)
|
||||
{
|
||||
bestResults.insert(bestScoredResult(result, &scoresCache, maxBestScoredResultsLength));
|
||||
}
|
||||
}
|
||||
|
||||
// narrow down to max result count
|
||||
|
||||
@@ -649,15 +649,16 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionMatches(const std::
|
||||
TRACE();
|
||||
|
||||
// search in indices
|
||||
const size_t maxResultsCount = 100;
|
||||
const size_t maxResultsCount = std::pow(3, query.size() + 3);
|
||||
const size_t maxBestScoredResultsLength = 100;
|
||||
const size_t maxMatchesReturned = 1000;
|
||||
|
||||
// create SearchMatches
|
||||
std::vector<SearchMatch> matches;
|
||||
|
||||
if (!acceptedNodeTypes.getWithMatchingRemoved([](const NodeType& type) { return type.isFile(); }).isEmpty())
|
||||
{
|
||||
utility::append(matches, getAutocompletionSymbolMatches(query, acceptedNodeTypes, maxResultsCount, maxBestScoredResultsLength));
|
||||
matches = getAutocompletionSymbolMatches(query, acceptedNodeTypes, maxResultsCount, maxBestScoredResultsLength);
|
||||
}
|
||||
|
||||
if (acceptedNodeTypes.containsMatching([](const NodeType& type) { return type.isFile(); }))
|
||||
@@ -678,43 +679,55 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionMatches(const std::
|
||||
SearchIndex::rescoreText(match.name, match.text, match.indices, match.score, maxBestScoredResultsLength);
|
||||
|
||||
match.score = newResult.score;
|
||||
match.indices = newResult.indices;
|
||||
match.indices = std::move(newResult.indices);
|
||||
}
|
||||
|
||||
matchesMap.emplace(match.name, match);
|
||||
}
|
||||
|
||||
// Score child symbol matches with same score as parent lower
|
||||
SearchMatch lastMatch;
|
||||
const SearchMatch* lastMatch = nullptr;
|
||||
std::set<SearchMatch> matchesSet;
|
||||
for (const auto& p : matchesMap)
|
||||
for (auto& p : matchesMap)
|
||||
{
|
||||
SearchMatch match = p.second;
|
||||
if (!lastMatch.name.size() || !utility::isPrefix(lastMatch.name, match.name))
|
||||
SearchMatch& match = p.second;
|
||||
if (lastMatch == nullptr || !utility::isPrefix(lastMatch->name, match.name))
|
||||
{
|
||||
lastMatch = match;
|
||||
lastMatch = &match;
|
||||
}
|
||||
else if (lastMatch.score == match.score)
|
||||
else if (lastMatch->score == match.score)
|
||||
{
|
||||
if (utility::isPrefix(nameDelimiterTypeToString(match.tokenName.getDelimiter()), match.name.substr(lastMatch.name.size())))
|
||||
size_t lastSize = lastMatch->name.size();
|
||||
if (match.name.find(nameDelimiterTypeToString(match.tokenName.getDelimiter()), lastSize) == lastSize)
|
||||
{
|
||||
match.score -= 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastMatch = match;
|
||||
lastMatch = &match;
|
||||
}
|
||||
}
|
||||
|
||||
matchesSet.insert(match);
|
||||
}
|
||||
|
||||
// for (auto a : matchesSet)
|
||||
if (matchesSet.size() > maxMatchesReturned)
|
||||
{
|
||||
auto it = matchesSet.begin();
|
||||
std::advance(it, maxMatchesReturned);
|
||||
matches = std::vector<SearchMatch>(matchesSet.begin(), it);
|
||||
}
|
||||
else
|
||||
{
|
||||
matches = utility::toVector(matchesSet);
|
||||
}
|
||||
|
||||
// for (auto a : matches)
|
||||
// {
|
||||
// std::wcout << a.score << " " << a.name << std::endl;
|
||||
// }
|
||||
|
||||
return utility::toVector(matchesSet);
|
||||
return matches;
|
||||
}
|
||||
|
||||
std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
@@ -726,7 +739,6 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
|
||||
// fetch StorageNodes for node ids
|
||||
std::map<Id, StorageNode> storageNodeMap;
|
||||
std::map<Id, StorageSymbol> storageSymbolMap;
|
||||
{
|
||||
std::vector<Id> elementIds;
|
||||
|
||||
@@ -739,11 +751,6 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
{
|
||||
storageNodeMap[node.id] = node;
|
||||
}
|
||||
|
||||
for (const StorageSymbol& symbol : m_sqliteIndexStorage.getAllByIds<StorageSymbol>(elementIds))
|
||||
{
|
||||
storageSymbolMap[symbol.id] = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
// create SearchMatches
|
||||
@@ -778,8 +785,11 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
if (name.getQualifiedName() == match.name)
|
||||
{
|
||||
const size_t idx = m_hierarchyCache.getIndexOfLastVisibleParentNode(firstNode->id);
|
||||
match.text = name.getRange(idx, name.size()).getQualifiedName();
|
||||
match.subtext = name.getRange(0, idx).getQualifiedName();
|
||||
if (idx != 0)
|
||||
{
|
||||
match.text = name.getRange(idx, name.size()).getQualifiedName();
|
||||
match.subtext = name.getRange(0, idx).getQualifiedName();
|
||||
}
|
||||
}
|
||||
|
||||
match.tokenName = name;
|
||||
@@ -789,7 +799,7 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
match.typeName = match.nodeType.getReadableTypeWString();
|
||||
match.searchType = SearchMatch::SEARCH_TOKEN;
|
||||
|
||||
if (storageSymbolMap.find(firstNode->id) == storageSymbolMap.end())
|
||||
if (m_symbolDefinitionKinds.find(firstNode->id) == m_symbolDefinitionKinds.end())
|
||||
{
|
||||
match.typeName = L"non-indexed " + match.typeName;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user