logic: search ui performance optimization

* replaced the implementation of the search index. the search index now uses a search-trie. edges have gates that can be used to check if a character can be matched when following the edge.
* SearchMatches now have a text and a list of NameHierarchies that share this node (instead of Ids, so that nodes must be found by names (which makes sure that SearchMatches are still valid after refresh).
* when parsing a project's description the CodeController now looks for NameHierarchies instead of SearchNode names to find ids of linked elements.
This commit is contained in:
malte_langkabel
2016-04-04 15:39:39 +02:00
parent ffc00829f0
commit f3dc12b0ed
21 changed files with 410 additions and 1367 deletions
@@ -14,7 +14,7 @@
LET'S GET STARTED!\n LET'S GET STARTED!\n
\tTo keep things simple just click the "main" symbol below to start the tour.\n \tTo keep things simple just click the "main" symbol below to start the tour.\n
\n \n
[main](); // <- start here\n\n [main\tint\r()](); // <- start here\n\n
</description> </description>
</info> </info>
<source> <source>
-168
View File
@@ -1,171 +1,3 @@
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Settings.cpp WARNING: File for Settings not found: data/TestSettings.xml
Token.cpp ERROR: Location Id was not referenced by this Token. Token.cpp ERROR: Location Id was not referenced by this Token.
Node.cpp WARNING: Cannot change NodeType after it was already set from namespace to class Node.cpp WARNING: Cannot change NodeType after it was already set from namespace to class
Edge.cpp ERROR: Nodes are not plain copies. Edge.cpp ERROR: Nodes are not plain copies.
-4
View File
@@ -134,12 +134,8 @@ add_files(
data/search/SearchIndex.cpp data/search/SearchIndex.cpp
data/search/SearchIndex.h data/search/SearchIndex.h
data/search/SearchNode.cpp
data/search/SearchNode.h
data/search/SearchMatch.cpp data/search/SearchMatch.cpp
data/search/SearchMatch.h data/search/SearchMatch.h
data/search/SearchResult.cpp
data/search/SearchResult.h
data/type/ArrayModifiedDataType.cpp data/type/ArrayModifiedDataType.cpp
data/type/ArrayModifiedDataType.h data/type/ArrayModifiedDataType.h
@@ -582,6 +582,7 @@ std::vector<std::string> CodeController::getProjectDescription(TokenLocationFile
return std::vector<std::string>(); return std::vector<std::string>();
} }
// todo fixme: this split currently prevents the next step from recognizing multi level name hierarchies.
std::vector<std::string> lines = utility::splitToVector(description, "\\n"); std::vector<std::string> lines = utility::splitToVector(description, "\\n");
size_t startLineNumber = 4; size_t startLineNumber = 4;
@@ -590,6 +591,7 @@ std::vector<std::string> CodeController::getProjectDescription(TokenLocationFile
std::string line = "\t" + lines[i]; std::string line = "\t" + lines[i];
line = utility::replace(line, "\\t", "\t"); line = utility::replace(line, "\\t", "\t");
line = utility::replace(line, "\\r", "\r");
size_t pos = 0; size_t pos = 0;
while (pos != std::string::npos) while (pos != std::string::npos)
@@ -602,21 +604,23 @@ std::vector<std::string> CodeController::getProjectDescription(TokenLocationFile
break; break;
} }
std::string tokenName = line.substr(posA + 1, posB - posA - 1); std::string serializedName = line.substr(posA + 1, posB - posA - 1);
Id tokenId = m_storageAccess->getIdForNodeWithSearchNameHierarchy(NameHierarchy(tokenName)); NameHierarchy nameHierarchy = NameHierarchy::deserialize(serializedName);
Id tokenId = m_storageAccess->getIdForNodeWithNameHierarchy(nameHierarchy);
if (tokenId > 0) if (tokenId > 0)
{ {
line.replace(posA, posB - posA + 1, tokenName); std::string nameString = nameHierarchy.getQualifiedName();
line.replace(posA, posB - posA + 1, nameString);
locationFile->addTokenLocation( locationFile->addTokenLocation(
0, tokenId, 0, tokenId,
startLineNumber + i, posA + 1, startLineNumber + i, posA + 1,
startLineNumber + i, posA + tokenName.size() startLineNumber + i, posA + nameString.size()
); );
} }
pos = posA + tokenName.size(); pos = posA + serializedName.size();
} }
lines[i] = line; lines[i] = line;
+63 -74
View File
@@ -39,8 +39,9 @@ Version Storage::getVersion() const
bool Storage::init() bool Storage::init()
{ {
m_commandIndex.addNode(NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ALL))); m_commandIndex.addNode(0, NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ALL)));
m_commandIndex.addNode(NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ERROR))); m_commandIndex.addNode(0, NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ERROR)));
m_commandIndex.finishSetup();
return m_sqliteStorage.init(); return m_sqliteStorage.init();
} }
@@ -54,7 +55,7 @@ void Storage::clear()
void Storage::clearCaches() void Storage::clearCaches()
{ {
m_tokenIndex.clear(); m_searchIndex.clear();
m_fileNodeIds.clear(); m_fileNodeIds.clear();
m_hierarchyCache.clear(); m_hierarchyCache.clear();
} }
@@ -135,11 +136,6 @@ std::vector<FileInfo> Storage::getInfoOnAllFiles() const
return fileInfos; return fileInfos;
} }
const SearchIndex& Storage::getSearchIndex() const
{
return m_tokenIndex;
}
void Storage::logStats() const void Storage::logStats() const
{ {
std::stringstream ss; std::stringstream ss;
@@ -194,17 +190,6 @@ Id Storage::getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) co
return m_sqliteStorage.getNodeBySerializedName(NameHierarchy::serialize(nameHierarchy)).id; return m_sqliteStorage.getNodeBySerializedName(NameHierarchy::serialize(nameHierarchy)).id;
} }
Id Storage::getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const
{
SearchNode* node = m_tokenIndex.getNode(nameHierarchy);
if (node)
{
return node->getFirstTokenId();
}
return 0;
}
Id Storage::getIdForEdge( Id Storage::getIdForEdge(
Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy
) const ) const
@@ -231,77 +216,80 @@ Node::NodeType Storage::getNodeTypeForNodeWithId(Id nodeId) const
std::vector<SearchMatch> Storage::getAutocompletionMatches(const std::string& query) const std::vector<SearchMatch> Storage::getAutocompletionMatches(const std::string& query) const
{ {
if (query.size() == m_cachedQuery.size() + 1 && query.find(m_cachedQuery) == 0 && m_cachedResults.size()) const size_t maxResultCount = 100;
std::vector<SearchResult> results = m_commandIndex.search(query, 0);
utility::append(results, m_searchIndex.search(query, maxResultCount));
std::vector<SearchMatch> matches;
for (size_t i = 0; i < results.size(); i++)
{ {
m_cachedResults = m_tokenIndex.runFuzzySearchCached(query, m_cachedResults); SearchMatch match;
}
else
{
m_cachedResults = m_tokenIndex.runFuzzySearch(query);
}
m_cachedQuery = query; if (results[i].elementIds.size() > 0)
SearchResults results = m_cachedResults;
SearchResults commandResults = m_commandIndex.runFuzzySearch(query);
results.insert(commandResults.begin(), commandResults.end());
std::vector<SearchMatch> matches = SearchIndex::getMatches(results, query);
LOG_INFO_STREAM(<< matches.size() << " matches for \"" << query << "\"");
if (matches.size() > 100)
{
matches.resize(100);
}
for (SearchMatch& match : matches)
{
if (!match.tokenIds.size())
{ {
match.searchType = SearchMatch::SEARCH_COMMAND; StorageNode firstNode(0, 0, "", 0);
match.typeName = "command"; for (std::set<Id>::const_iterator itElementIds = results[i].elementIds.begin(); itElementIds != results[i].elementIds.end(); itElementIds++)
continue;
}
Id elementId = *(match.tokenIds.cbegin());
if (m_sqliteStorage.isNode(elementId))
{
StorageNode node = m_sqliteStorage.getNodeById(elementId);
match.nodeType = Node::intToType(node.type);
match.typeName = Node::getTypeString(match.nodeType);
if (intToDefinitionType(node.definitionType) == DEFINITION_NONE && match.nodeType != Node::NODE_UNDEFINED)
{ {
match.typeName = "undefined " + match.typeName; Id elementId = *itElementIds;
if (elementId != 0)
{
StorageNode node = m_sqliteStorage.getNodeById(elementId);
match.nameHierarchies.push_back(NameHierarchy::deserialize(node.serializedName));
if (firstNode.id == 0)
{
firstNode = node;
}
}
}
match.text = results[i].text;
match.indices = results[i].indices;
if (firstNode.id != 0)
{
match.nodeType = Node::intToType(firstNode.type);
match.typeName = Node::getTypeString(match.nodeType);
if (intToDefinitionType(firstNode.definitionType) == DEFINITION_NONE && match.nodeType != Node::NODE_UNDEFINED)
{
match.typeName = "undefined " + match.typeName;
}
match.searchType = SearchMatch::SEARCH_TOKEN;
}
else
{
match.searchType = SearchMatch::SEARCH_COMMAND;
match.typeName = "command";
} }
} }
else
{
match.typeName = Edge::getTypeString(Edge::intToType(m_sqliteStorage.getEdgeById(elementId).type));
}
match.searchType = SearchMatch::SEARCH_TOKEN; matches.push_back(match);
} }
std::sort(matches.begin(), matches.end(), [](SearchMatch a, SearchMatch b){
return b.text.size() > a.text.size();
});
return matches; return matches;
} }
std::vector<SearchMatch> Storage::getSearchMatchesForTokenIds(const std::vector<Id>& tokenIds) const std::vector<SearchMatch> Storage::getSearchMatchesForTokenIds(const std::vector<Id>& elementIds) const
{ {
// todo: what if all these elements share the same node in the searchindex? in that case there should be only one search match.
std::vector<SearchMatch> matches; std::vector<SearchMatch> matches;
for (Id tokenId : tokenIds) for (Id elementId : elementIds)
{ {
SearchMatch match; SearchMatch match;
if (m_sqliteStorage.isFile(tokenId)) if (m_sqliteStorage.isFile(elementId))
{ {
match.nodeType = Node::NODE_FILE; match.nodeType = Node::NODE_FILE;
} }
else if (m_sqliteStorage.isNode(tokenId)) else if (m_sqliteStorage.isNode(elementId))
{ {
StorageNode node = m_sqliteStorage.getNodeById(tokenId); StorageNode node = m_sqliteStorage.getNodeById(elementId);
match.nodeType = Node::intToType(node.type); match.nodeType = Node::intToType(node.type);
} }
else else
@@ -309,8 +297,9 @@ std::vector<SearchMatch> Storage::getSearchMatchesForTokenIds(const std::vector<
continue; continue;
} }
match.tokenIds.insert(tokenId); NameHierarchy nameHierarchy = NameHierarchy::deserialize(m_sqliteStorage.getNodeById(elementId).serializedName);
match.nameHierarchy = m_tokenIndex.getNameHierarchyForTokenId(tokenId); match.text = nameHierarchy.getQualifiedName();
match.nameHierarchies.push_back(nameHierarchy.getQualifiedName());
match.searchType = SearchMatch::SEARCH_TOKEN; match.searchType = SearchMatch::SEARCH_TOKEN;
matches.push_back(match); matches.push_back(match);
@@ -492,10 +481,9 @@ std::vector<Id> Storage::getTokenIdsForMatches(const std::vector<SearchMatch>& m
std::set<Id> idSet; std::set<Id> idSet;
for (const SearchMatch& match : matches) for (const SearchMatch& match : matches)
{ {
SearchNode* searchNode = m_tokenIndex.getNode(match.nameHierarchy); for (size_t i = 0; i < match.nameHierarchies.size(); i++)
if (searchNode)
{ {
utility::append(idSet, searchNode->getTokenIds()); idSet.insert(m_sqliteStorage.getNodeBySerializedName(NameHierarchy::serialize(match.nameHierarchies[i])).id);
} }
} }
@@ -980,8 +968,9 @@ void Storage::buildSearchIndex()
{ {
for (StorageNode node: m_sqliteStorage.getAllNodes()) for (StorageNode node: m_sqliteStorage.getAllNodes())
{ {
m_tokenIndex.addTokenId(m_tokenIndex.addNode(NameHierarchy::deserialize(node.serializedName)), node.id); m_searchIndex.addNode(node.id, NameHierarchy::deserialize(node.serializedName));
} }
m_searchIndex.finishSetup();
} }
void Storage::buildHierarchyCache() void Storage::buildHierarchyCache()
+3 -8
View File
@@ -7,11 +7,11 @@
#include "utility/file/FilePath.h" #include "utility/file/FilePath.h"
#include "data/access/StorageAccess.h" #include "data/access/StorageAccess.h"
#include "data/HierarchyCache.h"
#include "data/graph/token_component/TokenComponentAccess.h" #include "data/graph/token_component/TokenComponentAccess.h"
#include "data/location/TokenLocationCollection.h" #include "data/location/TokenLocationCollection.h"
#include "data/parser/ParserClient.h" #include "data/parser/ParserClient.h"
#include "data/search/SearchIndex.h" #include "data/search/SearchIndex.h"
#include "data/HierarchyCache.h"
#include "data/SqliteStorage.h" #include "data/SqliteStorage.h"
#include "data/parser/ParserClientImpl.h" #include "data/parser/ParserClientImpl.h"
@@ -35,7 +35,6 @@ public:
void removeUnusedNames(); void removeUnusedNames();
std::vector<FileInfo> getInfoOnAllFiles() const; std::vector<FileInfo> getInfoOnAllFiles() const;
const SearchIndex& getSearchIndex() const;
void logStats() const; void logStats() const;
@@ -46,7 +45,6 @@ public:
// StorageAccess implementation // StorageAccess implementation
virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const; virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const;
virtual Id getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const;
virtual Id getIdForEdge( virtual Id getIdForEdge(
Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const; Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const;
@@ -56,7 +54,7 @@ public:
virtual Node::NodeType getNodeTypeForNodeWithId(Id nodeId) const; virtual Node::NodeType getNodeTypeForNodeWithId(Id nodeId) const;
virtual std::vector<SearchMatch> getAutocompletionMatches(const std::string& query) const; virtual std::vector<SearchMatch> getAutocompletionMatches(const std::string& query) const;
virtual std::vector<SearchMatch> getSearchMatchesForTokenIds(const std::vector<Id>& tokenIds) const; virtual std::vector<SearchMatch> getSearchMatchesForTokenIds(const std::vector<Id>& elementIds) const;
virtual std::shared_ptr<Graph> getGraphForAll() const; virtual std::shared_ptr<Graph> getGraphForAll() const;
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool activeOnly) const; virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool activeOnly) const;
@@ -105,16 +103,13 @@ private:
void log(std::string type, std::string str, const ParseLocation& location) const; void log(std::string type, std::string str, const ParseLocation& location) const;
SearchIndex m_tokenIndex;
SearchIndex m_commandIndex; SearchIndex m_commandIndex;
SearchIndex m_searchIndex;
SqliteStorage m_sqliteStorage; SqliteStorage m_sqliteStorage;
mutable std::map <FilePath, Id> m_fileNodeIds; mutable std::map <FilePath, Id> m_fileNodeIds;
HierarchyCache m_hierarchyCache; HierarchyCache m_hierarchyCache;
mutable SearchResults m_cachedResults;
mutable std::string m_cachedQuery;
}; };
#endif // STORAGE_H #endif // STORAGE_H
-1
View File
@@ -27,7 +27,6 @@ public:
virtual ~StorageAccess(); virtual ~StorageAccess();
virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const = 0; virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const = 0;
virtual Id getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const = 0;
virtual Id getIdForEdge( virtual Id getIdForEdge(
Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const = 0; Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const = 0;
@@ -43,16 +43,6 @@ Id StorageAccessProxy::getIdForNodeWithNameHierarchy(const NameHierarchy& nameHi
return 0; return 0;
} }
Id StorageAccessProxy::getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const
{
if (hasSubject())
{
return m_subject->getIdForNodeWithSearchNameHierarchy(nameHierarchy);
}
return 0;
}
Id StorageAccessProxy::getIdForEdge( Id StorageAccessProxy::getIdForEdge(
Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy
) const { ) const {
-1
View File
@@ -14,7 +14,6 @@ public:
// StorageAccess implementation // StorageAccess implementation
virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const; virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const;
virtual Id getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const;
virtual Id getIdForEdge( virtual Id getIdForEdge(
Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const; Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const;
+214 -164
View File
@@ -1,208 +1,258 @@
#include "data/search/SearchIndex.h" #include "data/search/SearchIndex.h"
#include <algorithm> #include <algorithm>
#include <cctype> #include <ctype.h>
#include "utility/logging/logging.h" #include "utility/utility.h"
#include "data/search/SearchMatch.h"
std::vector<SearchMatch> SearchIndex::getMatches(
const SearchResults& searchResults,
const std::string& query
){
std::vector<SearchMatch> result;
for (SearchResultsIterator it = searchResults.begin(); it != searchResults.end(); it++)
{
SearchMatch match = it->node->fuzzyMatchData(query, it->parent->getParent());
result.push_back(match);
}
return result;
}
SearchIndex::SearchIndex() SearchIndex::SearchIndex()
: m_root(nullptr, DELIMITER, 0)
{ {
clear();
} }
SearchIndex::~SearchIndex() SearchIndex::~SearchIndex()
{ {
} }
void SearchIndex::clear() void SearchIndex::addNode(Id id, const NameHierarchy& nameHierarchy)
{ {
m_root.m_nodes.clear(); Node* currentNode = m_root;
m_dictionary.clear();
m_tokenIds.clear();
}
size_t SearchIndex::getNodeCount() const // we don't use the signature here, so elements with the same signature share the same node in the search index.
{ std::string remaining = nameHierarchy.getQualifiedName();
return m_root.getNodeCount() - 1;
}
size_t SearchIndex::getCharCount() const while (remaining.size() > 0)
{
return m_dictionary.getCharCount();
}
size_t SearchIndex::getWordCount() const
{
return m_dictionary.getWordCount();
}
Id SearchIndex::getWordId(const std::string& word)
{
return m_dictionary.getWordId(word);
}
const std::string& SearchIndex::getWord(Id wordId) const
{
return m_dictionary.getWord(wordId);
}
SearchNode* SearchIndex::addNode(NameHierarchy nameHierarchy)
{
std::deque<Id> nameIds;
for (size_t i = 0; i < nameHierarchy.size(); i++)
{ {
nameIds.push_back(m_dictionary.getWordId(nameHierarchy[i]->getName())); bool matchingEdgeFound = false;
} // has edge starting with c?
for (size_t i = 0; i < currentNode->edges.size(); i++)
if (nameIds.size())
{
return m_root.addNodeRecursive(&nameIds, m_dictionary).get();
}
return nullptr;
}
SearchNode* SearchIndex::getNode(const NameHierarchy& nameHierarchy) const
{
std::deque<Id> nameIds = m_dictionary.getWordIdsConst(nameHierarchy);
if (nameIds.size())
{
return m_root.getNodeRecursive(&nameIds).get();
}
return nullptr;
}
SearchNode* SearchIndex::getNode(const std::string& fullName) const
{
std::deque<Id> nameIds = m_dictionary.getWordIdsConst(fullName, DELIMITER);
if (nameIds.size())
{
return m_root.getNodeRecursive(&nameIds).get();
}
return nullptr;
}
SearchNode* SearchIndex::getNode(const SearchNode* searchNode) const
{
if(searchNode == nullptr)
{
return nullptr;
}
std::deque<Id> nameIds = searchNode->getNameIdsRecursive();
if (nameIds.size())
{
return m_root.getNodeRecursive(&nameIds).get();
}
return nullptr;
}
void SearchIndex::removeNode(SearchNode* searchNode)
{
if(searchNode == nullptr)
{
return;
}
SearchNode* parent = searchNode->getParent();
if (!parent)
{
LOG_ERROR_STREAM(<< "SearchNode to be removed has no parent: " << searchNode->getFullName());
return;
}
parent->removeSearchNode(searchNode);
}
bool SearchIndex::removeNodeIfUnreferencedRecursive(SearchNode* searchNode)
{
if(searchNode == nullptr)
{
return false;
}
if (!searchNode->hasTokenIdsRecursive())
{
SearchNode* parent = searchNode->getParent();
removeNode(searchNode);
if (parent && parent != &m_root)
{ {
removeNodeIfUnreferencedRecursive(parent); Edge* currentEdge = currentNode->edges[i];
const std::string& edgeString = currentEdge->s;
int matchCount = 0;
for (size_t j = 0; j < edgeString.size() && j < remaining.size(); j++)
{
if (edgeString[j] != remaining[j])
{
break;
}
matchCount++;
}
if (matchCount != 0)
{
remaining = remaining.substr(matchCount);
if (matchCount < edgeString.size())
{
// split current edge
std::shared_ptr<Node> n = std::make_shared<Node>();
m_nodes.push_back(n);
std::shared_ptr<Edge> e = std::make_shared<Edge>();
m_edges.push_back(e);
n->edges.push_back(e.get());
e->s = edgeString.substr(matchCount);
e->target = currentEdge->target;
currentEdge->s = edgeString.substr(0, matchCount);
currentEdge->target = n.get();
}
currentNode = currentEdge->target;
matchingEdgeFound = true;
break;
}
} }
return true; if (!matchingEdgeFound)
{
std::shared_ptr<Node> n = std::make_shared<Node>();
m_nodes.push_back(n);
std::shared_ptr<Edge> e = std::make_shared<Edge>();
m_edges.push_back(e);
e->s = remaining;
e->target = n.get();
currentNode->edges.push_back(e.get());
currentNode = n.get();
remaining = "";
}
} }
return false; currentNode->elementIds.insert(id);
} }
void SearchIndex::addTokenId(SearchNode* node, Id tokenId) void SearchIndex::finishSetup()
{ {
if(node == nullptr) for (size_t i = 0; i < m_root->edges.size(); i++)
{ {
LOG_ERROR("Node points to nullptr: Can't add tokenid"); populateEdgeGate(m_root->edges[i]);
return;
} }
node->addTokenId(tokenId);
m_tokenIds.emplace(tokenId, node);
} }
NameHierarchy SearchIndex::getNameHierarchyForTokenId(Id tokenId) const void SearchIndex::clear()
{ {
std::map<Id, SearchNode*>::const_iterator it = m_tokenIds.find(tokenId); m_nodes.clear();
m_edges.clear();
if (it != m_tokenIds.end()) std::shared_ptr<Node> n = std::make_shared<Node>();
m_nodes.push_back(n);
m_root = n.get();
}
std::vector<SearchResult> SearchIndex::search(const std::string& query, size_t maxResultCount) const
{
std::string lowerCaseQuery = "";
for (size_t i = 0; i < query.size(); i++)
{ {
SearchNode* node = it->second; lowerCaseQuery += tolower(query[i]);
return node->getNameHierarchy();
} }
return NameHierarchy(); Path startPath;
startPath.node = m_root;
std::vector<Path> paths = search(startPath, lowerCaseQuery);
// scoring paths
std::vector<std::pair<float, Path>> scoredPaths;
for (size_t i = 0; i < paths.size(); i++)
{
const std::vector<size_t>& currentIndices = paths[i].indices;
int gapCount = 0;
for (size_t j = 1; j < currentIndices.size(); j++)
{
if (currentIndices[j] - currentIndices[j-1] > 1)
{
gapCount++;
}
}
float score = (1.0f / (gapCount + 1)) + (1.0f / (paths[i].text.size() + 1));
scoredPaths.push_back(std::make_pair(score, paths[i]));
}
// sorting paths
std::sort(scoredPaths.begin(), scoredPaths.end(), [](
std::pair<float, Path> a,
std::pair<float, Path> b)
{
return b.first < a.first;
}
);
// preparing results
std::vector<SearchResult> searchResults;
for (size_t i = 0; i < scoredPaths.size() && (maxResultCount == 0 || searchResults.size() < maxResultCount); i++)
{
std::vector<Path> currentPaths;
currentPaths.push_back(scoredPaths[i].second);
while (currentPaths.size() > 0)
{
std::vector<Path> nextPaths;
for (size_t j = 0; j < currentPaths.size(); j++)
{
Path& currentPath = currentPaths[j];
if (currentPath.node->elementIds.size() > 0 && (maxResultCount == 0 || searchResults.size() < maxResultCount))
{
SearchResult result;
result.elementIds = currentPath.node->elementIds;
result.indices = currentPath.indices;
result.text = currentPath.text;
searchResults.push_back(result);
}
for (size_t k = 0; k < currentPath.node->edges.size(); k++)
{
Path nextPath;
nextPath.indices = currentPath.indices;
nextPath.node = currentPath.node->edges[k]->target;
nextPath.text = currentPath.text + currentPath.node->edges[k]->s;
nextPaths.push_back(nextPath);
}
}
currentPaths = nextPaths;
if (!(maxResultCount == 0 || searchResults.size() < maxResultCount))
{
break;
}
}
}
return searchResults;
} }
SearchResults SearchIndex::runFuzzySearch(const std::string& query) const void SearchIndex::populateEdgeGate(Edge* e)
{ {
return m_root.runFuzzySearch(query); Node* target = e->target;
for (size_t i = 0; i < target->edges.size(); i++)
{
Edge* targetEdge = target->edges[i];
populateEdgeGate(targetEdge);
utility::append(e->gate, targetEdge->gate);
}
for (size_t i = 0; i < e->s.size(); i++)
{
e->gate.insert(tolower(e->s[i]));
}
} }
SearchResults SearchIndex::runFuzzySearchCached(const std::string& query, const SearchResults& searchResults) const std::vector<SearchIndex::Path> SearchIndex::search(const Path& path, const std::string& remainingQuery) const
{ {
return m_root.runFuzzySearchCached(query, searchResults); std::vector<Path> results;
}
std::vector<SearchMatch> SearchIndex::runFuzzySearchAndGetMatches(const std::string& query) const if (remainingQuery.size() == 0)
{ {
return getMatches(runFuzzySearch(query), query); results.push_back(path);
} }
else
{
for (size_t i = 0; i < path.node->edges.size(); i++)
{
const Edge* currentEdge = path.node->edges[i];
const std::string SearchIndex::DELIMITER = "::"; // test if s passes the edge's gate.
bool passesGate = true;
for (size_t j = 0; j < remainingQuery.size(); j++)
{
if (currentEdge->gate.find(tolower(remainingQuery[j])) == currentEdge->gate.end())
{
passesGate = false;
break;
}
}
std::ostream& operator<<(std::ostream& ostream, const SearchIndex& index) if (passesGate)
{ {
ostream << "SearchIndex:\n"; // consume characters for edge
ostream << &index.m_root; const std::string& edgeString = currentEdge->s;
return ostream;
std::vector<size_t> currentFoundIds = path.indices;
std::string currentRemainingQuery = remainingQuery;
for (size_t j = 0; j < edgeString.size() && currentRemainingQuery.size() > 0; j++)
{
if (currentRemainingQuery[0] == tolower(edgeString[j]))
{
currentFoundIds.push_back(path.text.size() + j);
currentRemainingQuery = currentRemainingQuery.substr(1);
}
}
Path currentPath;
currentPath.node = currentEdge->target;
currentPath.indices = currentFoundIds;
currentPath.text = path.text + edgeString;
utility::append(results, search(currentPath, currentRemainingQuery));
}
}
}
return results;
} }
+44 -37
View File
@@ -1,59 +1,66 @@
#ifndef SEARCH_INDEX_H #ifndef SEARCH_INDEX_H
#define SEARCH_INDEX_H #define SEARCH_INDEX_H
#include <ostream> #include <map>
#include <set> #include <memory>
#include <vector> #include <vector>
#include <set>
#include "utility/text/Dictionary.h" #include <string>
#include "utility/types.h" #include <unordered_set>
#include "data/name/NameHierarchy.h" #include "data/name/NameHierarchy.h"
#include "data/search/SearchNode.h" #include "utility/types.h"
struct SearchResult
{
std::string text;
std::set<Id> elementIds;
std::vector<size_t> indices;
};
class SearchIndex class SearchIndex
{ {
public: public:
static std::vector<SearchMatch> getMatches(const SearchResults& searchResults, const std::string& query);
SearchIndex(); SearchIndex();
virtual ~SearchIndex(); virtual ~SearchIndex();
void addNode(Id id, const NameHierarchy& nameHierarchy);
void finishSetup();
void clear(); void clear();
size_t getNodeCount() const; // maxResultCount == 0 means "no restriction".
size_t getCharCount() const; std::vector<SearchResult> search(const std::string& query, size_t maxResultCount) const;
size_t getWordCount() const;
Id getWordId(const std::string& word);
const std::string& getWord(Id wordId) const;
SearchNode* addNode(NameHierarchy nameHierarchy);
SearchNode* getNode(const NameHierarchy& nameHierarchy) const;
SearchNode* getNode(const std::string& fullName) const;
SearchNode* getNode(const SearchNode* searchNode) const;
void removeNode(SearchNode* searchNode);
bool removeNodeIfUnreferencedRecursive(SearchNode* searchNode);
void addTokenId(SearchNode* node, Id tokenId);
NameHierarchy getNameHierarchyForTokenId(Id tokenId) const;
SearchResults runFuzzySearch(const std::string& query) const;
SearchResults runFuzzySearchCached(const std::string& query, const SearchResults& searchResults) const;
std::vector<SearchMatch> runFuzzySearchAndGetMatches(const std::string& query) const;
static const std::string DELIMITER;
private: private:
SearchNode m_root; struct Node;
Dictionary m_dictionary; struct Edge;
std::map<Id, SearchNode*> m_tokenIds; struct Node
{
std::set<Id> elementIds;
std::vector<Edge*> edges;
};
friend std::ostream& operator<<(std::ostream& ostream, const SearchIndex& index); struct Edge
{
Node* target;
std::string s;
std::unordered_set<char> gate;
};
struct Path
{
std::string text;
std::vector<size_t> indices;
Node* node;
};
void populateEdgeGate(Edge* e);
std::vector<Path> search(const Path& path, const std::string& remainingQuery) const;
std::vector<std::shared_ptr<Node>> m_nodes;
std::vector<std::shared_ptr<Edge>> m_edges;
Node* m_root;
}; };
std::ostream& operator<<(std::ostream& ostream, const SearchIndex& index);
#endif // SEARCH_INDEX_H #endif // SEARCH_INDEX_H
+4 -4
View File
@@ -48,7 +48,7 @@ std::string SearchMatch::searchMatchesToString(const std::vector<SearchMatch>& m
SearchMatch SearchMatch::createCommand(CommandType type) SearchMatch SearchMatch::createCommand(CommandType type)
{ {
SearchMatch match; SearchMatch match;
match.nameHierarchy = NameHierarchy(getCommandName(type)); match.text = getCommandName(type);
match.typeName = "command"; match.typeName = "command";
match.searchType = SEARCH_COMMAND; match.searchType = SEARCH_COMMAND;
return match; return match;
@@ -74,7 +74,7 @@ SearchMatch::SearchMatch()
} }
SearchMatch::SearchMatch(const std::string& query) SearchMatch::SearchMatch(const std::string& query)
: nameHierarchy(query) : text(query)
, typeName("") , typeName("")
, searchType(SEARCH_NONE) , searchType(SEARCH_NONE)
{ {
@@ -87,7 +87,7 @@ bool SearchMatch::isValid() const
void SearchMatch::print(std::ostream& ostream) const void SearchMatch::print(std::ostream& ostream) const
{ {
ostream << weight << '\t' << nameHierarchy.getQualifiedName() << std::endl << '\t'; ostream << text << std::endl << '\t';
size_t i = 0; size_t i = 0;
for (size_t index : indices) for (size_t index : indices)
{ {
@@ -104,7 +104,7 @@ void SearchMatch::print(std::ostream& ostream) const
std::string SearchMatch::getFullName() const std::string SearchMatch::getFullName() const
{ {
return nameHierarchy.getQualifiedName(); return text;
} }
std::string SearchMatch::getNodeTypeAsString() const std::string SearchMatch::getNodeTypeAsString() const
+3 -6
View File
@@ -44,16 +44,13 @@ struct SearchMatch
std::string getNodeTypeAsString() const; std::string getNodeTypeAsString() const;
std::string getSearchTypeName() const; std::string getSearchTypeName() const;
NameHierarchy nameHierarchy; std::string text;
std::string typeName; std::string typeName;
Node::NodeType nodeType; Node::NodeType nodeType;
SearchType searchType; SearchType searchType;
std::set<Id> tokenIds;
std::vector<size_t> indices; std::vector<size_t> indices;
size_t weight;
std::vector<NameHierarchy> nameHierarchies;
}; };
-455
View File
@@ -1,455 +0,0 @@
#include "data/search/SearchNode.h"
#include <algorithm>
#include "utility/text/Dictionary.h"
#include "data/name/NameHierarchy.h"
#include "data/search/SearchIndex.h"
#include "data/search/SearchMatch.h"
#include "data/search/SearchResult.h"
SearchNode::SearchNode(SearchNode* parent, const std::string& name, Id nameId)
: m_parent(parent)
, m_name(name)
, m_nameId(nameId)
{
}
SearchNode::~SearchNode()
{
}
size_t SearchNode::getNodeCount() const
{
size_t count = 1;
for (const std::pair<Id, std::shared_ptr<SearchNode>>& p : m_nodes)
{
count += p.second->getNodeCount();
}
return count;
}
const std::string& SearchNode::getName() const
{
return m_name;
}
NameHierarchy SearchNode::getNameHierarchy() const
{
NameHierarchy nameHierarchy;
const SearchNode* parent = getParent();
if (parent && parent->m_nameId)
{
nameHierarchy = parent->getNameHierarchy();
}
nameHierarchy.push(std::make_shared<NameElement>(getName()));
return nameHierarchy;
}
std::string SearchNode::getFullName() const
{
if (m_parent && m_parent->m_nameId)
{
return m_parent->getFullName() + SearchIndex::DELIMITER + getName();
}
else
{
return getName();
}
}
Id SearchNode::getNameId() const
{
return m_nameId;
}
std::deque<Id> SearchNode::getNameIdsRecursive() const
{
std::deque<Id> ids;
ids.push_front(m_nameId);
SearchNode* parent = m_parent;
while (parent && parent->m_nameId)
{
ids.push_front(parent->getNameId());
parent = parent->getParent();
}
return ids;
}
Id SearchNode::getFirstTokenId() const
{
if (m_tokenIds.size())
{
return *m_tokenIds.begin();
}
return 0;
}
const std::set<Id>& SearchNode::getTokenIds() const
{
return m_tokenIds;
}
bool SearchNode::hasTokenIdsRecursive() const
{
if (m_tokenIds.size())
{
return true;
}
for (const std::pair<Id, std::shared_ptr<SearchNode>>& p : m_nodes)
{
if (p.second->hasTokenIdsRecursive())
{
return true;
}
}
return false;
}
void SearchNode::addTokenId(Id tokenId)
{
m_tokenIds.insert(tokenId);
}
void SearchNode::removeTokenId(Id tokenId)
{
m_tokenIds.erase(tokenId);
}
SearchNode* SearchNode::getParent() const
{
if (m_parent)
{
return m_parent;
}
return nullptr;
}
std::deque<SearchNode*> SearchNode::getParentsWithoutTokenId()
{
std::deque<SearchNode*> nodes;
SearchNode* node = this;
while (node->m_nameId && !node->m_tokenIds.size())
{
nodes.push_front(node);
node = node->m_parent;
}
return nodes;
}
const std::map<Id, std::shared_ptr<SearchNode>>& SearchNode::getChildren() const
{
return m_nodes;
}
SearchResults SearchNode::runFuzzySearch(const std::string& query) const
{
SearchResults result;
for (const std::pair<Id, std::shared_ptr<SearchNode>>& p : m_nodes)
{
FuzzyMap m = p.second->fuzzyMatchRecursive(query, 0, 0, 0);
for (const std::pair<size_t, const SearchNode*>& p2 : m)
{
addResultsRecursive(&result, p2.first, p2.second, p.second.get());
}
}
return result;
}
SearchResults SearchNode::runFuzzySearchCached(const std::string& query, const SearchResults& searchResults) const
{
SearchResults result;
std::set<const SearchNode*> nodes;
for (const SearchResult& r : searchResults)
{
nodes.insert(r.parent);
}
for (const SearchNode* n : nodes)
{
FuzzyMap m = n->fuzzyMatchRecursive(query, 0, 0, 0);
for (const std::pair<size_t, const SearchNode*>& p : m)
{
addResultsRecursive(&result, p.first, p.second, n);
}
}
return result;
}
SearchResults SearchNode::runFuzzySearchOnSelf(const std::string& query) const
{
SearchResults result;
FuzzyMap m = fuzzyMatchRecursive(query, 0, 0, 0);
for (const std::pair<size_t, const SearchNode*>& p : m)
{
addResultsRecursive(&result, p.first, p.second, this);
}
return result;
}
void SearchNode::addResultsRecursive(
SearchResults* results, size_t weight, const SearchNode* node, const SearchNode* parent
) const {
results->insert(SearchResult(weight, node, parent));
for (const std::pair<Id, std::shared_ptr<SearchNode>>& p : node->m_nodes)
{
addResultsRecursive(results, weight, p.second.get(), parent);
}
}
std::shared_ptr<SearchNode> SearchNode::addNodeRecursive(
std::deque<Id>* nameIds, const Dictionary& dictionary
){
Id nameId = nameIds->front();
nameIds->pop_front();
std::shared_ptr<SearchNode> node = getChildWithNameId(nameId);
if (!node)
{
node = std::make_shared<SearchNode>(this, dictionary.getWord(nameId), nameId);
m_nodes.emplace(nameId, node);
}
if (nameIds->size() > 0)
{
return node->addNodeRecursive(nameIds, dictionary);
}
return node;
}
std::shared_ptr<SearchNode> SearchNode::getNodeRecursive(std::deque<Id>* nameIds) const
{
Id nameId = nameIds->front();
nameIds->pop_front();
std::shared_ptr<SearchNode> node = getChildWithNameId(nameId);
if (node)
{
if (!nameIds->size())
{
return node;
}
return node->getNodeRecursive(nameIds);
}
return nullptr;
}
void SearchNode::removeSearchNode(SearchNode* node)
{
std::map<Id, std::shared_ptr<SearchNode>>::iterator it = m_nodes.find(node->getNameId());
if (it != m_nodes.end())
{
m_nodes.erase(it);
}
}
SearchMatch SearchNode::fuzzyMatchData(const std::string& query, const SearchNode* parent) const
{
SearchMatch data;
data.nameHierarchy = getNameHierarchy();
data.tokenIds = m_tokenIds;
data.weight = 0;
size_t pos = 0;
size_t size = 0;
const SearchNode* root = parent;
std::deque<const SearchNode*> nodes = getNodesToParent(parent);
if (!nodes.size())
{
nodes.push_back(this);
root = root->getParent();
}
while (root && root->getNameId())
{
size += root->getName().size() + SearchIndex::DELIMITER.size();
root = root->getParent();
}
for (const SearchNode* node : nodes)
{
std::pair<size_t, size_t> p = node->fuzzyMatch(query, pos, size, &data.indices);
pos = p.first;
data.weight += p.second;
size += node->m_name.size() + SearchIndex::DELIMITER.size();
}
return data;
}
SearchNode::FuzzyMap SearchNode::fuzzyMatchRecursive(
const std::string& query, size_t pos, size_t weight, size_t size) const
{
FuzzyMap result;
std::pair<size_t, size_t> p = fuzzyMatch(query, pos, size);
pos = p.first;
weight += p.second;
if (pos == query.size())
{
result.emplace(weight, this);
return result;
}
for (const std::pair<Id, std::shared_ptr<SearchNode>>& p : m_nodes)
{
FuzzyMap m = p.second->fuzzyMatchRecursive(query, pos, weight, size + m_name.size() + SearchIndex::DELIMITER.size());
result.insert(m.begin(), m.end());
}
return result;
}
std::pair<size_t, size_t> SearchNode::fuzzyMatch(
const std::string query, size_t start, size_t size, std::vector<size_t>* indices) const
{
size_t pos = start;
size_t weight = 0;
size_t matchCount = 0;
size_t ql = query.size();
if (!query.size())
{
return std::pair<size_t, size_t>(pos, weight);
}
if (query[pos] == ':')
{
pos++;
if (indices && size >= 2)
{
indices->push_back(size - 2);
}
if (pos < ql && query[pos] == ':')
{
pos++;
if (indices && size >= 1)
{
indices->push_back(size - 1);
}
}
}
char last = '\0';
size_t nl = m_name.size();
for (size_t i = 0; i < nl; i++)
{
char c = m_name[i];
char next = (i + 1 == nl ? '\0' : m_name[i + 1]);
if (tolower(query[pos]) == tolower(c))
{
weight += std::max<size_t>(100 / sqrt(size + i + 1), 1);
if (matchCount)
{
weight += matchCount * matchCount * 10;
}
if (i == 0)
{
weight += 20;
}
if (last == '_' || next == '_' || next == '\0')
{
weight += 20;
}
else if ((tolower(c) != c && tolower(next) == next) || (tolower(c) == c && tolower(next) != next))
{
weight += 20;
}
matchCount++;
pos++;
if (indices)
{
indices->push_back(size + i);
}
if (pos == ql || query[pos] == ':')
{
break;
}
}
else
{
matchCount = 0;
}
last = c;
}
return std::pair<size_t, size_t>(pos, weight);
}
std::shared_ptr<SearchNode> SearchNode::getChildWithNameId(Id nameId) const
{
std::map<Id, std::shared_ptr<SearchNode>>::const_iterator it = m_nodes.find(nameId);
if (it != m_nodes.end())
{
return it->second;
}
return nullptr;
}
std::deque<const SearchNode*> SearchNode::getNodesToParent(const SearchNode* parent) const
{
std::deque<const SearchNode*> nodes;
const SearchNode* node = this;
while (node->m_nameId && node != parent)
{
nodes.push_front(node);
node = node->m_parent;
}
return nodes;
}
std::ostream& operator<<(std::ostream& ostream, const SearchNode* node)
{
ostream << node->m_name;
for (Id tokenId : node->m_tokenIds)
{
ostream << ' ' << tokenId;
}
ostream << '\n';
for (const std::pair<Id, std::shared_ptr<SearchNode>> p : node->m_nodes)
{
ostream << p.second.get();
}
return ostream;
}
-87
View File
@@ -1,87 +0,0 @@
#ifndef SEARCH_NODE_H
#define SEARCH_NODE_H
#include <deque>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "utility/types.h"
#include "data/search/SearchResult.h"
class Dictionary;
class NameHierarchy;
class SearchIndex;
struct SearchMatch;
class SearchNode
{
public:
SearchNode(SearchNode* parent, const std::string& name, Id nameId);
~SearchNode();
size_t getNodeCount() const;
const std::string& getName() const;
NameHierarchy getNameHierarchy() const;
std::string getFullName() const;
Id getNameId() const;
std::deque<Id> getNameIdsRecursive() const;
Id getFirstTokenId() const;
const std::set<Id>& getTokenIds() const;
bool hasTokenIdsRecursive() const;
void addTokenId(Id tokenId);
void removeTokenId(Id tokenId);
SearchNode* getParent() const;
std::deque<SearchNode*> getParentsWithoutTokenId();
const std::map<Id, std::shared_ptr<SearchNode>>& getChildren() const;
SearchResults runFuzzySearch(const std::string& query) const;
SearchResults runFuzzySearchCached(const std::string& query, const SearchResults& searchResults) const;
SearchResults runFuzzySearchOnSelf(const std::string& query) const;
void addResultsRecursive(SearchResults* results, size_t weight, const SearchNode* node, const SearchNode* parent) const;
private:
typedef std::multimap<size_t, const SearchNode*> FuzzyMap;
typedef FuzzyMap::const_iterator FuzzyMapIterator;
// Accessed by SearchIndex
std::shared_ptr<SearchNode> addNodeRecursive(std::deque<Id>* nameIds, const Dictionary& dictionary);
std::shared_ptr<SearchNode> getNodeRecursive(std::deque<Id>* nameIds) const;
void removeSearchNode(SearchNode* node);
SearchMatch fuzzyMatchData(const std::string& query, const SearchNode* parent) const;
friend class SearchIndex;
FuzzyMap fuzzyMatchRecursive(const std::string& query, size_t pos, size_t weight, size_t size) const;
std::pair<size_t, size_t> fuzzyMatch(
const std::string query, size_t start, size_t size, std::vector<size_t>* indices = nullptr) const;
std::shared_ptr<SearchNode> getChildWithNameId(Id nameId) const;
std::deque<const SearchNode*> getNodesToParent(const SearchNode* parent) const;
std::map<Id, std::shared_ptr<SearchNode>> m_nodes;
SearchNode* m_parent;
std::set<Id> m_tokenIds;
const std::string& m_name;
const Id m_nameId;
friend std::ostream& operator<<(std::ostream& ostream, const SearchNode* node);
};
std::ostream& operator<<(std::ostream& ostream, const SearchNode* node);
#endif // SEARCH_NODE_H
-34
View File
@@ -1,34 +0,0 @@
#include "data/search/SearchResult.h"
#include "utility/utilityString.h"
#include "data/search/SearchNode.h"
SearchResult::SearchResult()
{
}
SearchResult::SearchResult(size_t weight, const SearchNode* node, const SearchNode* parent)
: weight(weight)
, node(node)
, parent(parent)
{
}
bool SearchResult::operator()(const SearchResult& lhs, const SearchResult& rhs) const
{
if (lhs.weight != rhs.weight)
{
return lhs.weight > rhs.weight;
}
std::string lhsLow = utility::toLowerCase(lhs.node->getFullName());
std::string rhsLow = utility::toLowerCase(rhs.node->getFullName());
if (lhsLow != rhsLow)
{
return lhsLow < rhsLow;
}
return lhs.node->getFirstTokenId() < rhs.node->getFirstTokenId();
}
-24
View File
@@ -1,24 +0,0 @@
#ifndef SEARCH_RESULT_H
#define SEARCH_RESULT_H
#include <set>
#include <stddef.h>
class SearchNode;
struct SearchResult
{
SearchResult();
SearchResult(size_t weight, const SearchNode* node, const SearchNode* parent);
bool operator()(const SearchResult& lhs, const SearchResult& rhs) const;
size_t weight;
const SearchNode* node;
const SearchNode* parent;
};
typedef std::set<SearchResult, SearchResult> SearchResults;
typedef SearchResults::const_iterator SearchResultsIterator;
#endif // SEARCH_RESULT_H
+10
View File
@@ -5,6 +5,7 @@
#include <deque> #include <deque>
#include <set> #include <set>
#include <time.h> #include <time.h>
#include <unordered_set>
#include "boost/date_time/posix_time/posix_time.hpp" #include "boost/date_time/posix_time/posix_time.hpp"
@@ -30,6 +31,9 @@ namespace utility
template<typename T> template<typename T>
void append(std::set<T>& a, const std::set<T>& b); void append(std::set<T>& a, const std::set<T>& b);
template<typename T>
void append(std::unordered_set<T>& a, const std::unordered_set<T>& b);
template<typename T> template<typename T>
std::vector<T> toVector(const std::deque<T>& d); std::vector<T> toVector(const std::deque<T>& d);
@@ -76,6 +80,12 @@ void utility::append(std::set<T>& a, const std::set<T>& b)
a.insert(b.begin(), b.end()); a.insert(b.begin(), b.end());
} }
template<typename T>
void utility::append(std::unordered_set<T>& a, const std::unordered_set<T>& b)
{
a.insert(b.begin(), b.end());
}
template<typename T> template<typename T>
std::vector<T> utility::toVector(const std::deque<T>& d) std::vector<T> utility::toVector(const std::deque<T>& d)
{ {
+5 -5
View File
@@ -396,7 +396,7 @@ void QtSmartSearchBox::onTextEdited(const QString& text)
} }
} }
if (match.nameHierarchy.size() && !m_allowMultipleElements) if (match.text.size() && !m_allowMultipleElements)
{ {
if (m_matches.size()) if (m_matches.size())
{ {
@@ -415,7 +415,7 @@ void QtSmartSearchBox::onTextEdited(const QString& text)
layoutElements(); layoutElements();
} }
if (match.nameHierarchy.size() || m_elements.size()) if (match.text.size() || m_elements.size())
{ {
requestAutoCompletions(); requestAutoCompletions();
} }
@@ -446,7 +446,7 @@ void QtSmartSearchBox::onAutocompletionActivated(const SearchMatch& match)
{ {
addMatchAndUpdate(match); addMatchAndUpdate(match);
if (match.nameHierarchy.size()) if (match.text.size())
{ {
search(); search();
} }
@@ -516,7 +516,7 @@ void QtSmartSearchBox::moveCursorTo(int target)
void QtSmartSearchBox::addMatch(const SearchMatch& match) void QtSmartSearchBox::addMatch(const SearchMatch& match)
{ {
if (!match.nameHierarchy.size()) if (!match.text.size())
{ {
return; return;
} }
@@ -543,7 +543,7 @@ void QtSmartSearchBox::addMatch(const SearchMatch& match)
void QtSmartSearchBox::addMatchAndUpdate(const SearchMatch& match) void QtSmartSearchBox::addMatchAndUpdate(const SearchMatch& match)
{ {
if (match.nameHierarchy.size()) if (match.text.size())
{ {
m_oldText.clear(); m_oldText.clear();
clearLineEdit(); clearLineEdit();
+52 -259
View File
@@ -3,302 +3,95 @@
#include "utility/utilityString.h" #include "utility/utilityString.h"
#include "data/search/SearchIndex.h" #include "data/search/SearchIndex.h"
#include "data/search/SearchMatch.h"
class SearchIndexTestSuite : public CxxTest::TestSuite class SearchIndexTestSuite : public CxxTest::TestSuite
{ {
public: public:
void test_add_node()
void test_search_index_finds_id_of_element_added()
{ {
SearchIndex index; SearchIndex index;
SearchNode* node = index.addNode(createNameHierarchy("util")); index.addNode(1, NameHierarchy::deserialize("foo\tvoid\r() const"));
index.finishSetup();
std::vector<SearchResult> results = index.search("oo", 0);
TS_ASSERT(node); TS_ASSERT_EQUALS(1, results.size());
TS_ASSERT_EQUALS("util", node->getName()); TS_ASSERT_EQUALS(1, results[0].elementIds.size());
TS_ASSERT_EQUALS("util", node->getFullName()); TS_ASSERT_DIFFERS(results[0].elementIds.end(), results[0].elementIds.find(1));
TS_ASSERT(node->getNameId());
TS_ASSERT(!node->getFirstTokenId());
TS_ASSERT(node->getParent());
TS_ASSERT(!node->getParent()->getNameId());
} }
void test_get_node() void test_search_index_finds_correct_indices_for_query()
{ {
SearchIndex index; SearchIndex index;
index.addNode(createNameHierarchy("util")); index.addNode(1, NameHierarchy::deserialize("foo\tvoid\r() const"));
SearchNode* node = index.getNode("util"); index.finishSetup();
std::vector<SearchResult> results = index.search("oo", 0);
TS_ASSERT(node); TS_ASSERT_EQUALS(1, results.size());
TS_ASSERT_EQUALS("util", node->getName()); TS_ASSERT_EQUALS(2, results[0].indices.size());
TS_ASSERT_EQUALS("util", node->getFullName()); TS_ASSERT_EQUALS(1, results[0].indices[0]);
TS_ASSERT_EQUALS(2, results[0].indices[1]);
TS_ASSERT(node->getNameId());
TS_ASSERT(!node->getFirstTokenId());
TS_ASSERT(node->getParent());
TS_ASSERT(!node->getParent()->getNameId());
node = index.getNode("math");
TS_ASSERT(!node);
} }
void test_add_hierarchy_node() void test_search_index_finds_ids_for_ambiguous_query()
{ {
SearchIndex index; SearchIndex index;
SearchNode* node = index.addNode(createNameHierarchy("util::math::pow")); index.addNode(1, NameHierarchy::deserialize("for\tvoid\r() const"));
index.addNode(2, NameHierarchy::deserialize("fos\tvoid\r() const"));
index.finishSetup();
std::vector<SearchResult> results = index.search("fo", 0);
TS_ASSERT(node); TS_ASSERT_EQUALS(2, results.size());
TS_ASSERT_EQUALS("pow", node->getName()); TS_ASSERT_EQUALS(1, results[0].elementIds.size());
TS_ASSERT_EQUALS("util::math::pow", node->getFullName()); TS_ASSERT_DIFFERS(results[0].elementIds.end(), results[0].elementIds.find(1));
TS_ASSERT_EQUALS(1, results[1].elementIds.size());
TS_ASSERT(node->getNameId()); TS_ASSERT_DIFFERS(results[1].elementIds.end(), results[1].elementIds.find(2));
TS_ASSERT(!node->getFirstTokenId());
TS_ASSERT(node->getParent());
TS_ASSERT_EQUALS("math", node->getParent()->getName());
TS_ASSERT(node->getParent()->getParent());
TS_ASSERT_EQUALS("util", node->getParent()->getParent()->getName());
} }
void test_reuse_hierarchy_node() void test_search_index_does_not_find_anything_after_clear()
{ {
SearchIndex index; SearchIndex index;
SearchNode* node1 = index.addNode(createNameHierarchy("math::pow")); index.addNode(1, NameHierarchy::deserialize("foo\tvoid\r() const"));
SearchNode* node2 = index.addNode(createNameHierarchy("math::floor")); index.finishSetup();
TS_ASSERT(node1);
TS_ASSERT(node2);
TS_ASSERT_EQUALS("pow", node1->getName());
TS_ASSERT_EQUALS("floor", node2->getName());
TS_ASSERT_EQUALS(node1->getParent(), node2->getParent());
}
void test_remove_nodes()
{
SearchIndex index;
index.addNode(createNameHierarchy("util::math::pow"));
index.addNode(createNameHierarchy("util::math::floor"));
index.removeNode(index.getNode("util::math::pow"));
TS_ASSERT(!index.getNode("util::math::pow"));
TS_ASSERT(index.getNode("util::math::floor"));
TS_ASSERT(index.getNode("util::math"));
index.removeNode(index.getNode("util::math"));
TS_ASSERT(!index.getNode("util::math::floor"));
TS_ASSERT(!index.getNode("util::math"));
TS_ASSERT(index.getNode("util"));
}
void test_remove_unreferenced_nodes()
{
SearchIndex index;
SearchNode* node1 = index.addNode(createNameHierarchy("util::math::pow"));
SearchNode* node2 = index.addNode(createNameHierarchy("util::math::floor"));
node1->addTokenId(1);
node2->addTokenId(2);
TS_ASSERT(index.getNode("util")->hasTokenIdsRecursive());
TS_ASSERT(!index.removeNodeIfUnreferencedRecursive(index.getNode("util")));
TS_ASSERT(!index.removeNodeIfUnreferencedRecursive(index.getNode("util::math")));
TS_ASSERT(!index.removeNodeIfUnreferencedRecursive(index.getNode("util::math::pow")));
node1->removeTokenId(1);
TS_ASSERT(index.removeNodeIfUnreferencedRecursive(index.getNode("util::math::pow")));
TS_ASSERT(!index.getNode("util::math::pow"));
TS_ASSERT(index.getNode("util::math"));
TS_ASSERT(index.getNode("util"));
node2->removeTokenId(2);
TS_ASSERT(index.removeNodeIfUnreferencedRecursive(index.getNode("util::math")));
TS_ASSERT(!index.getNode("util::math"));
TS_ASSERT(!index.getNode("util"));
}
void test_clear()
{
SearchIndex index;
index.addNode(createNameHierarchy("math"));
index.addNode(createNameHierarchy("string"));
TS_ASSERT(index.getNode("math"));
index.clear(); index.clear();
std::vector<SearchResult> results = index.search("oo", 0);
TS_ASSERT(!index.getNode("math")); TS_ASSERT_EQUALS(0, results.size());
TS_ASSERT(!index.getNode("string"));
} }
void test_fuzzy_matching() void test_search_index_does_not_find_all_results_when_max_amount_is_limited()
{ {
SearchIndex index; SearchIndex index;
index.addNode(createNameHierarchy("util")); index.addNode(1, NameHierarchy::deserialize("foo1\tvoid\r() const"));
index.addNode(createNameHierarchy("math")); index.addNode(2, NameHierarchy::deserialize("foo2\tvoid\r() const"));
index.addNode(createNameHierarchy("string")); index.finishSetup();
std::vector<SearchResult> results = index.search("oo", 1);
std::vector<SearchMatch> matches = index.runFuzzySearchAndGetMatches("u"); TS_ASSERT_EQUALS(1, results.size());
TS_ASSERT_EQUALS(1, matches.size());
TS_ASSERT_EQUALS("util", matches[0].getFullName());
TS_ASSERT_EQUALS(1, matches[0].indices.size());
TS_ASSERT_EQUALS(0, matches[0].indices[0]);
matches = index.runFuzzySearchAndGetMatches("");
TS_ASSERT_EQUALS(3, matches.size());
TS_ASSERT_EQUALS("math", matches[0].getFullName());
TS_ASSERT_EQUALS("string", matches[1].getFullName());
TS_ASSERT_EQUALS("util", matches[2].getFullName());
TS_ASSERT_EQUALS(0, matches[0].weight);
TS_ASSERT_EQUALS(0, matches[1].weight);
TS_ASSERT_EQUALS(0, matches[2].weight);
} }
void test_fuzzy_matching_is_case_insensitive() void test_search_index_query_is_case_insensitive()
{ {
SearchIndex index; SearchIndex index;
index.addNode(createNameHierarchy("util")); index.addNode(1, NameHierarchy::deserialize("foo1\tvoid\r() const"));
index.addNode(createNameHierarchy("MATH")); index.addNode(2, NameHierarchy::deserialize("FOO2\tvoid\r() const"));
index.finishSetup();
std::vector<SearchResult> results = index.search("oo", 0);
std::vector<SearchMatch> matches = index.runFuzzySearchAndGetMatches("t"); TS_ASSERT_EQUALS(2, results.size());
TS_ASSERT_EQUALS(2, matches.size());
TS_ASSERT_EQUALS("util", matches[0].getFullName());
TS_ASSERT_EQUALS("MATH", matches[1].getFullName());
matches = index.runFuzzySearchAndGetMatches("T");
TS_ASSERT_EQUALS(2, matches.size());
TS_ASSERT_EQUALS("util", matches[0].getFullName());
TS_ASSERT_EQUALS("MATH", matches[1].getFullName());
} }
void test_fuzzy_matching_wheighs_by_distance_and_alphabet() void test_search_index_rates_higher_on_consecutive_letters()
{ {
SearchIndex index; SearchIndex index;
index.addNode(createNameHierarchy("util")); index.addNode(1, NameHierarchy::deserialize("oaabbcc\tvoid\r() const"));
index.addNode(createNameHierarchy("math")); index.addNode(2, NameHierarchy::deserialize("ocbcabc\tvoid\r() const"));
index.addNode(createNameHierarchy("string")); index.finishSetup();
std::vector<SearchResult> results = index.search("abc", 0);
std::vector<SearchMatch> matches = index.runFuzzySearchAndGetMatches("t"); TS_ASSERT_EQUALS(2, results.size());
TS_ASSERT_EQUALS("ocbcabc", results[0].text);
TS_ASSERT_EQUALS(3, matches.size()); TS_ASSERT_EQUALS("oaabbcc", results[1].text);
TS_ASSERT_EQUALS("string", matches[0].getFullName());
TS_ASSERT_EQUALS("util", matches[1].getFullName());
TS_ASSERT_EQUALS("math", matches[2].getFullName());
TS_ASSERT_EQUALS(1, matches[0].indices.size());
TS_ASSERT_EQUALS(1, matches[0].indices[0]);
TS_ASSERT_EQUALS(1, matches[1].indices.size());
TS_ASSERT_EQUALS(1, matches[1].indices[0]);
TS_ASSERT_EQUALS(1, matches[2].indices.size());
TS_ASSERT_EQUALS(2, matches[2].indices[0]);
}
void test_fuzzy_matching_wheighs_higher_by_uppercase()
{
SearchIndex index;
index.addNode(createNameHierarchy("uTil"));
index.addNode(createNameHierarchy("string"));
std::vector<SearchMatch> matches = index.runFuzzySearchAndGetMatches("t");
TS_ASSERT_EQUALS(2, matches.size());
TS_ASSERT_EQUALS("uTil", matches[0].getFullName());
TS_ASSERT_EQUALS("string", matches[1].getFullName());
}
void test_fuzzy_matching_wheighs_higher_on_consecutive_letters()
{
SearchIndex index;
index.addNode(createNameHierarchy("oaabbcc"));
index.addNode(createNameHierarchy("ocbcabc"));
std::vector<SearchMatch> matches = index.runFuzzySearchAndGetMatches("abc");
TS_ASSERT_EQUALS(2, matches.size());
TS_ASSERT_EQUALS("ocbcabc", matches[0].getFullName());
TS_ASSERT_EQUALS("oaabbcc", matches[1].getFullName());
}
void test_fuzzy_matching_in_hierarchy()
{
SearchIndex index;
index.addNode(createNameHierarchy("util::math::ceil"));
index.addNode(createNameHierarchy("util::math::floor"));
index.addNode(createNameHierarchy("util::string::concat"));
std::vector<SearchMatch> matches = index.runFuzzySearchAndGetMatches("t");
TS_ASSERT_EQUALS(6, matches.size());
TS_ASSERT_EQUALS("util", matches[0].getFullName());
TS_ASSERT_EQUALS("util::math", matches[1].getFullName());
TS_ASSERT_EQUALS("util::math::ceil", matches[2].getFullName());
TS_ASSERT_EQUALS("util::math::floor", matches[3].getFullName());
TS_ASSERT_EQUALS("util::string", matches[4].getFullName());
TS_ASSERT_EQUALS("util::string::concat", matches[5].getFullName());
matches = index.runFuzzySearchAndGetMatches("uml");
TS_ASSERT_EQUALS(2, matches.size());
TS_ASSERT_EQUALS("util::math::ceil", matches[0].getFullName());
TS_ASSERT_EQUALS("util::math::floor", matches[1].getFullName());
}
void test_fuzzy_matching_in_hierarchy_respects_collin()
{
SearchIndex index;
index.addNode(createNameHierarchy("util::math::ceil"));
index.addNode(createNameHierarchy("util::math::floor"));
index.addNode(createNameHierarchy("util::string::concat"));
std::vector<SearchMatch> matches = index.runFuzzySearchAndGetMatches("u:i");
TS_ASSERT_EQUALS(3, matches.size());
TS_ASSERT_EQUALS("util::string", matches[0].getFullName());
TS_ASSERT_EQUALS("util::string::concat", matches[1].getFullName());
TS_ASSERT_EQUALS("util::math::ceil", matches[2].getFullName());
matches = index.runFuzzySearchAndGetMatches("u:t:i");
TS_ASSERT_EQUALS(1, matches.size());
TS_ASSERT_EQUALS("util::math::ceil", matches[0].getFullName());
}
void test_fuzzy_matching_in_hierarchy_weighs_front_letters_higher()
{
SearchIndex index;
index.addNode(createNameHierarchy("abc::dfe::ghi"));
index.addNode(createNameHierarchy("abc::hgi"));
std::vector<SearchMatch> matches = index.runFuzzySearchAndGetMatches("g");
TS_ASSERT_EQUALS(2, matches.size());
TS_ASSERT_EQUALS("abc::dfe::ghi", matches[0].getFullName());
TS_ASSERT_EQUALS("abc::hgi", matches[1].getFullName());
}
private:
NameHierarchy createNameHierarchy(std::string s) const
{
NameHierarchy nameHierarchy;
for (std::string element: utility::splitToVector(s, "::"))
{
nameHierarchy.push(std::make_shared<NameElement>(element));
}
return nameHierarchy;
} }
}; };
+2 -20
View File
@@ -97,7 +97,6 @@ public:
TS_ASSERT_EQUALS(storage.getNodeCount(), 3); TS_ASSERT_EQUALS(storage.getNodeCount(), 3);
TS_ASSERT_EQUALS(storage.getEdgeCount(), 2); TS_ASSERT_EQUALS(storage.getEdgeCount(), 2);
TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4); TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4);
TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 3);
std::set<FilePath> files; std::set<FilePath> files;
files.insert(FilePath(m_filePath)); files.insert(FilePath(m_filePath));
@@ -105,8 +104,7 @@ public:
TS_ASSERT_EQUALS(storage.getNodeCount(), 0); TS_ASSERT_EQUALS(storage.getNodeCount(), 0);
TS_ASSERT_EQUALS(storage.getEdgeCount(), 0); TS_ASSERT_EQUALS(storage.getEdgeCount(), 0);
TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0); TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0);*/
TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 0);*/
} }
void test_storage_clears_unreferenced_single_file_data_of_multi_file_storage() void test_storage_clears_unreferenced_single_file_data_of_multi_file_storage()
@@ -128,7 +126,6 @@ public:
//TS_ASSERT_EQUALS(storage.getNodeCount(), 6); //TS_ASSERT_EQUALS(storage.getNodeCount(), 6);
//TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5);
//TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9);
//TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 6);
//std::set<FilePath> files; //std::set<FilePath> files;
//files.insert(FilePath("file.cpp")); //files.insert(FilePath("file.cpp"));
@@ -136,8 +133,7 @@ public:
//TS_ASSERT_EQUALS(storage.getNodeCount(), 3); //TS_ASSERT_EQUALS(storage.getNodeCount(), 3);
//TS_ASSERT_EQUALS(storage.getEdgeCount(), 2); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 2);
//TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4);*/
//TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 3);
} }
void test_storage_clears_referenced_single_file_data_of_multi_file_storage() void test_storage_clears_referenced_single_file_data_of_multi_file_storage()
@@ -159,7 +155,6 @@ public:
//TS_ASSERT_EQUALS(storage.getNodeCount(), 5); //TS_ASSERT_EQUALS(storage.getNodeCount(), 5);
//TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5);
//TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9);
//TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 5);
//std::set<FilePath> files; //std::set<FilePath> files;
//files.insert(FilePath("file.h")); //files.insert(FilePath("file.h"));
@@ -168,7 +163,6 @@ public:
//TS_ASSERT_EQUALS(storage.getNodeCount(), 4); //TS_ASSERT_EQUALS(storage.getNodeCount(), 4);
//TS_ASSERT_EQUALS(storage.getEdgeCount(), 3); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 3);
//TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 5); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 5);
//TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 4);
} }
void test_storage_clears_multi_file_data_of_multi_file_storage() void test_storage_clears_multi_file_data_of_multi_file_storage()
@@ -190,7 +184,6 @@ public:
//TS_ASSERT_EQUALS(storage.getNodeCount(), 5); //TS_ASSERT_EQUALS(storage.getNodeCount(), 5);
//TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5);
//TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9);
//TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 5);
//std::set<FilePath> filePaths; //std::set<FilePath> filePaths;
//filePaths.insert(FilePath("file.cpp")); //filePaths.insert(FilePath("file.cpp"));
@@ -200,7 +193,6 @@ public:
//TS_ASSERT_EQUALS(storage.getNodeCount(), 0); //TS_ASSERT_EQUALS(storage.getNodeCount(), 0);
//TS_ASSERT_EQUALS(storage.getEdgeCount(), 0); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 0);
//TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0);
//TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 0);
} }
void test_storage_finds_and_removes_depending_file_nodes() void test_storage_finds_and_removes_depending_file_nodes()
@@ -248,11 +240,6 @@ private:
return getTokenLocationsForTokenIds(tokenIds); return getTokenLocationsForTokenIds(tokenIds);
} }
//const std::string& getWord(Id wordId) const
//{
// return getSearchIndex().getWord(wordId);
//}
//const size_t getNodeCount() const //const size_t getNodeCount() const
//{ //{
// return getGraph().getNodeCount(); // return getGraph().getNodeCount();
@@ -263,11 +250,6 @@ private:
// return getGraph().getEdgeCount(); // return getGraph().getEdgeCount();
//} //}
const SearchIndex& searchIndex() const
{
return getSearchIndex();
}
Id getEdgeId(Edge::EdgeType type, const NameHierarchy& fromName, const NameHierarchy& toName) const Id getEdgeId(Edge::EdgeType type, const NameHierarchy& fromName, const NameHierarchy& toName) const
{ {
NameHierarchy from; NameHierarchy from;