From c5a68737ba51ab05fd2ad56cec91688c31e0d11e Mon Sep 17 00:00:00 2001 From: Kirollos Onsi Date: Sat, 14 Sep 2024 18:53:21 +0300 Subject: [PATCH] Add MatchPattern Method --- README.md | 1 + .../Common/ExpressionParserUtilities.cs | 8 ++++++ src/Redis.OM/Extensions/StringExtension.cs | 14 +++++++++++ .../RediSearchTests/SearchFunctionalTests.cs | 25 +++++++++++++++++++ .../RediSearchTests/SearchTests.cs | 20 ++++++++++++++- 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 59e2daf8..e3459753 100644 --- a/README.md +++ b/README.md @@ -484,6 +484,7 @@ We'd love your contributions! If you want to contribute please read our [Contrib * [@PrudiusVladislav](https://github.com/PrudiusVladislav) * [@CormacLennon](https://github.com/CormacLennon) * [@ahmedisam99](https://github.com/ahmedisam99) +* [@kirollosonsi](https://github.com/kirollosonsi) [Logo]: images/logo.svg diff --git a/src/Redis.OM/Common/ExpressionParserUtilities.cs b/src/Redis.OM/Common/ExpressionParserUtilities.cs index 783f75db..381e957d 100644 --- a/src/Redis.OM/Common/ExpressionParserUtilities.cs +++ b/src/Redis.OM/Common/ExpressionParserUtilities.cs @@ -191,6 +191,7 @@ internal static string TranslateMethodExpressions(MethodCallExpression exp, List "Contains" => TranslateContainsStandardQuerySyntax(exp, parameters), nameof(StringExtension.FuzzyMatch) => TranslateFuzzyMatch(exp), nameof(StringExtension.MatchContains) => TranslateMatchContains(exp), + nameof(StringExtension.MatchPattern) => TranslateMatchPattern(exp), nameof(StringExtension.MatchStartsWith) => TranslateMatchStartsWith(exp), nameof(StringExtension.MatchEndsWith) => TranslateMatchEndsWith(exp), nameof(VectorExtensions.VectorRange) => TranslateVectorRange(exp, parameters), @@ -784,6 +785,13 @@ private static string TranslateMatchContains(MethodCallExpression exp) return $"({source}:*{infix}*)"; } + private static string TranslateMatchPattern(MethodCallExpression exp) + { + var source = GetOperandString(exp.Arguments[0]); + var pattern = GetOperandString(exp.Arguments[1]); + return $"({source}:{pattern})"; + } + private static string TranslateFuzzyMatch(MethodCallExpression exp) { var source = GetOperandString(exp.Arguments[0]); diff --git a/src/Redis.OM/Extensions/StringExtension.cs b/src/Redis.OM/Extensions/StringExtension.cs index 2d53fb36..442462e8 100644 --- a/src/Redis.OM/Extensions/StringExtension.cs +++ b/src/Redis.OM/Extensions/StringExtension.cs @@ -84,6 +84,20 @@ public static bool MatchContains(this string source, string infix) return terms.Any(t => t.EndsWith(infix)); } + /// + /// Checks the source string to see if any tokens within the source matches the pattern. + /// + /// The string to check. + /// The pattern to look for within the string. + /// Whether any token within the source string matches the pattern. + /// This is meant to be a shadow method that runs within an expression, a working implementation is + /// provided here for completeness. + public static bool MatchPattern(this string source, string pattern) + { + var terms = source.Split(SplitChars); + return terms.Any(t => t.EndsWith(pattern)); + } + /// /// Wagner-Fischer dynamic programming string distance algorithm. /// diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs index 31fefcbf..6c36fe52 100644 --- a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchFunctionalTests.cs @@ -1245,5 +1245,30 @@ public void TestMultipleSearchAttributesOnEmbeddedDoc() var res = collection.First(x => x.Address.City == "Long" && x.Address.State == "New"); Assert.Equal(obj.Id, res.Id); } + + [Theory] + [InlineData("Ste*", 1)] + [InlineData("*orello*", 1)] + [InlineData("Sto* & Lor*", 0)] + [InlineData("Sto* | Lor*", 1)] + [InlineData("%%Pittor%%", 1)] + [InlineData("%Pittor%", 0)] + [InlineData("%%%Pittor%%%", 1)] + [InlineData("%%Pittor%% & Ha*", 1)] + [InlineData("Ste* | Ha*", 2)] + public async Task TestSearchByMatchPattern(string pattern, int existingRecordsCount) + { + var collection = new RedisCollection(_connection); + var persons = await collection.ToListAsync(); + await collection.DeleteAsync(persons); + + var person1 = new Person { Name = "Steve Lorello" }; + var person2 = new Person { Name = "Harry Potter" }; + collection.Insert(person1); + collection.Insert(person2); + + var res = await collection.Where(x => x.Name.MatchPattern(pattern)).ToListAsync(); + Assert.Equal(existingRecordsCount, res.Count); + } } } \ No newline at end of file diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs index 813e08d9..7200e923 100644 --- a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs @@ -415,7 +415,25 @@ public void TestMatchContains() "0", "100"); } - + + [Fact] + public void TestMatchPattern() + { + _substitute.ClearSubstitute(); + _substitute.Execute(Arg.Any(), Arg.Any()).Returns(_mockReply); + + var collection = new RedisCollection(_substitute); + var ddfgdf = collection.Where(x => x.Name.MatchPattern("Ste* Lo*")).ToList(); + + _substitute.Received().Execute( + "FT.SEARCH", + "person-idx", + "(@Name:Ste* Lo*)", + "LIMIT", + "0", + "100"); + } + [Fact] public void TestTagContains() {