diff --git a/include/_mgp.hpp b/include/_mgp.hpp index 61c630751..1d9916554 100644 --- a/include/_mgp.hpp +++ b/include/_mgp.hpp @@ -330,9 +330,9 @@ inline bool graph_has_text_index(mgp_graph *graph, const char *index_name) { return MgInvoke(mgp_graph_has_text_index, graph, index_name); } -// TODO antepusic change result type -inline bool graph_search_text_index(mgp_graph *graph, const char *index_name, const char *search_string) { - return MgInvoke(mgp_graph_has_text_index, graph, index_name, search_string); +inline mgp_list *graph_search_text_index(mgp_graph *graph, mgp_memory *memory, const char *index_name, + const char *search_query) { + return MgInvoke(graph_search_text_index, graph, memory, index_name, search_query); } inline mgp_vertices_iterator *graph_iter_vertices(mgp_graph *g, mgp_memory *memory) { diff --git a/include/mg_procedure.h b/include/mg_procedure.h index 723d55ffc..5bfa4d7cd 100644 --- a/include/mg_procedure.h +++ b/include/mg_procedure.h @@ -891,11 +891,15 @@ enum mgp_error mgp_edge_iter_properties(struct mgp_edge *e, struct mgp_memory *m enum mgp_error mgp_graph_get_vertex_by_id(struct mgp_graph *g, struct mgp_vertex_id id, struct mgp_memory *memory, struct mgp_vertex **result); -enum mgp_error mgp_graph_has_text_index(mgp_graph *graph, const char *label, int *result); +/// Result is non-zero if the index with the given name exists. +/// Current implementation always returns without errors. +enum mgp_error mgp_graph_has_text_index(mgp_graph *graph, const char *index_name, int *result); -// TODO antepusic change result type -enum mgp_error mgp_graph_search_text_index(mgp_graph *graph, const char *index_name, const char *search_string, - int *result); +/// Search the named text index for the given query. The result is a list of the vertices whose text properties match +/// the given query. +/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate search result vertices. +enum mgp_error mgp_graph_search_text_index(mgp_graph *graph, mgp_memory *memory, const char *index_name, + const char *search_query, struct mgp_list **result); /// Creates label index for given label. /// mgp_error::MGP_ERROR_NO_ERROR is always returned. diff --git a/query_modules/text_search.cpp b/query_modules/text_search.cpp index e29bb5f4d..dd0d2b1d8 100644 --- a/query_modules/text_search.cpp +++ b/query_modules/text_search.cpp @@ -17,34 +17,30 @@ namespace TextSearch { constexpr std::string_view kProcedureSearch = "search"; constexpr std::string_view kParameterLabel = "label"; -constexpr std::string_view kParameterSearchString = "search_string"; +constexpr std::string_view kParameterSearchString = "search_query"; constexpr std::string_view kReturnNode = "node"; void Search(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory); } // namespace TextSearch void Search(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) { - // CALL text_search.search("Label", "someQuery", searchFields, returnFields) RETURN node, score - mgp::MemoryDispatcherGuard guard{memory}; const auto record_factory = mgp::RecordFactory(result); auto arguments = mgp::List(args); auto label = arguments[0].ValueString(); - auto search_string = arguments[1].ValueString(); + auto search_query = arguments[1].ValueString(); // 1. See if the given label is text-indexed if (!mgp::graph_has_text_index(memgraph_graph, label.data())) { return; } - // 2. Run text search of that index - mgp::graph_search_text_index(memgraph_graph, label.data(), search_string); - - // text_index.search(label, search_string); - - // 3. Get the graph elements from their IDs in the search results - - // 4. Return records (one per element) + // 2. Run a text search of that index and return the search results + for (const auto &node : + mgp::List(mgp::graph_search_text_index(memgraph_graph, memory, label.data(), search_query.data()))) { + auto record = record_factory.NewRecord(); + record.Insert(TextSearch::kReturnNode.data(), node); + } } extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) { diff --git a/src/query/db_accessor.hpp b/src/query/db_accessor.hpp index c9416138f..b2e6fa826 100644 --- a/src/query/db_accessor.hpp +++ b/src/query/db_accessor.hpp @@ -555,8 +555,8 @@ class DbAccessor final { bool TextIndexExists(std::string index_name) const { return accessor_->TextIndexExists(index_name); } - mgcxx_mock::text_search::SearchOutput SearchTextIndex(std::string index_name, std::string search_string) const { - return accessor_->SearchTextIndex(index_name, search_string); + std::vector SearchTextIndex(std::string index_name, std::string search_query) const { + return accessor_->SearchTextIndex(index_name, search_query); } std::optional GetIndexStats(const storage::LabelId &label) const { diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp index 075329578..441e1597e 100644 --- a/src/query/procedure/mg_procedure_impl.cpp +++ b/src/query/procedure/mg_procedure_impl.cpp @@ -3333,9 +3333,56 @@ mgp_error mgp_graph_has_text_index(mgp_graph *graph, const char *index_name, int }); } -mgp_error mgp_graph_search_text_index(mgp_graph *graph, const char *index_name, const char *search_string, - int *result) { - return WrapExceptions([graph, index_name, result]() { *result = 1; }); +mgp_vertex *GetVertexByGid(mgp_graph *graph, memgraph::storage::Gid id, mgp_memory *memory) { + std::optional maybe_vertex = + std::visit([graph, id](auto *impl) { return impl->FindVertex(id, graph->view); }, graph->impl); + if (maybe_vertex) { + return std::visit(memgraph::utils::Overloaded{ + [memory, graph, maybe_vertex](memgraph::query::DbAccessor *) { + return NewRawMgpObject(memory, *maybe_vertex, graph); + }, + [memory, graph, maybe_vertex](memgraph::query::SubgraphDbAccessor *impl) { + return NewRawMgpObject( + memory, memgraph::query::SubgraphVertexAccessor(*maybe_vertex, impl->getGraph()), + graph); + }}, + graph->impl); + } + return nullptr; +} + +void WrapIntoVertexList(std::vector vertex_ids, mgp_graph *graph, mgp_memory *memory, + mgp_list **result) { + if (const auto err = mgp_list_make_empty(vertex_ids.size(), memory, result); err != mgp_error::MGP_ERROR_NO_ERROR) { + throw std::logic_error("Retrieving text search results failed during creation of a mgp_vertex"); + } + + for (const auto &vertex_id : vertex_ids) { + mgp_value *vertex; + if (const auto err = mgp_value_make_vertex(GetVertexByGid(graph, vertex_id, memory), &vertex); + err != mgp_error::MGP_ERROR_NO_ERROR) { + throw std::logic_error("Retrieving text search results failed during creation of a vertex mgp_value"); + } + if (const auto err_list = mgp_list_append(*result, vertex); err_list != mgp_error::MGP_ERROR_NO_ERROR) { + throw std::logic_error( + "Retrieving text search results failed during insertion of the mgp_value into the result list"); + } + } +} + +mgp_error mgp_graph_search_text_index(mgp_graph *graph, mgp_memory *memory, const char *index_name, + const char *search_query, mgp_list **result) { + return WrapExceptions([graph, memory, index_name, search_query, result]() { + std::visit(memgraph::utils::Overloaded{ + [&](memgraph::query::DbAccessor *impl) { + WrapIntoVertexList(impl->SearchTextIndex(index_name, search_query), graph, memory, result); + }, + [&](memgraph::query::SubgraphDbAccessor *impl) { + WrapIntoVertexList(impl->GetAccessor()->SearchTextIndex(index_name, search_query), graph, memory, + result); + }}, + graph->impl); + }); } #ifdef MG_ENTERPRISE diff --git a/src/storage/v2/indices/text_index.hpp b/src/storage/v2/indices/text_index.hpp index 801b67290..d96abc75c 100644 --- a/src/storage/v2/indices/text_index.hpp +++ b/src/storage/v2/indices/text_index.hpp @@ -57,10 +57,18 @@ class TextIndex { bool IndexExists(std::string index_name) { return index_.contains(index_name); } - mgcxx_mock::text_search::SearchOutput Search(std::string index_name, std::string search_string) { - // TODO antepusic: Add metadata to the return fields before search - auto input = mgcxx_mock::text_search::SearchInput{}; - return mgcxx_mock::text_search::Mock::search(index_.at(index_name), input); + std::vector Search(std::string index_name, std::string search_query) { + auto input = mgcxx_mock::text_search::SearchInput{.search_query = search_query, .return_fields = {"metadata.gid"}}; + // Basic check for search fields in the query (Tantivy syntax delimits them with `:` to the right) + if (search_query.find(":") == std::string::npos) { + input.search_fields = {"data"}; + } + + std::vector found_nodes; + for (const auto &doc : mgcxx_mock::text_search::Mock::search(index_.at(index_name), input).docs) { + found_nodes.push_back(storage::Gid::FromString(doc.data)); + } + return found_nodes; } std::vector ListIndices() { diff --git a/src/storage/v2/mgcxx_mock.hpp b/src/storage/v2/mgcxx_mock.hpp index 784219c64..4b7ef05b1 100644 --- a/src/storage/v2/mgcxx_mock.hpp +++ b/src/storage/v2/mgcxx_mock.hpp @@ -43,7 +43,9 @@ struct SearchOutput { std::vector docs; }; -// NOTE: The function names don't follow the style guide in order to be uniform with the mgcxx API +// NOTE: +// * The function names don't follow the style guide in order to be uniform with the mgcxx API +// * All methods are static in order to avoid having to make a Mock object that's globally available class Mock { public: static void init(std::string _log_level) {} @@ -59,7 +61,7 @@ class Mock { static void rollback(IndexContext context) {} static SearchOutput search(IndexContext context, SearchInput input) { - return SearchOutput{.docs = {DocumentOutput{.data = ""}}}; + return SearchOutput{.docs = {DocumentOutput{.data = "0"}}}; } static DocumentOutput aggregate(IndexContext context, SearchInput input) { return DocumentOutput(); } diff --git a/src/storage/v2/storage.hpp b/src/storage/v2/storage.hpp index 5cd8dd2ea..3aa957aaf 100644 --- a/src/storage/v2/storage.hpp +++ b/src/storage/v2/storage.hpp @@ -216,8 +216,8 @@ class Storage { return storage_->indices_.text_index_->IndexExists(index_name); } - mgcxx_mock::text_search::SearchOutput SearchTextIndex(std::string index_name, std::string search_string) const { - return storage_->indices_.text_index_->Search(index_name, search_string); + std::vector SearchTextIndex(std::string index_name, std::string search_query) const { + return storage_->indices_.text_index_->Search(index_name, search_query); } virtual IndicesInfo ListAllIndices() const = 0; @@ -256,8 +256,8 @@ class Storage { std::vector ListAllPossiblyPresentEdgeTypes() const; - mgcxx_mock::text_search::SearchOutput TextSearch(std::string index_name, std::string &search_string) const { - return storage_->indices_.text_index_->Search(index_name, search_string); + std::vector TextSearch(std::string index_name, std::string &search_query) const { + return storage_->indices_.text_index_->Search(index_name, search_query); } virtual utils::BasicResult CreateIndex(LabelId label) = 0;