From f3dc12b0edcf9dc8dfd1e0fe7e30a5997d65ae1b Mon Sep 17 00:00:00 2001 From: malte_langkabel Date: Mon, 4 Apr 2016 15:39:39 +0200 Subject: [PATCH] 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. --- .../projects/tutorial/tutorial.coatiproject | 2 +- bin/test/data/log/test_log.txt | 168 ------- src/lib/CMakeLists.txt | 4 - .../component/controller/CodeController.cpp | 14 +- src/lib/data/Storage.cpp | 137 +++--- src/lib/data/Storage.h | 11 +- src/lib/data/access/StorageAccess.h | 1 - src/lib/data/access/StorageAccessProxy.cpp | 10 - src/lib/data/access/StorageAccessProxy.h | 1 - src/lib/data/search/SearchIndex.cpp | 378 ++++++++------- src/lib/data/search/SearchIndex.h | 81 ++-- src/lib/data/search/SearchMatch.cpp | 8 +- src/lib/data/search/SearchMatch.h | 9 +- src/lib/data/search/SearchNode.cpp | 455 ------------------ src/lib/data/search/SearchNode.h | 87 ---- src/lib/data/search/SearchResult.cpp | 34 -- src/lib/data/search/SearchResult.h | 24 - src/lib/utility/utility.h | 10 + src/lib_gui/qt/element/QtSmartSearchBox.cpp | 10 +- src/test/SearchIndexTestSuite.h | 311 ++---------- src/test/StorageTestSuite.h | 22 +- 21 files changed, 410 insertions(+), 1367 deletions(-) delete mode 100644 src/lib/data/search/SearchNode.cpp delete mode 100644 src/lib/data/search/SearchNode.h delete mode 100644 src/lib/data/search/SearchResult.cpp delete mode 100644 src/lib/data/search/SearchResult.h diff --git a/bin/app/data/projects/tutorial/tutorial.coatiproject b/bin/app/data/projects/tutorial/tutorial.coatiproject index 163cdb52..594137bf 100644 --- a/bin/app/data/projects/tutorial/tutorial.coatiproject +++ b/bin/app/data/projects/tutorial/tutorial.coatiproject @@ -14,7 +14,7 @@ LET'S GET STARTED!\n \tTo keep things simple just click the "main" symbol below to start the tour.\n \n - [main](); // <- start here\n\n + [main\tint\r()](); // <- start here\n\n diff --git a/bin/test/data/log/test_log.txt b/bin/test/data/log/test_log.txt index 523dd8f6..c99f7782 100644 --- a/bin/test/data/log/test_log.txt +++ b/bin/test/data/log/test_log.txt @@ -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. Node.cpp WARNING: Cannot change NodeType after it was already set from namespace to class Edge.cpp ERROR: Nodes are not plain copies. diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 692e03c0..407907b4 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -134,12 +134,8 @@ add_files( data/search/SearchIndex.cpp data/search/SearchIndex.h - data/search/SearchNode.cpp - data/search/SearchNode.h data/search/SearchMatch.cpp data/search/SearchMatch.h - data/search/SearchResult.cpp - data/search/SearchResult.h data/type/ArrayModifiedDataType.cpp data/type/ArrayModifiedDataType.h diff --git a/src/lib/component/controller/CodeController.cpp b/src/lib/component/controller/CodeController.cpp index 660301f6..3ecd21a0 100644 --- a/src/lib/component/controller/CodeController.cpp +++ b/src/lib/component/controller/CodeController.cpp @@ -582,6 +582,7 @@ std::vector CodeController::getProjectDescription(TokenLocationFile return std::vector(); } + // todo fixme: this split currently prevents the next step from recognizing multi level name hierarchies. std::vector lines = utility::splitToVector(description, "\\n"); size_t startLineNumber = 4; @@ -590,6 +591,7 @@ std::vector CodeController::getProjectDescription(TokenLocationFile std::string line = "\t" + lines[i]; line = utility::replace(line, "\\t", "\t"); + line = utility::replace(line, "\\r", "\r"); size_t pos = 0; while (pos != std::string::npos) @@ -602,21 +604,23 @@ std::vector CodeController::getProjectDescription(TokenLocationFile 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) { - line.replace(posA, posB - posA + 1, tokenName); + std::string nameString = nameHierarchy.getQualifiedName(); + line.replace(posA, posB - posA + 1, nameString); locationFile->addTokenLocation( 0, tokenId, 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; diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 977c9b62..f676ee68 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -39,8 +39,9 @@ Version Storage::getVersion() const bool Storage::init() { - m_commandIndex.addNode(NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ALL))); - m_commandIndex.addNode(NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ERROR))); + m_commandIndex.addNode(0, NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ALL))); + m_commandIndex.addNode(0, NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ERROR))); + m_commandIndex.finishSetup(); return m_sqliteStorage.init(); } @@ -54,7 +55,7 @@ void Storage::clear() void Storage::clearCaches() { - m_tokenIndex.clear(); + m_searchIndex.clear(); m_fileNodeIds.clear(); m_hierarchyCache.clear(); } @@ -135,11 +136,6 @@ std::vector Storage::getInfoOnAllFiles() const return fileInfos; } -const SearchIndex& Storage::getSearchIndex() const -{ - return m_tokenIndex; -} - void Storage::logStats() const { std::stringstream ss; @@ -194,17 +190,6 @@ Id Storage::getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) co 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( Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy ) const @@ -231,77 +216,80 @@ Node::NodeType Storage::getNodeTypeForNodeWithId(Id nodeId) const std::vector 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 results = m_commandIndex.search(query, 0); + utility::append(results, m_searchIndex.search(query, maxResultCount)); + + std::vector matches; + for (size_t i = 0; i < results.size(); i++) { - m_cachedResults = m_tokenIndex.runFuzzySearchCached(query, m_cachedResults); - } - else - { - m_cachedResults = m_tokenIndex.runFuzzySearch(query); - } + SearchMatch match; - m_cachedQuery = query; - - SearchResults results = m_cachedResults; - SearchResults commandResults = m_commandIndex.runFuzzySearch(query); - results.insert(commandResults.begin(), commandResults.end()); - - std::vector 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()) + if (results[i].elementIds.size() > 0) { - match.searchType = SearchMatch::SEARCH_COMMAND; - match.typeName = "command"; - 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) + StorageNode firstNode(0, 0, "", 0); + for (std::set::const_iterator itElementIds = results[i].elementIds.begin(); itElementIds != results[i].elementIds.end(); itElementIds++) { - 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; } -std::vector Storage::getSearchMatchesForTokenIds(const std::vector& tokenIds) const +std::vector Storage::getSearchMatchesForTokenIds(const std::vector& 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 matches; - for (Id tokenId : tokenIds) + for (Id elementId : elementIds) { SearchMatch match; - if (m_sqliteStorage.isFile(tokenId)) + if (m_sqliteStorage.isFile(elementId)) { 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); } else @@ -309,8 +297,9 @@ std::vector Storage::getSearchMatchesForTokenIds(const std::vector< continue; } - match.tokenIds.insert(tokenId); - match.nameHierarchy = m_tokenIndex.getNameHierarchyForTokenId(tokenId); + NameHierarchy nameHierarchy = NameHierarchy::deserialize(m_sqliteStorage.getNodeById(elementId).serializedName); + match.text = nameHierarchy.getQualifiedName(); + match.nameHierarchies.push_back(nameHierarchy.getQualifiedName()); match.searchType = SearchMatch::SEARCH_TOKEN; matches.push_back(match); @@ -492,10 +481,9 @@ std::vector Storage::getTokenIdsForMatches(const std::vector& m std::set idSet; for (const SearchMatch& match : matches) { - SearchNode* searchNode = m_tokenIndex.getNode(match.nameHierarchy); - if (searchNode) + for (size_t i = 0; i < match.nameHierarchies.size(); i++) { - 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()) { - 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() diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 4f776d28..790c5e03 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -7,11 +7,11 @@ #include "utility/file/FilePath.h" #include "data/access/StorageAccess.h" -#include "data/HierarchyCache.h" #include "data/graph/token_component/TokenComponentAccess.h" #include "data/location/TokenLocationCollection.h" #include "data/parser/ParserClient.h" #include "data/search/SearchIndex.h" +#include "data/HierarchyCache.h" #include "data/SqliteStorage.h" #include "data/parser/ParserClientImpl.h" @@ -35,7 +35,6 @@ public: void removeUnusedNames(); std::vector getInfoOnAllFiles() const; - const SearchIndex& getSearchIndex() const; void logStats() const; @@ -46,7 +45,6 @@ public: // StorageAccess implementation virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const; - virtual Id getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const; virtual Id getIdForEdge( Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const; @@ -56,7 +54,7 @@ public: virtual Node::NodeType getNodeTypeForNodeWithId(Id nodeId) const; virtual std::vector getAutocompletionMatches(const std::string& query) const; - virtual std::vector getSearchMatchesForTokenIds(const std::vector& tokenIds) const; + virtual std::vector getSearchMatchesForTokenIds(const std::vector& elementIds) const; virtual std::shared_ptr getGraphForAll() const; virtual std::shared_ptr getGraphForActiveTokenIds(const std::vector& tokenIds, bool activeOnly) const; @@ -105,16 +103,13 @@ private: void log(std::string type, std::string str, const ParseLocation& location) const; - SearchIndex m_tokenIndex; SearchIndex m_commandIndex; + SearchIndex m_searchIndex; SqliteStorage m_sqliteStorage; mutable std::map m_fileNodeIds; HierarchyCache m_hierarchyCache; - - mutable SearchResults m_cachedResults; - mutable std::string m_cachedQuery; }; #endif // STORAGE_H diff --git a/src/lib/data/access/StorageAccess.h b/src/lib/data/access/StorageAccess.h index ca39fc78..4bd66536 100644 --- a/src/lib/data/access/StorageAccess.h +++ b/src/lib/data/access/StorageAccess.h @@ -27,7 +27,6 @@ public: virtual ~StorageAccess(); virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const = 0; - virtual Id getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const = 0; virtual Id getIdForEdge( Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const = 0; diff --git a/src/lib/data/access/StorageAccessProxy.cpp b/src/lib/data/access/StorageAccessProxy.cpp index c4ce7197..273da0dd 100644 --- a/src/lib/data/access/StorageAccessProxy.cpp +++ b/src/lib/data/access/StorageAccessProxy.cpp @@ -43,16 +43,6 @@ Id StorageAccessProxy::getIdForNodeWithNameHierarchy(const NameHierarchy& nameHi return 0; } -Id StorageAccessProxy::getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const -{ - if (hasSubject()) - { - return m_subject->getIdForNodeWithSearchNameHierarchy(nameHierarchy); - } - - return 0; -} - Id StorageAccessProxy::getIdForEdge( Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy ) const { diff --git a/src/lib/data/access/StorageAccessProxy.h b/src/lib/data/access/StorageAccessProxy.h index 6d25439c..0a536002 100644 --- a/src/lib/data/access/StorageAccessProxy.h +++ b/src/lib/data/access/StorageAccessProxy.h @@ -14,7 +14,6 @@ public: // StorageAccess implementation virtual Id getIdForNodeWithNameHierarchy(const NameHierarchy& nameHierarchy) const; - virtual Id getIdForNodeWithSearchNameHierarchy(const NameHierarchy& nameHierarchy) const; virtual Id getIdForEdge( Edge::EdgeType type, const NameHierarchy& fromNameHierarchy, const NameHierarchy& toNameHierarchy) const; diff --git a/src/lib/data/search/SearchIndex.cpp b/src/lib/data/search/SearchIndex.cpp index 2d3c6e48..f089bee5 100644 --- a/src/lib/data/search/SearchIndex.cpp +++ b/src/lib/data/search/SearchIndex.cpp @@ -1,208 +1,258 @@ #include "data/search/SearchIndex.h" #include -#include +#include -#include "utility/logging/logging.h" - -#include "data/search/SearchMatch.h" - -std::vector SearchIndex::getMatches( - const SearchResults& searchResults, - const std::string& query -){ - std::vector 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; -} +#include "utility/utility.h" SearchIndex::SearchIndex() - : m_root(nullptr, DELIMITER, 0) { + clear(); } SearchIndex::~SearchIndex() { } -void SearchIndex::clear() +void SearchIndex::addNode(Id id, const NameHierarchy& nameHierarchy) { - m_root.m_nodes.clear(); - m_dictionary.clear(); - m_tokenIds.clear(); -} + Node* currentNode = m_root; -size_t SearchIndex::getNodeCount() const -{ - return m_root.getNodeCount() - 1; -} + // 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(); -size_t SearchIndex::getCharCount() const -{ - 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 nameIds; - for (size_t i = 0; i < nameHierarchy.size(); i++) + while (remaining.size() > 0) { - nameIds.push_back(m_dictionary.getWordId(nameHierarchy[i]->getName())); - } - - if (nameIds.size()) - { - return m_root.addNodeRecursive(&nameIds, m_dictionary).get(); - } - - return nullptr; -} - -SearchNode* SearchIndex::getNode(const NameHierarchy& nameHierarchy) const -{ - std::deque 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 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 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) + bool matchingEdgeFound = false; + // has edge starting with c? + for (size_t i = 0; i < currentNode->edges.size(); i++) { - 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 n = std::make_shared(); + m_nodes.push_back(n); + std::shared_ptr e = std::make_shared(); + 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 n = std::make_shared(); + m_nodes.push_back(n); + std::shared_ptr e = std::make_shared(); + 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"); - return; + populateEdgeGate(m_root->edges[i]); } - node->addTokenId(tokenId); - m_tokenIds.emplace(tokenId, node); } -NameHierarchy SearchIndex::getNameHierarchyForTokenId(Id tokenId) const +void SearchIndex::clear() { - std::map::const_iterator it = m_tokenIds.find(tokenId); + m_nodes.clear(); + m_edges.clear(); - if (it != m_tokenIds.end()) + std::shared_ptr n = std::make_shared(); + m_nodes.push_back(n); + + m_root = n.get(); +} + +std::vector 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; - return node->getNameHierarchy(); + lowerCaseQuery += tolower(query[i]); } - return NameHierarchy(); + Path startPath; + startPath.node = m_root; + std::vector paths = search(startPath, lowerCaseQuery); + + // scoring paths + std::vector> scoredPaths; + for (size_t i = 0; i < paths.size(); i++) + { + const std::vector& 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 a, + std::pair b) + { + return b.first < a.first; + } + ); + + // preparing results + std::vector searchResults; + for (size_t i = 0; i < scoredPaths.size() && (maxResultCount == 0 || searchResults.size() < maxResultCount); i++) + { + std::vector currentPaths; + currentPaths.push_back(scoredPaths[i].second); + + while (currentPaths.size() > 0) + { + std::vector 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::search(const Path& path, const std::string& remainingQuery) const { - return m_root.runFuzzySearchCached(query, searchResults); -} + std::vector results; -std::vector SearchIndex::runFuzzySearchAndGetMatches(const std::string& query) const -{ - return getMatches(runFuzzySearch(query), query); -} + if (remainingQuery.size() == 0) + { + 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) -{ - ostream << "SearchIndex:\n"; - ostream << &index.m_root; - return ostream; + if (passesGate) + { + // consume characters for edge + const std::string& edgeString = currentEdge->s; + + std::vector 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; } diff --git a/src/lib/data/search/SearchIndex.h b/src/lib/data/search/SearchIndex.h index 300a653e..4ce59ae6 100644 --- a/src/lib/data/search/SearchIndex.h +++ b/src/lib/data/search/SearchIndex.h @@ -1,59 +1,66 @@ #ifndef SEARCH_INDEX_H #define SEARCH_INDEX_H -#include -#include +#include +#include #include - -#include "utility/text/Dictionary.h" -#include "utility/types.h" +#include +#include +#include #include "data/name/NameHierarchy.h" -#include "data/search/SearchNode.h" +#include "utility/types.h" + +struct SearchResult +{ + std::string text; + std::set elementIds; + std::vector indices; +}; class SearchIndex { public: - static std::vector getMatches(const SearchResults& searchResults, const std::string& query); - SearchIndex(); virtual ~SearchIndex(); + void addNode(Id id, const NameHierarchy& nameHierarchy); + void finishSetup(); void clear(); - size_t getNodeCount() const; - size_t getCharCount() 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 runFuzzySearchAndGetMatches(const std::string& query) const; - - static const std::string DELIMITER; + // maxResultCount == 0 means "no restriction". + std::vector search(const std::string& query, size_t maxResultCount) const; private: - SearchNode m_root; - Dictionary m_dictionary; + struct Node; + struct Edge; - std::map m_tokenIds; + struct Node + { + std::set elementIds; + std::vector edges; + }; - friend std::ostream& operator<<(std::ostream& ostream, const SearchIndex& index); + struct Edge + { + Node* target; + std::string s; + std::unordered_set gate; + }; + + struct Path + { + std::string text; + std::vector indices; + Node* node; + }; + + void populateEdgeGate(Edge* e); + std::vector search(const Path& path, const std::string& remainingQuery) const; + + std::vector> m_nodes; + std::vector> m_edges; + Node* m_root; }; -std::ostream& operator<<(std::ostream& ostream, const SearchIndex& index); - #endif // SEARCH_INDEX_H diff --git a/src/lib/data/search/SearchMatch.cpp b/src/lib/data/search/SearchMatch.cpp index 9869391d..a644f9b1 100644 --- a/src/lib/data/search/SearchMatch.cpp +++ b/src/lib/data/search/SearchMatch.cpp @@ -48,7 +48,7 @@ std::string SearchMatch::searchMatchesToString(const std::vector& m SearchMatch SearchMatch::createCommand(CommandType type) { SearchMatch match; - match.nameHierarchy = NameHierarchy(getCommandName(type)); + match.text = getCommandName(type); match.typeName = "command"; match.searchType = SEARCH_COMMAND; return match; @@ -74,7 +74,7 @@ SearchMatch::SearchMatch() } SearchMatch::SearchMatch(const std::string& query) - : nameHierarchy(query) + : text(query) , typeName("") , searchType(SEARCH_NONE) { @@ -87,7 +87,7 @@ bool SearchMatch::isValid() 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; for (size_t index : indices) { @@ -104,7 +104,7 @@ void SearchMatch::print(std::ostream& ostream) const std::string SearchMatch::getFullName() const { - return nameHierarchy.getQualifiedName(); + return text; } std::string SearchMatch::getNodeTypeAsString() const diff --git a/src/lib/data/search/SearchMatch.h b/src/lib/data/search/SearchMatch.h index aa6f94eb..ca044f37 100644 --- a/src/lib/data/search/SearchMatch.h +++ b/src/lib/data/search/SearchMatch.h @@ -44,16 +44,13 @@ struct SearchMatch std::string getNodeTypeAsString() const; std::string getSearchTypeName() const; - NameHierarchy nameHierarchy; + std::string text; std::string typeName; - Node::NodeType nodeType; SearchType searchType; - - std::set tokenIds; - std::vector indices; - size_t weight; + + std::vector nameHierarchies; }; diff --git a/src/lib/data/search/SearchNode.cpp b/src/lib/data/search/SearchNode.cpp deleted file mode 100644 index 21357d87..00000000 --- a/src/lib/data/search/SearchNode.cpp +++ /dev/null @@ -1,455 +0,0 @@ -#include "data/search/SearchNode.h" - -#include - -#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>& 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(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 SearchNode::getNameIdsRecursive() const -{ - std::deque 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& SearchNode::getTokenIds() const -{ - return m_tokenIds; -} - -bool SearchNode::hasTokenIdsRecursive() const -{ - if (m_tokenIds.size()) - { - return true; - } - - for (const std::pair>& 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::getParentsWithoutTokenId() -{ - std::deque 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>& SearchNode::getChildren() const -{ - return m_nodes; -} - -SearchResults SearchNode::runFuzzySearch(const std::string& query) const -{ - SearchResults result; - - for (const std::pair>& p : m_nodes) - { - FuzzyMap m = p.second->fuzzyMatchRecursive(query, 0, 0, 0); - for (const std::pair& 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 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& 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& 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>& p : node->m_nodes) - { - addResultsRecursive(results, weight, p.second.get(), parent); - } -} - -std::shared_ptr SearchNode::addNodeRecursive( - std::deque* nameIds, const Dictionary& dictionary -){ - Id nameId = nameIds->front(); - nameIds->pop_front(); - - std::shared_ptr node = getChildWithNameId(nameId); - if (!node) - { - node = std::make_shared(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::getNodeRecursive(std::deque* nameIds) const -{ - Id nameId = nameIds->front(); - nameIds->pop_front(); - - std::shared_ptr node = getChildWithNameId(nameId); - if (node) - { - if (!nameIds->size()) - { - return node; - } - - return node->getNodeRecursive(nameIds); - } - - return nullptr; -} - -void SearchNode::removeSearchNode(SearchNode* node) -{ - std::map>::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 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 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 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>& 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 SearchNode::fuzzyMatch( - const std::string query, size_t start, size_t size, std::vector* 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(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(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(pos, weight); -} - -std::shared_ptr SearchNode::getChildWithNameId(Id nameId) const -{ - std::map>::const_iterator it = m_nodes.find(nameId); - - if (it != m_nodes.end()) - { - return it->second; - } - - return nullptr; -} - -std::deque SearchNode::getNodesToParent(const SearchNode* parent) const -{ - std::deque 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> p : node->m_nodes) - { - ostream << p.second.get(); - } - - return ostream; -} diff --git a/src/lib/data/search/SearchNode.h b/src/lib/data/search/SearchNode.h deleted file mode 100644 index 25f37f65..00000000 --- a/src/lib/data/search/SearchNode.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef SEARCH_NODE_H -#define SEARCH_NODE_H - -#include -#include -#include -#include -#include -#include - -#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 getNameIdsRecursive() const; - - Id getFirstTokenId() const; - const std::set& getTokenIds() const; - bool hasTokenIdsRecursive() const; - - void addTokenId(Id tokenId); - void removeTokenId(Id tokenId); - - SearchNode* getParent() const; - std::deque getParentsWithoutTokenId(); - - const std::map>& 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 FuzzyMap; - typedef FuzzyMap::const_iterator FuzzyMapIterator; - - // Accessed by SearchIndex - std::shared_ptr addNodeRecursive(std::deque* nameIds, const Dictionary& dictionary); - std::shared_ptr getNodeRecursive(std::deque* 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 fuzzyMatch( - const std::string query, size_t start, size_t size, std::vector* indices = nullptr) const; - - std::shared_ptr getChildWithNameId(Id nameId) const; - std::deque getNodesToParent(const SearchNode* parent) const; - - std::map> m_nodes; - SearchNode* m_parent; - - std::set 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 diff --git a/src/lib/data/search/SearchResult.cpp b/src/lib/data/search/SearchResult.cpp deleted file mode 100644 index 1e38deec..00000000 --- a/src/lib/data/search/SearchResult.cpp +++ /dev/null @@ -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(); -} diff --git a/src/lib/data/search/SearchResult.h b/src/lib/data/search/SearchResult.h deleted file mode 100644 index f550fd5f..00000000 --- a/src/lib/data/search/SearchResult.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef SEARCH_RESULT_H -#define SEARCH_RESULT_H - -#include -#include - -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 SearchResults; -typedef SearchResults::const_iterator SearchResultsIterator; - -#endif // SEARCH_RESULT_H diff --git a/src/lib/utility/utility.h b/src/lib/utility/utility.h index a1cfc7ee..10cbbdd9 100644 --- a/src/lib/utility/utility.h +++ b/src/lib/utility/utility.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "boost/date_time/posix_time/posix_time.hpp" @@ -30,6 +31,9 @@ namespace utility template void append(std::set& a, const std::set& b); + template + void append(std::unordered_set& a, const std::unordered_set& b); + template std::vector toVector(const std::deque& d); @@ -76,6 +80,12 @@ void utility::append(std::set& a, const std::set& b) a.insert(b.begin(), b.end()); } +template +void utility::append(std::unordered_set& a, const std::unordered_set& b) +{ + a.insert(b.begin(), b.end()); +} + template std::vector utility::toVector(const std::deque& d) { diff --git a/src/lib_gui/qt/element/QtSmartSearchBox.cpp b/src/lib_gui/qt/element/QtSmartSearchBox.cpp index d87688de..02946ce1 100644 --- a/src/lib_gui/qt/element/QtSmartSearchBox.cpp +++ b/src/lib_gui/qt/element/QtSmartSearchBox.cpp @@ -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()) { @@ -415,7 +415,7 @@ void QtSmartSearchBox::onTextEdited(const QString& text) layoutElements(); } - if (match.nameHierarchy.size() || m_elements.size()) + if (match.text.size() || m_elements.size()) { requestAutoCompletions(); } @@ -446,7 +446,7 @@ void QtSmartSearchBox::onAutocompletionActivated(const SearchMatch& match) { addMatchAndUpdate(match); - if (match.nameHierarchy.size()) + if (match.text.size()) { search(); } @@ -516,7 +516,7 @@ void QtSmartSearchBox::moveCursorTo(int target) void QtSmartSearchBox::addMatch(const SearchMatch& match) { - if (!match.nameHierarchy.size()) + if (!match.text.size()) { return; } @@ -543,7 +543,7 @@ void QtSmartSearchBox::addMatch(const SearchMatch& match) void QtSmartSearchBox::addMatchAndUpdate(const SearchMatch& match) { - if (match.nameHierarchy.size()) + if (match.text.size()) { m_oldText.clear(); clearLineEdit(); diff --git a/src/test/SearchIndexTestSuite.h b/src/test/SearchIndexTestSuite.h index 7f5d5e0e..5b423c4d 100644 --- a/src/test/SearchIndexTestSuite.h +++ b/src/test/SearchIndexTestSuite.h @@ -3,302 +3,95 @@ #include "utility/utilityString.h" #include "data/search/SearchIndex.h" -#include "data/search/SearchMatch.h" class SearchIndexTestSuite : public CxxTest::TestSuite { public: - void test_add_node() + + void test_search_index_finds_id_of_element_added() { SearchIndex index; - SearchNode* node = index.addNode(createNameHierarchy("util")); + index.addNode(1, NameHierarchy::deserialize("foo\tvoid\r() const")); + index.finishSetup(); + std::vector results = index.search("oo", 0); - TS_ASSERT(node); - TS_ASSERT_EQUALS("util", node->getName()); - TS_ASSERT_EQUALS("util", node->getFullName()); - - TS_ASSERT(node->getNameId()); - TS_ASSERT(!node->getFirstTokenId()); - TS_ASSERT(node->getParent()); - TS_ASSERT(!node->getParent()->getNameId()); + TS_ASSERT_EQUALS(1, results.size()); + TS_ASSERT_EQUALS(1, results[0].elementIds.size()); + TS_ASSERT_DIFFERS(results[0].elementIds.end(), results[0].elementIds.find(1)); } - void test_get_node() + void test_search_index_finds_correct_indices_for_query() { SearchIndex index; - index.addNode(createNameHierarchy("util")); - SearchNode* node = index.getNode("util"); + index.addNode(1, NameHierarchy::deserialize("foo\tvoid\r() const")); + index.finishSetup(); + std::vector results = index.search("oo", 0); - TS_ASSERT(node); - TS_ASSERT_EQUALS("util", node->getName()); - TS_ASSERT_EQUALS("util", node->getFullName()); - - TS_ASSERT(node->getNameId()); - TS_ASSERT(!node->getFirstTokenId()); - TS_ASSERT(node->getParent()); - TS_ASSERT(!node->getParent()->getNameId()); - - node = index.getNode("math"); - TS_ASSERT(!node); + TS_ASSERT_EQUALS(1, results.size()); + TS_ASSERT_EQUALS(2, results[0].indices.size()); + TS_ASSERT_EQUALS(1, results[0].indices[0]); + TS_ASSERT_EQUALS(2, results[0].indices[1]); } - void test_add_hierarchy_node() + void test_search_index_finds_ids_for_ambiguous_query() { 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 results = index.search("fo", 0); - TS_ASSERT(node); - TS_ASSERT_EQUALS("pow", node->getName()); - TS_ASSERT_EQUALS("util::math::pow", node->getFullName()); - - TS_ASSERT(node->getNameId()); - 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()); + TS_ASSERT_EQUALS(2, results.size()); + TS_ASSERT_EQUALS(1, results[0].elementIds.size()); + TS_ASSERT_DIFFERS(results[0].elementIds.end(), results[0].elementIds.find(1)); + TS_ASSERT_EQUALS(1, results[1].elementIds.size()); + TS_ASSERT_DIFFERS(results[1].elementIds.end(), results[1].elementIds.find(2)); } - void test_reuse_hierarchy_node() + void test_search_index_does_not_find_anything_after_clear() { SearchIndex index; - SearchNode* node1 = index.addNode(createNameHierarchy("math::pow")); - SearchNode* node2 = index.addNode(createNameHierarchy("math::floor")); - - 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.addNode(1, NameHierarchy::deserialize("foo\tvoid\r() const")); + index.finishSetup(); index.clear(); + std::vector results = index.search("oo", 0); - TS_ASSERT(!index.getNode("math")); - TS_ASSERT(!index.getNode("string")); + TS_ASSERT_EQUALS(0, results.size()); } - void test_fuzzy_matching() + void test_search_index_does_not_find_all_results_when_max_amount_is_limited() { SearchIndex index; - index.addNode(createNameHierarchy("util")); - index.addNode(createNameHierarchy("math")); - index.addNode(createNameHierarchy("string")); + index.addNode(1, NameHierarchy::deserialize("foo1\tvoid\r() const")); + index.addNode(2, NameHierarchy::deserialize("foo2\tvoid\r() const")); + index.finishSetup(); + std::vector results = index.search("oo", 1); - std::vector matches = index.runFuzzySearchAndGetMatches("u"); - - 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); + TS_ASSERT_EQUALS(1, results.size()); } - void test_fuzzy_matching_is_case_insensitive() + void test_search_index_query_is_case_insensitive() { SearchIndex index; - index.addNode(createNameHierarchy("util")); - index.addNode(createNameHierarchy("MATH")); + index.addNode(1, NameHierarchy::deserialize("foo1\tvoid\r() const")); + index.addNode(2, NameHierarchy::deserialize("FOO2\tvoid\r() const")); + index.finishSetup(); + std::vector results = index.search("oo", 0); - std::vector matches = index.runFuzzySearchAndGetMatches("t"); - - 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()); + TS_ASSERT_EQUALS(2, results.size()); } - void test_fuzzy_matching_wheighs_by_distance_and_alphabet() + void test_search_index_rates_higher_on_consecutive_letters() { + SearchIndex index; - index.addNode(createNameHierarchy("util")); - index.addNode(createNameHierarchy("math")); - index.addNode(createNameHierarchy("string")); + index.addNode(1, NameHierarchy::deserialize("oaabbcc\tvoid\r() const")); + index.addNode(2, NameHierarchy::deserialize("ocbcabc\tvoid\r() const")); + index.finishSetup(); + std::vector results = index.search("abc", 0); - std::vector matches = index.runFuzzySearchAndGetMatches("t"); - - TS_ASSERT_EQUALS(3, matches.size()); - 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 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 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 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 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 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(element)); - } - return nameHierarchy; + TS_ASSERT_EQUALS(2, results.size()); + TS_ASSERT_EQUALS("ocbcabc", results[0].text); + TS_ASSERT_EQUALS("oaabbcc", results[1].text); } }; diff --git a/src/test/StorageTestSuite.h b/src/test/StorageTestSuite.h index 6b18b046..bcbec7e5 100644 --- a/src/test/StorageTestSuite.h +++ b/src/test/StorageTestSuite.h @@ -97,7 +97,6 @@ public: TS_ASSERT_EQUALS(storage.getNodeCount(), 3); TS_ASSERT_EQUALS(storage.getEdgeCount(), 2); TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4); - TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 3); std::set files; files.insert(FilePath(m_filePath)); @@ -105,8 +104,7 @@ public: TS_ASSERT_EQUALS(storage.getNodeCount(), 0); TS_ASSERT_EQUALS(storage.getEdgeCount(), 0); - TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0); - TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 0);*/ + TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 0);*/ } 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.getEdgeCount(), 5); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); - //TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 6); //std::set files; //files.insert(FilePath("file.cpp")); @@ -136,8 +133,7 @@ public: //TS_ASSERT_EQUALS(storage.getNodeCount(), 3); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 2); - //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4); - //TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 3); + //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4);*/ } 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.getEdgeCount(), 5); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); - //TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 5); //std::set files; //files.insert(FilePath("file.h")); @@ -168,7 +163,6 @@ public: //TS_ASSERT_EQUALS(storage.getNodeCount(), 4); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 3); //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() @@ -190,7 +184,6 @@ public: //TS_ASSERT_EQUALS(storage.getNodeCount(), 5); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 5); //TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); - //TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 5); //std::set filePaths; //filePaths.insert(FilePath("file.cpp")); @@ -200,7 +193,6 @@ public: //TS_ASSERT_EQUALS(storage.getNodeCount(), 0); //TS_ASSERT_EQUALS(storage.getEdgeCount(), 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() @@ -248,11 +240,6 @@ private: return getTokenLocationsForTokenIds(tokenIds); } - //const std::string& getWord(Id wordId) const - //{ - // return getSearchIndex().getWord(wordId); - //} - //const size_t getNodeCount() const //{ // return getGraph().getNodeCount(); @@ -263,11 +250,6 @@ private: // return getGraph().getEdgeCount(); //} - const SearchIndex& searchIndex() const - { - return getSearchIndex(); - } - Id getEdgeId(Edge::EdgeType type, const NameHierarchy& fromName, const NameHierarchy& toName) const { NameHierarchy from;