logic: Add node type filtering to SearchIndex to avoid low or no results for filters
This commit is contained in:
@@ -16,9 +16,9 @@ SearchIndex::~SearchIndex()
|
||||
{
|
||||
}
|
||||
|
||||
void SearchIndex::addNode(Id id, const std::string& name)
|
||||
void SearchIndex::addNode(Id id, const std::string& name, Node::NodeTypeMask type)
|
||||
{
|
||||
Node* currentNode = m_root;
|
||||
SearchNode* currentNode = m_root;
|
||||
|
||||
std::string remaining = name;
|
||||
while (remaining.size() > 0)
|
||||
@@ -26,7 +26,7 @@ void SearchIndex::addNode(Id id, const std::string& name)
|
||||
auto it = currentNode->edges.find(remaining[0]);
|
||||
if (it != currentNode->edges.end())
|
||||
{
|
||||
Edge* currentEdge = it->second;
|
||||
SearchEdge* currentEdge = it->second;
|
||||
const std::string& edgeString = currentEdge->s;
|
||||
|
||||
size_t matchCount = 1;
|
||||
@@ -42,9 +42,9 @@ void SearchIndex::addNode(Id id, const std::string& name)
|
||||
if (matchCount < edgeString.size())
|
||||
{
|
||||
// split current edge
|
||||
std::shared_ptr<Node> n = std::make_shared<Node>();
|
||||
std::shared_ptr<SearchNode> n = std::make_shared<SearchNode>();
|
||||
m_nodes.push_back(n);
|
||||
std::shared_ptr<Edge> e = std::make_shared<Edge>();
|
||||
std::shared_ptr<SearchEdge> e = std::make_shared<SearchEdge>();
|
||||
m_edges.push_back(e);
|
||||
|
||||
e->s = edgeString.substr(matchCount);
|
||||
@@ -61,9 +61,9 @@ void SearchIndex::addNode(Id id, const std::string& name)
|
||||
}
|
||||
else
|
||||
{
|
||||
std::shared_ptr<Node> n = std::make_shared<Node>();
|
||||
std::shared_ptr<SearchNode> n = std::make_shared<SearchNode>();
|
||||
m_nodes.push_back(n);
|
||||
std::shared_ptr<Edge> e = std::make_shared<Edge>();
|
||||
std::shared_ptr<SearchEdge> e = std::make_shared<SearchEdge>();
|
||||
m_edges.push_back(e);
|
||||
|
||||
e->s = remaining;
|
||||
@@ -77,6 +77,7 @@ void SearchIndex::addNode(Id id, const std::string& name)
|
||||
}
|
||||
|
||||
currentNode->elementIds.insert(id);
|
||||
currentNode->mask |= type;
|
||||
}
|
||||
|
||||
void SearchIndex::finishSetup()
|
||||
@@ -92,24 +93,24 @@ void SearchIndex::clear()
|
||||
m_nodes.clear();
|
||||
m_edges.clear();
|
||||
|
||||
std::shared_ptr<Node> n = std::make_shared<Node>();
|
||||
std::shared_ptr<SearchNode> n = std::make_shared<SearchNode>();
|
||||
m_nodes.push_back(n);
|
||||
|
||||
m_root = n.get();
|
||||
}
|
||||
|
||||
std::vector<SearchResult> SearchIndex::search(
|
||||
const std::string& query, size_t maxResultCount, size_t maxBestScoredLength) const
|
||||
const std::string& query, Node::NodeTypeMask filter, size_t maxResultCount, size_t maxBestScoredLength) const
|
||||
{
|
||||
// find paths containing query
|
||||
Path startPath;
|
||||
SearchPath startPath;
|
||||
startPath.node = m_root;
|
||||
|
||||
std::vector<Path> paths;
|
||||
searchRecursive(startPath, utility::toLowerCase(query), &paths);
|
||||
std::vector<SearchPath> paths;
|
||||
searchRecursive(startPath, utility::toLowerCase(query), filter, &paths);
|
||||
|
||||
// create scored search results
|
||||
std::multiset<SearchResult> searchResults = createScoredResults(paths, maxResultCount * 3);
|
||||
std::multiset<SearchResult> searchResults = createScoredResults(paths, filter, maxResultCount * 3);
|
||||
|
||||
// find best scores
|
||||
std::map<std::string, SearchResult> scoresCache;
|
||||
@@ -130,12 +131,12 @@ std::vector<SearchResult> SearchIndex::search(
|
||||
return std::vector<SearchResult>(bestResults.begin(), it);
|
||||
}
|
||||
|
||||
void SearchIndex::populateEdgeGate(Edge* e)
|
||||
void SearchIndex::populateEdgeGate(SearchEdge* e)
|
||||
{
|
||||
Node* target = e->target;
|
||||
SearchNode* target = e->target;
|
||||
for (auto p : target->edges)
|
||||
{
|
||||
Edge* targetEdge = p.second;
|
||||
SearchEdge* targetEdge = p.second;
|
||||
populateEdgeGate(targetEdge);
|
||||
utility::append(e->gate, targetEdge->gate);
|
||||
}
|
||||
@@ -146,9 +147,9 @@ void SearchIndex::populateEdgeGate(Edge* e)
|
||||
}
|
||||
|
||||
void SearchIndex::searchRecursive(
|
||||
const Path& path, const std::string& remainingQuery, std::vector<SearchIndex::Path>* results) const
|
||||
const SearchPath& path, const std::string& remainingQuery, Node::NodeTypeMask filter, std::vector<SearchIndex::SearchPath>* results) const
|
||||
{
|
||||
if (remainingQuery.size() == 0)
|
||||
if (remainingQuery.size() == 0 && (!filter || (path.node->mask & filter)))
|
||||
{
|
||||
results->push_back(path);
|
||||
return;
|
||||
@@ -156,7 +157,7 @@ void SearchIndex::searchRecursive(
|
||||
|
||||
for (auto p : path.node->edges)
|
||||
{
|
||||
const Edge* currentEdge = p.second;
|
||||
const SearchEdge* currentEdge = p.second;
|
||||
|
||||
// test if s passes the edge's gate.
|
||||
bool passesGate = true;
|
||||
@@ -185,45 +186,46 @@ void SearchIndex::searchRecursive(
|
||||
}
|
||||
}
|
||||
|
||||
Path currentPath;
|
||||
SearchPath currentPath;
|
||||
currentPath.node = currentEdge->target;
|
||||
currentPath.indices = indices;
|
||||
currentPath.text = path.text + edgeString;
|
||||
|
||||
searchRecursive(currentPath, remainingQuery.substr(j), results);
|
||||
searchRecursive(currentPath, remainingQuery.substr(j), filter, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::multiset<SearchResult> SearchIndex::createScoredResults(const std::vector<Path>& paths, size_t maxResultCount) const
|
||||
std::multiset<SearchResult> SearchIndex::createScoredResults(
|
||||
const std::vector<SearchPath>& paths, Node::NodeTypeMask filter, size_t maxResultCount) const
|
||||
{
|
||||
// score and order initial paths
|
||||
std::multiset<std::pair<int, Path>, bool(*)(const std::pair<int, Path>&, const std::pair<int, Path>&)> scoredPaths(
|
||||
[](const std::pair<int, Path>& a, const std::pair<int, Path>& b)
|
||||
std::multiset<std::pair<int, SearchPath>, bool(*)(const std::pair<int, SearchPath>&, const std::pair<int, SearchPath>&)> scoredPaths(
|
||||
[](const std::pair<int, SearchPath>& a, const std::pair<int, SearchPath>& b)
|
||||
{
|
||||
return a.first > b.first;
|
||||
}
|
||||
);
|
||||
|
||||
for (const Path& path : paths)
|
||||
for (const SearchPath& path : paths)
|
||||
{
|
||||
scoredPaths.insert(std::make_pair(scoreText(path.text, path.indices), path));
|
||||
}
|
||||
|
||||
// score paths and subpaths
|
||||
std::multiset<SearchResult> searchResults;
|
||||
for (const std::pair<int, Path>& p : scoredPaths)
|
||||
for (const std::pair<int, SearchPath>& p : scoredPaths)
|
||||
{
|
||||
std::vector<Path> currentPaths;
|
||||
std::vector<SearchPath> currentPaths;
|
||||
currentPaths.push_back(p.second);
|
||||
|
||||
while (currentPaths.size())
|
||||
{
|
||||
std::vector<Path> nextPaths;
|
||||
std::vector<SearchPath> nextPaths;
|
||||
|
||||
for (const Path& path : currentPaths)
|
||||
for (const SearchPath& path : currentPaths)
|
||||
{
|
||||
if (path.node->elementIds.size())
|
||||
if (path.node->elementIds.size() && (!filter || (path.node->mask & filter)))
|
||||
{
|
||||
SearchResult result;
|
||||
result.text = path.text;
|
||||
@@ -240,8 +242,8 @@ std::multiset<SearchResult> SearchIndex::createScoredResults(const std::vector<P
|
||||
|
||||
for (auto p : path.node->edges)
|
||||
{
|
||||
const Edge* edge = p.second;
|
||||
Path nextPath;
|
||||
const SearchEdge* edge = p.second;
|
||||
SearchPath nextPath;
|
||||
nextPath.indices = path.indices;
|
||||
nextPath.node = edge->target;
|
||||
nextPath.text = path.text + edge->s;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "utility/types.h"
|
||||
#include "data/graph/Node.h"
|
||||
|
||||
struct SearchResult
|
||||
{
|
||||
@@ -29,41 +30,43 @@ public:
|
||||
SearchIndex();
|
||||
virtual ~SearchIndex();
|
||||
|
||||
void addNode(Id id, const std::string& name);
|
||||
void addNode(Id id, const std::string& name, Node::NodeTypeMask type = 0);
|
||||
void finishSetup();
|
||||
void clear();
|
||||
|
||||
// maxResultCount == 0 means "no restriction".
|
||||
std::vector<SearchResult> search(const std::string& query, size_t maxResultCount, size_t maxBestScoredLength = 0) const;
|
||||
std::vector<SearchResult> search(const std::string& query, Node::NodeTypeMask filter, size_t maxResultCount, size_t maxBestScoredLength = 0) const;
|
||||
|
||||
private:
|
||||
struct Node;
|
||||
struct Edge;
|
||||
struct SearchEdge;
|
||||
|
||||
struct Node
|
||||
struct SearchNode
|
||||
{
|
||||
std::set<Id> elementIds;
|
||||
std::map<char, Edge*> edges;
|
||||
Node::NodeTypeMask mask = 0;
|
||||
std::map<char, SearchEdge*> edges;
|
||||
};
|
||||
|
||||
struct Edge
|
||||
struct SearchEdge
|
||||
{
|
||||
Node* target;
|
||||
SearchNode* target;
|
||||
std::string s;
|
||||
std::unordered_set<char> gate;
|
||||
};
|
||||
|
||||
struct Path
|
||||
struct SearchPath
|
||||
{
|
||||
std::string text;
|
||||
std::vector<size_t> indices;
|
||||
Node* node;
|
||||
SearchNode* node;
|
||||
};
|
||||
|
||||
void populateEdgeGate(Edge* e);
|
||||
void searchRecursive(const Path& path, const std::string& remainingQuery, std::vector<SearchIndex::Path>* results) const;
|
||||
void populateEdgeGate(SearchEdge* e);
|
||||
void searchRecursive(const SearchPath& path, const std::string& remainingQuery, Node::NodeTypeMask filter,
|
||||
std::vector<SearchIndex::SearchPath>* results) const;
|
||||
|
||||
std::multiset<SearchResult> createScoredResults(const std::vector<Path>& paths, size_t maxResultCount) const;
|
||||
std::multiset<SearchResult> createScoredResults(
|
||||
const std::vector<SearchPath>& paths, Node::NodeTypeMask filter, size_t maxResultCount) const;
|
||||
|
||||
static SearchResult bestScoredResult(
|
||||
SearchResult result, std::map<std::string, SearchResult>* scoresCache, size_t maxBestScoredLength);
|
||||
@@ -81,9 +84,9 @@ public:
|
||||
size_t maxBestScoredLength);
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Node>> m_nodes;
|
||||
std::vector<std::shared_ptr<Edge>> m_edges;
|
||||
Node* m_root;
|
||||
std::vector<std::shared_ptr<SearchNode>> m_nodes;
|
||||
std::vector<std::shared_ptr<SearchEdge>> m_edges;
|
||||
SearchNode* m_root;
|
||||
};
|
||||
|
||||
#endif // SEARCH_INDEX_H
|
||||
|
||||
@@ -627,7 +627,7 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
const std::string& query, Node::NodeTypeMask filter, size_t maxResultsCount) const
|
||||
{
|
||||
// search in indices
|
||||
std::vector<SearchResult> results = m_symbolIndex.search(query, maxResultsCount, maxResultsCount);
|
||||
std::vector<SearchResult> results = m_symbolIndex.search(query, filter, maxResultsCount, maxResultsCount);
|
||||
|
||||
// fetch StorageNodes for node ids
|
||||
std::map<Id, StorageNode> storageNodeMap;
|
||||
@@ -676,11 +676,6 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
}
|
||||
}
|
||||
|
||||
if (filter && !(filter & Node::intToType(firstNode->type)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
match.name = result.text;
|
||||
match.text = result.text;
|
||||
|
||||
@@ -714,7 +709,7 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
|
||||
std::vector<SearchMatch> PersistentStorage::getAutocompletionFileMatches(const std::string& query, size_t maxResultsCount) const
|
||||
{
|
||||
std::vector<SearchResult> results = m_fileIndex.search(query, maxResultsCount);
|
||||
std::vector<SearchResult> results = m_fileIndex.search(query, Node::NODE_FILE, maxResultsCount);
|
||||
|
||||
// create SearchMatches
|
||||
std::vector<SearchMatch> matches;
|
||||
@@ -749,7 +744,7 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionCommandMatches(
|
||||
const std::string& query, Node::NodeTypeMask filter) const
|
||||
{
|
||||
// search in indices
|
||||
std::vector<SearchResult> results = m_commandIndex.search(query, 0);
|
||||
std::vector<SearchResult> results = m_commandIndex.search(query, 0, 0);
|
||||
|
||||
// create SearchMatches
|
||||
std::vector<SearchMatch> matches;
|
||||
@@ -2467,7 +2462,8 @@ void PersistentStorage::buildSearchIndex()
|
||||
|
||||
for (StorageNode& node : m_sqliteIndexStorage.getAll<StorageNode>())
|
||||
{
|
||||
if (Node::intToType(node.type) == Node::NODE_FILE)
|
||||
Node::NodeType type = Node::intToType(node.type);
|
||||
if (type == Node::NODE_FILE)
|
||||
{
|
||||
auto it = m_fileNodePaths.find(node.id);
|
||||
if (it != m_fileNodePaths.end())
|
||||
@@ -2479,7 +2475,7 @@ void PersistentStorage::buildSearchIndex()
|
||||
filePath = filePath.relativeTo(dbPath);
|
||||
}
|
||||
|
||||
m_fileIndex.addNode(node.id, filePath.str());
|
||||
m_fileIndex.addNode(node.id, filePath.str(), type);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2499,7 +2495,7 @@ void PersistentStorage::buildSearchIndex()
|
||||
name = utility::replaceBetween(name, '<', '>', "..");
|
||||
}
|
||||
|
||||
m_symbolIndex.addNode(node.id, name);
|
||||
m_symbolIndex.addNode(node.id, name, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public:
|
||||
SearchIndex index;
|
||||
index.addNode(1, NameHierarchy::deserialize("::\tmfoo\tsvoid\tp() const").getQualifiedName());
|
||||
index.finishSetup();
|
||||
std::vector<SearchResult> results = index.search("oo", 0);
|
||||
std::vector<SearchResult> results = index.search("oo", 0, 0);
|
||||
|
||||
TS_ASSERT_EQUALS(1, results.size());
|
||||
TS_ASSERT_EQUALS(1, results[0].elementIds.size());
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
SearchIndex index;
|
||||
index.addNode(1, NameHierarchy::deserialize("::\tmfoo\tsvoid\tp() const").getQualifiedName());
|
||||
index.finishSetup();
|
||||
std::vector<SearchResult> results = index.search("oo", 0);
|
||||
std::vector<SearchResult> results = index.search("oo", 0, 0);
|
||||
|
||||
TS_ASSERT_EQUALS(1, results.size());
|
||||
TS_ASSERT_EQUALS(2, results[0].indices.size());
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
index.addNode(1, NameHierarchy::deserialize("::\tmfor\tsvoid\tp() const").getQualifiedName());
|
||||
index.addNode(2, NameHierarchy::deserialize("::\tmfos\tsvoid\tp() const").getQualifiedName());
|
||||
index.finishSetup();
|
||||
std::vector<SearchResult> results = index.search("fo", 0);
|
||||
std::vector<SearchResult> results = index.search("fo", 0, 0);
|
||||
|
||||
TS_ASSERT_EQUALS(2, results.size());
|
||||
TS_ASSERT_EQUALS(1, results[0].elementIds.size());
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
index.addNode(1, NameHierarchy::deserialize("::\tmfoo\tsvoid\tp() const").getQualifiedName());
|
||||
index.finishSetup();
|
||||
index.clear();
|
||||
std::vector<SearchResult> results = index.search("oo", 0);
|
||||
std::vector<SearchResult> results = index.search("oo", 0, 0);
|
||||
|
||||
TS_ASSERT_EQUALS(0, results.size());
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
index.addNode(1, NameHierarchy::deserialize("::\tmfoo1\tsvoid\tp() const").getQualifiedName());
|
||||
index.addNode(2, NameHierarchy::deserialize("::\tmfoo2\tsvoid\tp() const").getQualifiedName());
|
||||
index.finishSetup();
|
||||
std::vector<SearchResult> results = index.search("oo", 1);
|
||||
std::vector<SearchResult> results = index.search("oo", 0, 1);
|
||||
|
||||
TS_ASSERT_EQUALS(1, results.size());
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
index.addNode(1, NameHierarchy::deserialize("::\tmfoo1\tsvoid\tp() const").getQualifiedName());
|
||||
index.addNode(2, NameHierarchy::deserialize("::\tmFOO2\tsvoid\tp() const").getQualifiedName());
|
||||
index.finishSetup();
|
||||
std::vector<SearchResult> results = index.search("oo", 0);
|
||||
std::vector<SearchResult> results = index.search("oo", 0, 0);
|
||||
|
||||
TS_ASSERT_EQUALS(2, results.size());
|
||||
}
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
index.addNode(1, NameHierarchy::deserialize("::\tmoaabbcc\tsvoid\tp() const").getQualifiedName());
|
||||
index.addNode(2, NameHierarchy::deserialize("::\tmocbcabc\tsvoid\tp() const").getQualifiedName());
|
||||
index.finishSetup();
|
||||
std::vector<SearchResult> results = index.search("abc", 0);
|
||||
std::vector<SearchResult> results = index.search("abc", 0, 0);
|
||||
|
||||
TS_ASSERT_EQUALS(2, results.size());
|
||||
TS_ASSERT_EQUALS("ocbcabc", results[0].text);
|
||||
|
||||
Reference in New Issue
Block a user