From 0b4dfd5de59f01e86b1a20e722e8805c65f45310 Mon Sep 17 00:00:00 2001 From: Florian Hockmann Date: Fri, 29 Nov 2024 12:32:58 +0100 Subject: [PATCH] 1.1.0 Release Notable changes: * Support for JanusGraph 1.1.0 * Gremlin.Net updated to 3.7.3 (JanusGraph 1.1.0 also uses TinkerPop 3.7.3) * Missing text predicates added (mostly negations, but also `TextContainsPhrase`) Fixes #165 Signed-off-by: Florian Hockmann --- .github/workflows/linter.yml | 2 + README.md | 17 ++- src/JanusGraph.Net/JanusGraph.Net.csproj | 2 +- src/JanusGraph.Net/Text.cs | 66 +++++++++ .../TextTests.cs | 137 +++++++++++++++++- .../appsettings.json | 2 +- 6 files changed, 209 insertions(+), 17 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index abcbf77..c16dc5a 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -41,5 +41,7 @@ jobs: env: VALIDATE_ALL_CODEBASE: false VALIDATE_JSCPD: false # need to find a way to ignore license headers for duplicate detection + VALIDATE_JSON_PRETTIER: false # this unfortunately conflicts with our .editorconfig for no final new line + VALIDATE_MARKDOWN_PRETTIER: false # this unfortunately conflicts with our .editorconfig for no final new line DEFAULT_BRANCH: master GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index ee08eab..45acd1f 100644 --- a/README.md +++ b/README.md @@ -95,13 +95,14 @@ The lowest supported JanusGraph version is 0.3.0. The following table shows the supported JanusGraph versions for each version of JanusGraph.Net: -| JanusGraph.Net | JanusGraph | -| -------------- | ---------------------- | -| 0.1.z | 0.3.z | -| 0.2.z | 0.4.z, 0.5.z | -| 0.3.z | 0.4.z, 0.5.z, 0.6.z | -| 0.4.z | (0.4.z, 0.5.z,) 0.6.z | -| 1.0.z | (0.6.z,) 1.0.z | +| JanusGraph.Net | JanusGraph | +| -------------- | --------------------- | +| 0.1.z | 0.3.z | +| 0.2.z | 0.4.z, 0.5.z | +| 0.3.z | 0.4.z, 0.5.z, 0.6.z | +| 0.4.z | (0.4.z, 0.5.z,) 0.6.z | +| 1.0.z | (0.6.z,) 1.0.z | +| 1.1.z | 1.0.0, 1.1.z | While it should also be possible to use JanusGraph.Net with other versions of JanusGraph than mentioned here, compatibility is not tested and some @@ -132,7 +133,7 @@ Not all of the JanusGraph-specific types are already supported by both formats: | Format | RelationIdentifier | Text predicates | Geoshapes | Geo predicates | | ----------- | ------------------ | --------------- | --------- | -------------- | | GraphSON3 | x | x | `Point` | - | -| GraphBinary | x | x | `Point`* | - | +| GraphBinary | x | x | `Point`\* | - | \* Since version 1.0.0 of JanusGraph.Net. JanusGraph also needs to be on version 1.0.0 or higher. diff --git a/src/JanusGraph.Net/JanusGraph.Net.csproj b/src/JanusGraph.Net/JanusGraph.Net.csproj index 58d5745..ee73b27 100644 --- a/src/JanusGraph.Net/JanusGraph.Net.csproj +++ b/src/JanusGraph.Net/JanusGraph.Net.csproj @@ -8,7 +8,7 @@ snupkg 12 enable - 1.0.0 + 1.1.0 JanusGraph.Net JanusGraph diff --git a/src/JanusGraph.Net/Text.cs b/src/JanusGraph.Net/Text.cs index 8168335..e9a31e1 100644 --- a/src/JanusGraph.Net/Text.cs +++ b/src/JanusGraph.Net/Text.cs @@ -34,6 +34,13 @@ public static class Text /// The text predicate. public static P TextContains(string query) => new JanusGraphP("textContains", query); + /// + /// Is true if no words inside the text string match the query string. + /// + /// The query to search. + /// The text predicate. + public static P TextNotContains(string query) => new JanusGraphP("textNotContains", query); + /// /// Is true if (at least) one word inside the text string begins with the query string. /// @@ -41,12 +48,27 @@ public static class Text /// The text predicate. public static P TextContainsPrefix(string query) => new JanusGraphP("textContainsPrefix", query); + /// + /// Is true if no words inside the text string begin with the query string. + /// + /// The query to search. + /// The text predicate. + public static P TextNotContainsPrefix(string query) => new JanusGraphP("textNotContainsPrefix", query); + /// /// Is true if (at least) one word inside the text string matches the given regular expression. /// /// The regular expression. /// The text predicate. public static P TextContainsRegex(string regex) => new JanusGraphP("textContainsRegex", regex); + + /// + /// Is true if no words inside the text string match the given regular expression. + /// + /// The regular expression. + /// The text predicate. + public static P TextNotContainsRegex(string regex) => new JanusGraphP("textNotContainsRegex", regex); + /// /// Is true if (at least) one word inside the text string is similar to the query String (based on /// Levenshtein edit distance). @@ -55,6 +77,28 @@ public static class Text /// The text predicate. public static P TextContainsFuzzy(string query) => new JanusGraphP("textContainsFuzzy", query); + /// + /// Is true if no words inside the text string are similar to the query string (based on Levenshtein edit + /// distance). + /// + /// The query to search. + /// The text predicate. + public static P TextNotContainsFuzzy(string query) => new JanusGraphP("textNotContainsFuzzy", query); + + /// + /// Is true if the text string does contain the sequence of words in the query string. + /// + /// The query to search. + /// The text predicate. + public static P TextContainsPhrase(string query) => new JanusGraphP("textContainsPhrase", query); + + /// + /// Is true if the text string does not contain the sequence of words in the query string. + /// + /// The query to search. + /// The text predicate. + public static P TextNotContainsPhrase(string query) => new JanusGraphP("textNotContainsPhrase", query); + /// /// Is true if the string value starts with the given query string. /// @@ -62,6 +106,13 @@ public static class Text /// The text predicate. public static P TextPrefix(string query) => new JanusGraphP("textPrefix", query); + /// + /// Is true if the string value does not start with the given query string. + /// + /// The query to search. + /// The text predicate. + public static P TextNotPrefix(string query) => new JanusGraphP("textNotPrefix", query); + /// /// Is true if the string value matches the given regular expression in its entirety. /// @@ -69,11 +120,26 @@ public static class Text /// The text predicate. public static P TextRegex(string regex) => new JanusGraphP("textRegex", regex); + /// + /// Is true if the string value does not match the given regular expression in its entirety. + /// + /// The regular expression. + /// The text predicate. + public static P TextNotRegex(string regex) => new JanusGraphP("textNotRegex", regex); + /// /// Is true if the string value is similar to the given query string (based on Levenshtein edit distance). /// /// The query to search. /// The text predicate. public static P TextFuzzy(string query) => new JanusGraphP("textFuzzy", query); + + /// + /// Is true if the string value is not similar to the given query string (based on Levenshtein edit + /// distance). + /// + /// The query to search. + /// The text predicate. + public static P TextNotFuzzy(string query) => new JanusGraphP("textNotFuzzy", query); } } \ No newline at end of file diff --git a/test/JanusGraph.Net.IntegrationTest/TextTests.cs b/test/JanusGraph.Net.IntegrationTest/TextTests.cs index 573b3f9..ec344a5 100644 --- a/test/JanusGraph.Net.IntegrationTest/TextTests.cs +++ b/test/JanusGraph.Net.IntegrationTest/TextTests.cs @@ -33,7 +33,7 @@ public abstract class TextTests : IDisposable [Theory] [InlineData("loves", 2)] [InlineData("shouldNotBeFound", 0)] - public async Task TextContainsgivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) + public async Task TextContainsGivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) { var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); @@ -42,11 +42,23 @@ public async Task TextContainsgivenSearchText_ExpectedCountOfElements(string sea Assert.Equal(expectedCount, count); } + [Theory] + [InlineData("loves", 1)] + [InlineData("shouldNotBeFound", 3)] + public async Task TextNotContainsGivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.E().Has("reason", Text.TextNotContains(searchText)).Count().Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + [Theory] [InlineData("wave", 1)] [InlineData("f", 2)] [InlineData("shouldNotBeFound", 0)] - public async Task TextContainsPrefixgivenSearchText_ExpectedCountOfElements(string searchText, + public async Task TextContainsPrefixGivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) { var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); @@ -56,11 +68,26 @@ public async Task TextContainsPrefixgivenSearchText_ExpectedCountOfElements(stri Assert.Equal(expectedCount, count); } + [Theory] + [InlineData("wave", 2)] + [InlineData("f", 1)] + [InlineData("shouldNotBeFound", 3)] + public async Task TextNotContainsPrefixGivenSearchText_ExpectedCountOfElements(string searchText, + int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.E().Has("reason", Text.TextNotContainsPrefix(searchText)).Count() + .Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + [Theory] [InlineData(".*ave.*", 1)] [InlineData("f.{3,4}", 2)] [InlineData("shouldNotBeFound", 0)] - public async Task TextContainsRegexgivenRegex_ExpectedCountOfElements(string regex, int expectedCount) + public async Task TextContainsRegexGivenRegex_ExpectedCountOfElements(string regex, int expectedCount) { var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); @@ -69,10 +96,23 @@ public async Task TextContainsRegexgivenRegex_ExpectedCountOfElements(string reg Assert.Equal(expectedCount, count); } + [Theory] + [InlineData(".*ave.*", 2)] + [InlineData("f.{3,4}", 1)] + [InlineData("shouldNotBeFound", 3)] + public async Task TextNotContainsRegexGivenRegex_ExpectedCountOfElements(string regex, int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.E().Has("reason", Text.TextNotContainsRegex(regex)).Count().Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + [Theory] [InlineData("waxes", 1)] [InlineData("shouldNotBeFound", 0)] - public async Task TextContainsFuzzygivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) + public async Task TextContainsFuzzyGivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) { var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); @@ -81,11 +121,55 @@ public async Task TextContainsFuzzygivenSearchText_ExpectedCountOfElements(strin Assert.Equal(expectedCount, count); } + [Theory] + [InlineData("waxes", 2)] + [InlineData("shouldNotBeFound", 3)] + public async Task TextNotContainsFuzzyGivenSearchText_ExpectedCountOfElements(string searchText, + int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.E().Has("reason", Text.TextNotContainsFuzzy(searchText)).Count().Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + + [Theory] + [InlineData("fresh breezes", 1)] + [InlineData("no fear", 1)] + [InlineData("fear of", 1)] + [InlineData("shouldNotBeFound", 0)] + public async Task TextContainsPhraseGivenSearchText_ExpectedCountOfElements(string searchText, + int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.E().Has("reason", Text.TextContainsPhrase(searchText)).Count().Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + + [Theory] + [InlineData("fresh breezes", 2)] + [InlineData("no fear", 2)] + [InlineData("fear of", 2)] + [InlineData("shouldNotBeFound", 3)] + public async Task TextNotContainsPhraseGivenSearchText_ExpectedCountOfElements(string searchText, + int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.E().Has("reason", Text.TextNotContainsPhrase(searchText)).Count() + .Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + [Theory] [InlineData("herc", 1)] [InlineData("s", 3)] [InlineData("shouldNotBeFound", 0)] - public async Task TextPrefixgivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) + public async Task TextPrefixGivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) { var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); @@ -94,11 +178,24 @@ public async Task TextPrefixgivenSearchText_ExpectedCountOfElements(string searc Assert.Equal(expectedCount, count); } + [Theory] + [InlineData("herc", 11)] + [InlineData("s", 9)] + [InlineData("shouldNotBeFound", 12)] + public async Task TextNotPrefixGivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.V().Has("name", Text.TextNotPrefix(searchText)).Count().Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + [Theory] [InlineData(".*rcule.*", 1)] [InlineData("s.{2}", 2)] [InlineData("shouldNotBeFound", 0)] - public async Task TextRegexgivenRegex_ExpectedCountOfElements(string regex, int expectedCount) + public async Task TextRegexGivenRegex_ExpectedCountOfElements(string regex, int expectedCount) { var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); @@ -107,11 +204,24 @@ public async Task TextRegexgivenRegex_ExpectedCountOfElements(string regex, int Assert.Equal(expectedCount, count); } + [Theory] + [InlineData(".*rcule.*", 11)] + [InlineData("s.{2}", 10)] + [InlineData("shouldNotBeFound", 12)] + public async Task TextNotRegexGivenRegex_ExpectedCountOfElements(string regex, int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.V().Has("name", Text.TextNotRegex(regex)).Count().Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + [Theory] [InlineData("herculex", 1)] [InlineData("ska", 2)] [InlineData("shouldNotBeFound", 0)] - public async Task TextFuzzygivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) + public async Task TextFuzzyGivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) { var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); @@ -120,6 +230,19 @@ public async Task TextFuzzygivenSearchText_ExpectedCountOfElements(string search Assert.Equal(expectedCount, count); } + [Theory] + [InlineData("herculex", 11)] + [InlineData("ska", 10)] + [InlineData("shouldNotBeFound", 12)] + public async Task TextNotFuzzyGivenSearchText_ExpectedCountOfElements(string searchText, int expectedCount) + { + var g = Traversal().WithRemote(ConnectionFactory.CreateRemoteConnection()); + + var count = await g.V().Has("name", Text.TextNotFuzzy(searchText)).Count().Promise(t => t.Next()); + + Assert.Equal(expectedCount, count); + } + public void Dispose() { ConnectionFactory?.Dispose(); diff --git a/test/JanusGraph.Net.IntegrationTest/appsettings.json b/test/JanusGraph.Net.IntegrationTest/appsettings.json index 362bcb7..c3d0b11 100644 --- a/test/JanusGraph.Net.IntegrationTest/appsettings.json +++ b/test/JanusGraph.Net.IntegrationTest/appsettings.json @@ -1,3 +1,3 @@ { - "dockerImage": "janusgraph/janusgraph:1.0.0" + "dockerImage": "janusgraph/janusgraph:1.1.0" } \ No newline at end of file