From 47761bcc950c8e492fe1c546a0414269ff7680da Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Sun, 8 Jun 2025 20:04:40 +0200 Subject: [PATCH 1/8] Add text replace apoc functions --- cpp/memgraph | 2 +- cpp/text_module/algorithm/text.cpp | 44 +++++++++++++++++++ cpp/text_module/algorithm/text.hpp | 24 +++++++--- cpp/text_module/text_module.cpp | 12 +++++ e2e/text_test/test_regreplace_basic/input.cyp | 0 e2e/text_test/test_regreplace_basic/test.yml | 5 +++ e2e/text_test/test_regreplace_empty/input.cyp | 0 e2e/text_test/test_regreplace_empty/test.yml | 5 +++ .../test_regreplace_multiple/input.cyp | 0 .../test_regreplace_multiple/test.yml | 5 +++ e2e/text_test/test_replace_basic/input.cyp | 0 e2e/text_test/test_replace_basic/test.yml | 5 +++ e2e/text_test/test_replace_empty/input.cyp | 0 e2e/text_test/test_replace_empty/test.yml | 5 +++ e2e/text_test/test_replace_multiple/input.cyp | 0 e2e/text_test/test_replace_multiple/test.yml | 5 +++ 16 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 e2e/text_test/test_regreplace_basic/input.cyp create mode 100644 e2e/text_test/test_regreplace_basic/test.yml create mode 100644 e2e/text_test/test_regreplace_empty/input.cyp create mode 100644 e2e/text_test/test_regreplace_empty/test.yml create mode 100644 e2e/text_test/test_regreplace_multiple/input.cyp create mode 100644 e2e/text_test/test_regreplace_multiple/test.yml create mode 100644 e2e/text_test/test_replace_basic/input.cyp create mode 100644 e2e/text_test/test_replace_basic/test.yml create mode 100644 e2e/text_test/test_replace_empty/input.cyp create mode 100644 e2e/text_test/test_replace_empty/test.yml create mode 100644 e2e/text_test/test_replace_multiple/input.cyp create mode 100644 e2e/text_test/test_replace_multiple/test.yml diff --git a/cpp/memgraph b/cpp/memgraph index 7749ad7cd..27614c8a5 160000 --- a/cpp/memgraph +++ b/cpp/memgraph @@ -1 +1 @@ -Subproject commit 7749ad7cddfb595928379d392a1efa39f8113a63 +Subproject commit 27614c8a5deaaadbe3af94bc57dce058cccc14ee diff --git a/cpp/text_module/algorithm/text.cpp b/cpp/text_module/algorithm/text.cpp index 5891bc0b7..c39b23f9a 100644 --- a/cpp/text_module/algorithm/text.cpp +++ b/cpp/text_module/algorithm/text.cpp @@ -101,3 +101,47 @@ void Text::RegexGroups(mgp_list *args, mgp_graph * /*memgraph_graph*/, mgp_resul record_factory.SetErrorMessage(e.what()); } } + +void Text::Replace(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_result *result, mgp_memory *memory) { + mgp::MemoryDispatcherGuard guard{memory}; + const auto arguments = mgp::List(args); + mgp::Result result_obj(result); + + try { + const auto text = std::string(arguments[0].ValueString()); + const auto regex = std::string(arguments[1].ValueString()); + const auto replacement = std::string(arguments[2].ValueString()); + + std::string result_str = text; + size_t pos = 0; + while ((pos = result_str.find(regex, pos)) != std::string::npos) { + result_str.replace(pos, regex.length(), replacement); + pos += replacement.length(); + } + + result_obj.SetValue(result_str); + } catch (const std::exception &e) { + result_obj.SetErrorMessage(e.what()); + return; + } +} + +void Text::RegReplace(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_result *result, mgp_memory *memory) { + mgp::MemoryDispatcherGuard guard{memory}; + const auto arguments = mgp::List(args); + mgp::Result result_obj(result); + + try { + const auto text = std::string(arguments[0].ValueString()); + const auto regex = std::string(arguments[1].ValueString()); + const auto replacement = std::string(arguments[2].ValueString()); + + std::regex pattern(regex); + std::string result_str = std::regex_replace(text, pattern, replacement); + + result_obj.SetValue(result_str); + } catch (const std::exception &e) { + result_obj.SetErrorMessage(e.what()); + return; + } +} diff --git a/cpp/text_module/algorithm/text.hpp b/cpp/text_module/algorithm/text.hpp index 3c2f5814f..bb9f1605f 100644 --- a/cpp/text_module/algorithm/text.hpp +++ b/cpp/text_module/algorithm/text.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include namespace Text { @@ -8,19 +11,30 @@ namespace Text { constexpr std::string_view kProcedureJoin = "join"; constexpr std::string_view kJoinArg1 = "strings"; constexpr std::string_view kJoinArg2 = "delimiter"; -constexpr std::string_view kResultJoin = "string"; +constexpr std::string_view kResultJoin = "join"; /* format constants */ constexpr std::string_view kProcedureFormat = "format"; -constexpr std::string_view kStringToFormat = "text"; +constexpr std::string_view kStringToFormat = "format"; constexpr std::string_view kParameters = "params"; -constexpr std::string_view kResultFormat = "result"; +constexpr std::string_view kResultFormat = "format"; /* regex constants */ -constexpr std::string_view kProcedureRegexGroups = "regexGroups"; +constexpr std::string_view kProcedureRegexGroups = "regex_groups"; constexpr std::string_view kInput = "input"; constexpr std::string_view kRegex = "regex"; -constexpr std::string_view kResultRegexGroups = "results"; +constexpr std::string_view kResultRegexGroups = "groups"; +/* replace constants */ +constexpr std::string_view kProcedureReplace = "replace"; +constexpr std::string_view kText = "text"; +constexpr std::string_view kRegexReplace = "search"; +constexpr std::string_view kReplacement = "replacement"; +constexpr std::string_view kResultReplace = "result"; +/* regreplace constants */ +constexpr std::string_view kProcedureRegReplace = "regreplace"; +constexpr std::string_view kResultRegReplace = "result"; void Join(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory); void Format(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory); void RegexGroups(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory); +void Replace(mgp_list *args, mgp_func_context *ctx, mgp_func_result *result, mgp_memory *memory); +void RegReplace(mgp_list *args, mgp_func_context *ctx, mgp_func_result *result, mgp_memory *memory); } // namespace Text diff --git a/cpp/text_module/text_module.cpp b/cpp/text_module/text_module.cpp index 84958a967..c9490b4df 100644 --- a/cpp/text_module/text_module.cpp +++ b/cpp/text_module/text_module.cpp @@ -20,6 +20,18 @@ extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *mem {mgp::Parameter(Text::kInput, mgp::Type::String), mgp::Parameter(Text::kRegex, mgp::Type::String)}, {mgp::Return(Text::kResultRegexGroups, {mgp::Type::List, mgp::Type::Any})}, module, memory); + + mgp::AddFunction(Text::Replace, Text::kProcedureReplace, + {mgp::Parameter(Text::kText, mgp::Type::String), + mgp::Parameter(Text::kRegexReplace, mgp::Type::String), + mgp::Parameter(Text::kReplacement, mgp::Type::String)}, + module, memory); + + mgp::AddFunction(Text::RegReplace, Text::kProcedureRegReplace, + {mgp::Parameter(Text::kText, mgp::Type::String), + mgp::Parameter(Text::kRegex, mgp::Type::String), + mgp::Parameter(Text::kReplacement, mgp::Type::String)}, + module, memory); } catch (const std::exception &e) { return 1; } diff --git a/e2e/text_test/test_regreplace_basic/input.cyp b/e2e/text_test/test_regreplace_basic/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_regreplace_basic/test.yml b/e2e/text_test/test_regreplace_basic/test.yml new file mode 100644 index 000000000..0fc05b438 --- /dev/null +++ b/e2e/text_test/test_regreplace_basic/test.yml @@ -0,0 +1,5 @@ +query: > + RETURN text.regreplace("Hello World!", "W[a-z]+", "Memgraph") AS output; + +output: + - output: "Hello Memgraph!" \ No newline at end of file diff --git a/e2e/text_test/test_regreplace_empty/input.cyp b/e2e/text_test/test_regreplace_empty/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_regreplace_empty/test.yml b/e2e/text_test/test_regreplace_empty/test.yml new file mode 100644 index 000000000..48d86e2a7 --- /dev/null +++ b/e2e/text_test/test_regreplace_empty/test.yml @@ -0,0 +1,5 @@ +query: > + RETURN text.regreplace("Hello World!", "", "Memgraph") AS output; + +output: + - output: "Hello World!" \ No newline at end of file diff --git a/e2e/text_test/test_regreplace_multiple/input.cyp b/e2e/text_test/test_regreplace_multiple/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_regreplace_multiple/test.yml b/e2e/text_test/test_regreplace_multiple/test.yml new file mode 100644 index 000000000..13791a333 --- /dev/null +++ b/e2e/text_test/test_regreplace_multiple/test.yml @@ -0,0 +1,5 @@ +query: > + RETURN text.regreplace("Hello World! Hello World!", "W[a-z]+", "Memgraph") AS output; + +output: + - output: "Hello Memgraph! Hello Memgraph!" \ No newline at end of file diff --git a/e2e/text_test/test_replace_basic/input.cyp b/e2e/text_test/test_replace_basic/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_replace_basic/test.yml b/e2e/text_test/test_replace_basic/test.yml new file mode 100644 index 000000000..b3481dc59 --- /dev/null +++ b/e2e/text_test/test_replace_basic/test.yml @@ -0,0 +1,5 @@ +query: > + RETURN text.replace("Hello World!", "World", "Memgraph") AS output; + +output: + - output: "Hello Memgraph!" \ No newline at end of file diff --git a/e2e/text_test/test_replace_empty/input.cyp b/e2e/text_test/test_replace_empty/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_replace_empty/test.yml b/e2e/text_test/test_replace_empty/test.yml new file mode 100644 index 000000000..73ef25972 --- /dev/null +++ b/e2e/text_test/test_replace_empty/test.yml @@ -0,0 +1,5 @@ +query: > + RETURN text.replace("Hello World!", "", "Memgraph") AS output; + +output: + - output: "Hello World!" \ No newline at end of file diff --git a/e2e/text_test/test_replace_multiple/input.cyp b/e2e/text_test/test_replace_multiple/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_replace_multiple/test.yml b/e2e/text_test/test_replace_multiple/test.yml new file mode 100644 index 000000000..2128dea00 --- /dev/null +++ b/e2e/text_test/test_replace_multiple/test.yml @@ -0,0 +1,5 @@ +query: > + RETURN text.replace("Hello World! Hello World!", "World", "Memgraph") AS output; + +output: + - output: "Hello Memgraph! Hello Memgraph!" \ No newline at end of file From b533fd730852d533822e30719e051150a4c13049 Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Sun, 8 Jun 2025 20:06:17 +0200 Subject: [PATCH 2/8] Fix empty lines --- e2e/text_test/test_regreplace_basic/test.yml | 2 +- e2e/text_test/test_regreplace_empty/test.yml | 2 +- e2e/text_test/test_regreplace_multiple/test.yml | 2 +- e2e/text_test/test_replace_basic/test.yml | 2 +- e2e/text_test/test_replace_empty/test.yml | 2 +- e2e/text_test/test_replace_multiple/test.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/e2e/text_test/test_regreplace_basic/test.yml b/e2e/text_test/test_regreplace_basic/test.yml index 0fc05b438..d7dcdb761 100644 --- a/e2e/text_test/test_regreplace_basic/test.yml +++ b/e2e/text_test/test_regreplace_basic/test.yml @@ -2,4 +2,4 @@ query: > RETURN text.regreplace("Hello World!", "W[a-z]+", "Memgraph") AS output; output: - - output: "Hello Memgraph!" \ No newline at end of file + - output: "Hello Memgraph!" diff --git a/e2e/text_test/test_regreplace_empty/test.yml b/e2e/text_test/test_regreplace_empty/test.yml index 48d86e2a7..158c07dcc 100644 --- a/e2e/text_test/test_regreplace_empty/test.yml +++ b/e2e/text_test/test_regreplace_empty/test.yml @@ -2,4 +2,4 @@ query: > RETURN text.regreplace("Hello World!", "", "Memgraph") AS output; output: - - output: "Hello World!" \ No newline at end of file + - output: "Hello World!" diff --git a/e2e/text_test/test_regreplace_multiple/test.yml b/e2e/text_test/test_regreplace_multiple/test.yml index 13791a333..57e9cec05 100644 --- a/e2e/text_test/test_regreplace_multiple/test.yml +++ b/e2e/text_test/test_regreplace_multiple/test.yml @@ -2,4 +2,4 @@ query: > RETURN text.regreplace("Hello World! Hello World!", "W[a-z]+", "Memgraph") AS output; output: - - output: "Hello Memgraph! Hello Memgraph!" \ No newline at end of file + - output: "Hello Memgraph! Hello Memgraph!" diff --git a/e2e/text_test/test_replace_basic/test.yml b/e2e/text_test/test_replace_basic/test.yml index b3481dc59..e201f7756 100644 --- a/e2e/text_test/test_replace_basic/test.yml +++ b/e2e/text_test/test_replace_basic/test.yml @@ -2,4 +2,4 @@ query: > RETURN text.replace("Hello World!", "World", "Memgraph") AS output; output: - - output: "Hello Memgraph!" \ No newline at end of file + - output: "Hello Memgraph!" diff --git a/e2e/text_test/test_replace_empty/test.yml b/e2e/text_test/test_replace_empty/test.yml index 73ef25972..29fe5d4c0 100644 --- a/e2e/text_test/test_replace_empty/test.yml +++ b/e2e/text_test/test_replace_empty/test.yml @@ -2,4 +2,4 @@ query: > RETURN text.replace("Hello World!", "", "Memgraph") AS output; output: - - output: "Hello World!" \ No newline at end of file + - output: "Hello World!" diff --git a/e2e/text_test/test_replace_multiple/test.yml b/e2e/text_test/test_replace_multiple/test.yml index 2128dea00..efd838641 100644 --- a/e2e/text_test/test_replace_multiple/test.yml +++ b/e2e/text_test/test_replace_multiple/test.yml @@ -2,4 +2,4 @@ query: > RETURN text.replace("Hello World! Hello World!", "World", "Memgraph") AS output; output: - - output: "Hello Memgraph! Hello Memgraph!" \ No newline at end of file + - output: "Hello Memgraph! Hello Memgraph!" From 307dc97860a593ff4cb408a495865babef54799b Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Sun, 8 Jun 2025 20:15:27 +0200 Subject: [PATCH 3/8] Add text.distance function --- cpp/text_module/algorithm/text.cpp | 38 +++++++++++++++++++ cpp/text_module/algorithm/text.hpp | 5 +++ cpp/text_module/text_module.cpp | 5 +++ e2e/text_test/test_distance_basic/input.cyp | 0 e2e/text_test/test_distance_basic/test.yml | 4 ++ .../test_distance_different/input.cyp | 0 .../test_distance_different/test.yml | 4 ++ e2e/text_test/test_distance_empty/input.cyp | 0 e2e/text_test/test_distance_empty/test.yml | 4 ++ 9 files changed, 60 insertions(+) create mode 100644 e2e/text_test/test_distance_basic/input.cyp create mode 100644 e2e/text_test/test_distance_basic/test.yml create mode 100644 e2e/text_test/test_distance_different/input.cyp create mode 100644 e2e/text_test/test_distance_different/test.yml create mode 100644 e2e/text_test/test_distance_empty/input.cyp create mode 100644 e2e/text_test/test_distance_empty/test.yml diff --git a/cpp/text_module/algorithm/text.cpp b/cpp/text_module/algorithm/text.cpp index c39b23f9a..92707d985 100644 --- a/cpp/text_module/algorithm/text.cpp +++ b/cpp/text_module/algorithm/text.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -145,3 +146,40 @@ void Text::RegReplace(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_resul return; } } + +void Text::Distance(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_result *result, mgp_memory *memory) { + mgp::MemoryDispatcherGuard guard{memory}; + const auto arguments = mgp::List(args); + mgp::Result result_obj(result); + + try { + const auto text1 = std::string(arguments[0].ValueString()); + const auto text2 = std::string(arguments[1].ValueString()); + + const size_t m = text1.length(); + const size_t n = text2.length(); + std::vector> dp(m + 1, std::vector(n + 1)); + + for (size_t i = 0; i <= m; i++) { + dp[i][0] = i; + } + for (size_t j = 0; j <= n; j++) { + dp[0][j] = j; + } + + for (size_t i = 1; i <= m; i++) { + for (size_t j = 1; j <= n; j++) { + if (text1[i - 1] == text2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + std::min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}); + } + } + } + + result_obj.SetValue(static_cast(dp[m][n])); + } catch (const std::exception &e) { + result_obj.SetErrorMessage(e.what()); + return; + } +} diff --git a/cpp/text_module/algorithm/text.hpp b/cpp/text_module/algorithm/text.hpp index bb9f1605f..581cc2135 100644 --- a/cpp/text_module/algorithm/text.hpp +++ b/cpp/text_module/algorithm/text.hpp @@ -31,10 +31,15 @@ constexpr std::string_view kResultReplace = "result"; /* regreplace constants */ constexpr std::string_view kProcedureRegReplace = "regreplace"; constexpr std::string_view kResultRegReplace = "result"; +/* distance constants */ +constexpr std::string_view kProcedureDistance = "distance"; +constexpr std::string_view kText1 = "text1"; +constexpr std::string_view kText2 = "text2"; void Join(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory); void Format(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory); void RegexGroups(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory); void Replace(mgp_list *args, mgp_func_context *ctx, mgp_func_result *result, mgp_memory *memory); void RegReplace(mgp_list *args, mgp_func_context *ctx, mgp_func_result *result, mgp_memory *memory); +void Distance(mgp_list *args, mgp_func_context *ctx, mgp_func_result *result, mgp_memory *memory); } // namespace Text diff --git a/cpp/text_module/text_module.cpp b/cpp/text_module/text_module.cpp index c9490b4df..e4668a1cf 100644 --- a/cpp/text_module/text_module.cpp +++ b/cpp/text_module/text_module.cpp @@ -32,6 +32,11 @@ extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *mem mgp::Parameter(Text::kRegex, mgp::Type::String), mgp::Parameter(Text::kReplacement, mgp::Type::String)}, module, memory); + + mgp::AddFunction(Text::Distance, Text::kProcedureDistance, + {mgp::Parameter(Text::kText1, mgp::Type::String), + mgp::Parameter(Text::kText2, mgp::Type::String)}, + module, memory); } catch (const std::exception &e) { return 1; } diff --git a/e2e/text_test/test_distance_basic/input.cyp b/e2e/text_test/test_distance_basic/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_distance_basic/test.yml b/e2e/text_test/test_distance_basic/test.yml new file mode 100644 index 000000000..0e99a087b --- /dev/null +++ b/e2e/text_test/test_distance_basic/test.yml @@ -0,0 +1,4 @@ +query: | + RETURN text.distance("Levenshtein", "Levenstein") AS output; +output: | + - output: 1 diff --git a/e2e/text_test/test_distance_different/input.cyp b/e2e/text_test/test_distance_different/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_distance_different/test.yml b/e2e/text_test/test_distance_different/test.yml new file mode 100644 index 000000000..ce79bae92 --- /dev/null +++ b/e2e/text_test/test_distance_different/test.yml @@ -0,0 +1,4 @@ +query: | + RETURN text.distance("Hello", "World") AS output; +output: | + - output: 4 diff --git a/e2e/text_test/test_distance_empty/input.cyp b/e2e/text_test/test_distance_empty/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_distance_empty/test.yml b/e2e/text_test/test_distance_empty/test.yml new file mode 100644 index 000000000..2b85a54e4 --- /dev/null +++ b/e2e/text_test/test_distance_empty/test.yml @@ -0,0 +1,4 @@ +query: | + RETURN text.distance("", "") AS output; +output: | + - output: 0 From 2d253a09a3f5a23b2ebcbff428a637c9e6098b14 Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Mon, 9 Jun 2025 16:54:41 +0200 Subject: [PATCH 4/8] Adjust text tests --- cpp/text_module/algorithm/text.cpp | 12 +++++++++++- e2e/text_test/test_distance_basic/test.yml | 2 +- e2e/text_test/test_distance_different/test.yml | 2 +- e2e/text_test/test_distance_empty/test.yml | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/cpp/text_module/algorithm/text.cpp b/cpp/text_module/algorithm/text.cpp index 92707d985..e0d0d2e4b 100644 --- a/cpp/text_module/algorithm/text.cpp +++ b/cpp/text_module/algorithm/text.cpp @@ -1,8 +1,8 @@ #include "text.hpp" +#include #include #include -#include #include #include @@ -113,6 +113,11 @@ void Text::Replace(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_result * const auto regex = std::string(arguments[1].ValueString()); const auto replacement = std::string(arguments[2].ValueString()); + if (regex.size() == 0) { + result_obj.SetValue(text); + return; + } + std::string result_str = text; size_t pos = 0; while ((pos = result_str.find(regex, pos)) != std::string::npos) { @@ -137,6 +142,11 @@ void Text::RegReplace(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_resul const auto regex = std::string(arguments[1].ValueString()); const auto replacement = std::string(arguments[2].ValueString()); + if (regex.size() == 0) { + result_obj.SetValue(text); + return; + } + std::regex pattern(regex); std::string result_str = std::regex_replace(text, pattern, replacement); diff --git a/e2e/text_test/test_distance_basic/test.yml b/e2e/text_test/test_distance_basic/test.yml index 0e99a087b..7ea992fa3 100644 --- a/e2e/text_test/test_distance_basic/test.yml +++ b/e2e/text_test/test_distance_basic/test.yml @@ -1,4 +1,4 @@ query: | RETURN text.distance("Levenshtein", "Levenstein") AS output; -output: | +output: - output: 1 diff --git a/e2e/text_test/test_distance_different/test.yml b/e2e/text_test/test_distance_different/test.yml index ce79bae92..6e3e60194 100644 --- a/e2e/text_test/test_distance_different/test.yml +++ b/e2e/text_test/test_distance_different/test.yml @@ -1,4 +1,4 @@ query: | RETURN text.distance("Hello", "World") AS output; -output: | +output: - output: 4 diff --git a/e2e/text_test/test_distance_empty/test.yml b/e2e/text_test/test_distance_empty/test.yml index 2b85a54e4..542c58c8a 100644 --- a/e2e/text_test/test_distance_empty/test.yml +++ b/e2e/text_test/test_distance_empty/test.yml @@ -1,4 +1,4 @@ query: | RETURN text.distance("", "") AS output; -output: | +output: - output: 0 From 501bd1f8b7dc2f797b5ca6c4ed25fced36e8bc67 Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Mon, 16 Jun 2025 09:28:27 +0200 Subject: [PATCH 5/8] Change constants --- cpp/text_module/algorithm/text.hpp | 13 +++++-------- cpp/text_module/text_module.cpp | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cpp/text_module/algorithm/text.hpp b/cpp/text_module/algorithm/text.hpp index 581cc2135..f14de26fb 100644 --- a/cpp/text_module/algorithm/text.hpp +++ b/cpp/text_module/algorithm/text.hpp @@ -11,26 +11,23 @@ namespace Text { constexpr std::string_view kProcedureJoin = "join"; constexpr std::string_view kJoinArg1 = "strings"; constexpr std::string_view kJoinArg2 = "delimiter"; -constexpr std::string_view kResultJoin = "join"; +constexpr std::string_view kResultJoin = "string"; /* format constants */ constexpr std::string_view kProcedureFormat = "format"; -constexpr std::string_view kStringToFormat = "format"; +constexpr std::string_view kStringToFormat = "text"; constexpr std::string_view kParameters = "params"; -constexpr std::string_view kResultFormat = "format"; +constexpr std::string_view kResultFormat = "result"; /* regex constants */ -constexpr std::string_view kProcedureRegexGroups = "regex_groups"; +constexpr std::string_view kProcedureRegexGroups = "regexGroups"; constexpr std::string_view kInput = "input"; constexpr std::string_view kRegex = "regex"; -constexpr std::string_view kResultRegexGroups = "groups"; +constexpr std::string_view kResultRegexGroups = "results"; /* replace constants */ constexpr std::string_view kProcedureReplace = "replace"; constexpr std::string_view kText = "text"; -constexpr std::string_view kRegexReplace = "search"; constexpr std::string_view kReplacement = "replacement"; -constexpr std::string_view kResultReplace = "result"; /* regreplace constants */ constexpr std::string_view kProcedureRegReplace = "regreplace"; -constexpr std::string_view kResultRegReplace = "result"; /* distance constants */ constexpr std::string_view kProcedureDistance = "distance"; constexpr std::string_view kText1 = "text1"; diff --git a/cpp/text_module/text_module.cpp b/cpp/text_module/text_module.cpp index e4668a1cf..b23b18293 100644 --- a/cpp/text_module/text_module.cpp +++ b/cpp/text_module/text_module.cpp @@ -23,7 +23,7 @@ extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *mem mgp::AddFunction(Text::Replace, Text::kProcedureReplace, {mgp::Parameter(Text::kText, mgp::Type::String), - mgp::Parameter(Text::kRegexReplace, mgp::Type::String), + mgp::Parameter(Text::kRegex, mgp::Type::String), mgp::Parameter(Text::kReplacement, mgp::Type::String)}, module, memory); From ddfcd8f799ea4ee935003f14270ba8baaa66abe7 Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Mon, 16 Jun 2025 09:29:12 +0200 Subject: [PATCH 6/8] Add test --- e2e/text_test/test_distance_levenshtein/input.cyp | 0 e2e/text_test/test_distance_levenshtein/test.yml | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 e2e/text_test/test_distance_levenshtein/input.cyp create mode 100644 e2e/text_test/test_distance_levenshtein/test.yml diff --git a/e2e/text_test/test_distance_levenshtein/input.cyp b/e2e/text_test/test_distance_levenshtein/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_distance_levenshtein/test.yml b/e2e/text_test/test_distance_levenshtein/test.yml new file mode 100644 index 000000000..7ea992fa3 --- /dev/null +++ b/e2e/text_test/test_distance_levenshtein/test.yml @@ -0,0 +1,4 @@ +query: | + RETURN text.distance("Levenshtein", "Levenstein") AS output; +output: + - output: 1 From b4892f59d0e5e7c0ba3f9e4da71869635c06201c Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Mon, 16 Jun 2025 09:31:58 +0200 Subject: [PATCH 7/8] Add tests --- e2e/text_test/test_regreplace_mage/input.cyp | 0 e2e/text_test/test_regreplace_mage/test.yml | 5 +++++ e2e/text_test/test_replace_mage/input.cyp | 0 e2e/text_test/test_replace_mage/test.yml | 5 +++++ 4 files changed, 10 insertions(+) create mode 100644 e2e/text_test/test_regreplace_mage/input.cyp create mode 100644 e2e/text_test/test_regreplace_mage/test.yml create mode 100644 e2e/text_test/test_replace_mage/input.cyp create mode 100644 e2e/text_test/test_replace_mage/test.yml diff --git a/e2e/text_test/test_regreplace_mage/input.cyp b/e2e/text_test/test_regreplace_mage/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_regreplace_mage/test.yml b/e2e/text_test/test_regreplace_mage/test.yml new file mode 100644 index 000000000..d393952f4 --- /dev/null +++ b/e2e/text_test/test_regreplace_mage/test.yml @@ -0,0 +1,5 @@ +query: > + RETURN text.regreplace("Memgraph MAGE Memgraph MAGE!", "MAGE", "GQLAlchemy") AS output; + +output: + - output: "Memgraph GQLAlchemy Memgraph GQLAlchemy!" diff --git a/e2e/text_test/test_replace_mage/input.cyp b/e2e/text_test/test_replace_mage/input.cyp new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/text_test/test_replace_mage/test.yml b/e2e/text_test/test_replace_mage/test.yml new file mode 100644 index 000000000..8b7b1ba34 --- /dev/null +++ b/e2e/text_test/test_replace_mage/test.yml @@ -0,0 +1,5 @@ +query: > + RETURN text.replace("MAGE is a Memgraph product", "MAGE", "GQLAlchemy") AS output; + +output: + - output: "GQLAlchemy is a Memgraph product" From aaa20b2198e72694a85d23667de92f9ee4ee53c2 Mon Sep 17 00:00:00 2001 From: Josip Mrden Date: Mon, 16 Jun 2025 17:27:45 +0200 Subject: [PATCH 8/8] Address PR comments --- cpp/text_module/algorithm/text.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cpp/text_module/algorithm/text.cpp b/cpp/text_module/algorithm/text.cpp index e0d0d2e4b..ba5bf70a7 100644 --- a/cpp/text_module/algorithm/text.cpp +++ b/cpp/text_module/algorithm/text.cpp @@ -109,7 +109,7 @@ void Text::Replace(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_result * mgp::Result result_obj(result); try { - const auto text = std::string(arguments[0].ValueString()); + auto text = std::string(arguments[0].ValueString()); const auto regex = std::string(arguments[1].ValueString()); const auto replacement = std::string(arguments[2].ValueString()); @@ -118,14 +118,13 @@ void Text::Replace(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_result * return; } - std::string result_str = text; size_t pos = 0; - while ((pos = result_str.find(regex, pos)) != std::string::npos) { - result_str.replace(pos, regex.length(), replacement); + while ((pos = text.find(regex, pos)) != std::string::npos) { + text.replace(pos, regex.length(), replacement); pos += replacement.length(); } - result_obj.SetValue(result_str); + result_obj.SetValue(std::move(text)); } catch (const std::exception &e) { result_obj.SetErrorMessage(e.what()); return; @@ -150,7 +149,7 @@ void Text::RegReplace(mgp_list *args, mgp_func_context * /*ctx*/, mgp_func_resul std::regex pattern(regex); std::string result_str = std::regex_replace(text, pattern, replacement); - result_obj.SetValue(result_str); + result_obj.SetValue(std::move(result_str)); } catch (const std::exception &e) { result_obj.SetErrorMessage(e.what()); return;