From c87171bfa7dd886169a7b1ac31df7b7f3e27bc53 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Wed, 11 Oct 2023 22:30:22 -0700 Subject: [PATCH] Change substring() to use `startIndex, endIndex` instead of `startIndex, length` --- .../dev/provider/gremlin-semantics.asciidoc | 8 ++-- docs/src/upgrade/release-3.7.x.asciidoc | 21 +++++---- .../traversal/dsl/graph/GraphTraversal.java | 18 ++++---- .../process/traversal/dsl/graph/__.java | 4 +- .../traversal/step/map/SubstringStep.java | 43 +++++++++-------- .../traversal/step/map/SubstringStepTest.java | 28 +++++++---- .../Process/Traversal/GraphTraversal.cs | 4 +- .../src/Gremlin.Net/Process/Traversal/__.cs | 4 +- .../Gherkin/Gremlin.cs | 7 ++- gremlin-go/driver/cucumber/gremlin.go | 7 ++- .../test/cucumber/gremlin.js | 7 ++- .../src/main/python/radish/gremlin.py | 7 ++- .../test/features/map/Substring.feature | 46 +++++++++++++++++-- 13 files changed, 132 insertions(+), 72 deletions(-) diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc index 74b69e53299..c1825c261db 100644 --- a/docs/src/dev/provider/gremlin-semantics.asciidoc +++ b/docs/src/dev/provider/gremlin-semantics.asciidoc @@ -1194,9 +1194,9 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#split-step[reference] [[substring-step]] === substring() -*Description:* Returns a substring of the incoming string traverser with a 0-based start index (inclusive) and length specified. +*Description:* Returns a substring of the incoming string traverser with a 0-based start index (inclusive) and end index (exclusive). -*Syntax:* `substring(long, long)` +*Syntax:* `substring(int, int)` [width="100%",options="header"] |========================================================= @@ -1208,8 +1208,8 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#split-step[reference] * `int` - The start index, 0 based. If the start index is negative then it will begin at the specified index counted from the end of the string, or 0 if it exceeds the string length. -* `int` - The number of characters to return. Optional, if it is not specific or if it exceeds the length of the string -then all remaining characters will be returned. Length ≤ 0 will return the empty string. +* `int` - The end index, 0 based. Optional, if it is not specific then all remaining characters will be returned. End +index ≤ start index will return the empty string. Null values from the incoming traverser are not processed and remain as null when returned. diff --git a/docs/src/upgrade/release-3.7.x.asciidoc b/docs/src/upgrade/release-3.7.x.asciidoc index 75206c70765..40ee470eaaf 100644 --- a/docs/src/upgrade/release-3.7.x.asciidoc +++ b/docs/src/upgrade/release-3.7.x.asciidoc @@ -187,15 +187,15 @@ gremlin> g.V().hasLabel("person").values("name").split("a") ==>[peter] ---- -For `substring()`, the new Gremlin step follows the SQL standard, taking parameters start index and desired length of substring, -instead of start and end indices like Java/Groovy, enabling certain operations that would be complex to achieve with closure: +For `substring()`, the new Gremlin step follows the Python standard, taking parameters start index and optionally an +end index. This will enable certain operations that would be complex to achieve with closure: [source,text] ---- -gremlin> g.V().hasLabel("person").values("name").map{it.get().substring(1,3)} -==>ar -==>ad -==>os -==>et +gremlin> g.V().hasLabel("person").values("name").map{it.get().substring(1,4)} +==>ark +==>ada +==>osh +==>ete gremlin> g.V().hasLabel("person").values("name").map{it.get().substring(1)} ==>arko ==>adas @@ -206,11 +206,12 @@ String index out of range: -2 Type ':help' or ':h' for help. ---- -The `substring()`-step will begin at the start index and return a substring with the length specified. Negative start -indices are allowed and will begin at the specified index counted from the end of the string: +The `substring()`-step will return a substring with indices specified by the start and end indices, or from +the start index to the remainder of the string if an end index is not specified. Negative indices are allowed and will +count from the end of the string: [source,text] ---- -gremlin> g.V().hasLabel("person").values("name").substring(1,3) +gremlin> g.V().hasLabel("person").values("name").substring(1,4) ==>ark ==>ada ==>osh diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java index be9674e507b..a6257a4eba0 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java @@ -1608,20 +1608,20 @@ public default GraphTraversal substring(final int startIndex) { } /** - * Returns a substring of the incoming string traverser with a 0-based start index (inclusive) index and length - * specified. If the start index is negative then it will begin at the specified index counted from the end - * of the string, or 0 if exceeding the string length. Length is optional, if it is not specific or if it exceeds - * the length of the string then all remaining characters will be returned. Length <= 0 will return the empty string. - * Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an - * {@code IllegalArgumentException} will be thrown. + * Returns a substring of the incoming string traverser with a 0-based start index (inclusive) and end index + * (exclusive). If the start index is negative then it will begin at the specified index counted from the end of the + * string, or 0 if exceeding the string length. If the end index is negative then it will end at the specified index + * counted from the end, or at the end of the string if exceeding the string length. End index <= start index will + * return the empty string. Null values are not processed and remain as null when returned. If the incoming + * traverser is a non-String value then an {@code IllegalArgumentException} will be thrown. * * @return the traversal with an appended {@link SubstringStep}. * @see Reference Documentation - Substring Step * @since 3.7.1 */ - public default GraphTraversal substring(final int startIndex, final int length) { - this.asAdmin().getBytecode().addStep(Symbols.substring, startIndex, length); - return this.asAdmin().addStep(new SubstringStep<>(this.asAdmin(), startIndex, length)); + public default GraphTraversal substring(final int startIndex, final int endIndex) { + this.asAdmin().getBytecode().addStep(Symbols.substring, startIndex, endIndex); + return this.asAdmin().addStep(new SubstringStep<>(this.asAdmin(), startIndex, endIndex)); } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java index f41f3506a67..cee80f1dadc 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java @@ -684,8 +684,8 @@ public static GraphTraversal substring(final int startIndex) { /** * @see GraphTraversal#substring(int, int) */ - public static GraphTraversal substring(final int startIndex, final int length) { - return __.start().substring(startIndex, length); + public static GraphTraversal substring(final int startIndex, final int endIndex) { + return __.start().substring(startIndex, endIndex); } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringStep.java index e28f40557e0..6638a54c2fb 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringStep.java @@ -28,10 +28,11 @@ /** * Reference implementation for substring step, a mid-traversal step which returns a substring of the incoming string - * traverser with a 0-based start index (inclusive) and length specified. If the start index is negative then it will - * begin at the specified index counted from the end of the string, or 0 if exceeding the string length. - * Length is optional, if it is not specific or if it exceeds the length of the string then all remaining characters will - * be returned. Length <= 0 will return the empty string. Null values are not processed and remain as null when returned. + * traverser with a 0-based start index (inclusive) and optionally an end index (exclusive). If the start index is negative then it will + * begin at the specified index counted from the end of the string, or 0 if exceeding the string length. Likewise, if + * the end index is negative then it will end at the specified index counted from the end of the string, or 0 if exceeding the string length. + * End index is optional, if it is not specified or if it exceeds the length of the string then all remaining characters will + * be returned. End index <= start index will return the empty string. Null values are not processed and remain as null when returned. * If the incoming traverser is a non-String value then an {@code IllegalArgumentException} will be thrown. * * @author David Bechberger (http://bechberger.com) @@ -40,12 +41,12 @@ public final class SubstringStep extends ScalarMapStep { private final Integer start; - private final Integer length; + private final Integer end; - public SubstringStep(final Traversal.Admin traversal, final Integer startIndex, final Integer length) { + public SubstringStep(final Traversal.Admin traversal, final Integer startIndex, final Integer end) { super(traversal); this.start = startIndex; - this.length = length; + this.end = end; } public SubstringStep(final Traversal.Admin traversal, final Integer startIndex) { @@ -67,17 +68,16 @@ protected String map(final Traverser.Admin traverser) { if (null == strItem) return null; - final int newStart = processStartIndex(strItem.length()); - if (null == this.length) + final int newStart = processStringIndex(strItem.length(), this.start); + if (null == this.end) return strItem.substring(newStart); - // length < 0 will return the empty string. - if (this.length <= 0) + final int newEnd = processStringIndex(strItem.length(), this.end); + + if (newEnd <= newStart) return ""; - // if length specified exceeds the string length it is assumed to be equal to the length, which returns all - // remaining characters in the string. - return strItem.substring(newStart, Math.min(this.length + newStart, strItem.length())); + return strItem.substring(newStart, newEnd); } @Override @@ -89,19 +89,18 @@ public Set getRequirements() { public int hashCode() { int result = super.hashCode(); result = 31 * result + this.start.hashCode(); - result = 31 * result + (null != this.length ? this.length.hashCode() : 0); + result = 31 * result + (null != this.end ? this.end.hashCode() : 0); return result; } - // Helper function to process the start index, if it is negative (which counts from end of string) it is converted - // to the positive index position or 0 when negative index exceeds the string length, if it is positive and exceeds + // Helper function to process indices. If it is negative (which counts from end of string) it is converted + // to the positive index position or 0 when negative index exceeds the string length. If it is positive and exceeds // the length of the string, it is assumed to equal to the length, which means an empty string will be returned. - private int processStartIndex(int strLen) { - if (this.start < 0) { - return Math.max(0, (strLen + this.start) % strLen); + private int processStringIndex(int strLen, int index) { + if (index < 0) { + return Math.max(0, (strLen + index) % strLen); } else { - return Math.min(this.start, strLen); + return Math.min(index, strLen); } } - } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringStepTest.java index 3af4e06e130..10dc5c288d0 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringStepTest.java @@ -41,16 +41,28 @@ protected List getTraversals() { @Test public void testReturnTypes() { - assertEquals("ello wor", __.__("hello world").substring(1, 8).next()); - assertEquals("ello", __.__("hello").substring(1, 8).next()); - assertEquals("world", __.__("hello world").substring(6).next()); + assertEquals("hello world", __.__("hello world").substring(0).next()); + assertEquals("ello world", __.__("hello world").substring(1).next()); + assertEquals("d", __.__("hello world").substring(-1).next()); + assertEquals("d", __.__("hello world").substring(10).next()); + assertEquals("", __.__("hello world").substring(11).next()); - assertEquals("world", __.__("world").substring(-10).next()); - assertEquals("orld", __.__("world").substring(-4).next()); - assertEquals("orl", __.__("world").substring(-4, 3).next()); - assertEquals("", __.__("world").substring(1, -1).next()); + assertEquals("", __.__("hello world").substring(0, 0).next()); + assertEquals("h", __.__("hello world").substring(0, 1).next()); + assertEquals("", __.__("hello world").substring(1, 0).next()); + assertEquals("hello worl", __.__("hello world").substring(0, -1).next()); + assertEquals("", __.__("hello world").substring(-1, 0).next()); + assertEquals("ello worl", __.__("hello world").substring(1, -1).next()); + assertEquals("", __.__("hello world").substring(-1, 1).next()); + assertEquals("rl", __.__("hello world").substring(-3, -1).next()); + assertEquals("", __.__("hello world").substring(-1, -3).next()); + assertEquals("d", __.__("hello world").substring(-1, 11).next()); + assertEquals("ello world", __.__("hello world").substring(1, 11).next()); + assertEquals("d", __.__("hello world").substring(10, 11).next()); + assertEquals("hello world", __.__("hello world").substring(-11, 11).next()); + assertEquals("h", __.__("hello world").substring(-11, 1).next()); - assertArrayEquals(new String[]{"st", "llo worl", null, ""}, + assertArrayEquals(new String[]{"st", "llo wo", null, ""}, __.__("test", "hello world", null, "").substring(2, 8).toList().toArray()); } diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs index 91df6644f7a..d232bff33d0 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs @@ -1938,9 +1938,9 @@ public GraphTraversal Subgraph (string sideEffectKey) /// /// Adds the subgraph step to this . /// - public GraphTraversal Substring (int startIndex, int length) + public GraphTraversal Substring (int startIndex, int endIndex) { - Bytecode.AddStep("substring", startIndex, length); + Bytecode.AddStep("substring", startIndex, endIndex); return Wrap(this); } diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs index a0fdae1632a..070644c89d6 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs @@ -1381,9 +1381,9 @@ public static GraphTraversal Subgraph(string sideEffectKey) /// /// Spawns a and adds the substring step to that traversal. /// - public static GraphTraversal Substring(int startIndex, int length) + public static GraphTraversal Substring(int startIndex, int endIndex) { - return new GraphTraversal().Substring(startIndex, length); + return new GraphTraversal().Substring(startIndex, endIndex); } /// diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 76669ca1e79..be261ba622a 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -1005,11 +1005,14 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values("name").Split(null)}}, {"g_V_hasLabelXpersonX_valueXnameX_splitXaX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values("name").Split("a")}}, {"g_injectXthat_this_testX_substringX1_8X", new List, ITraversal>> {(g,p) =>g.Inject("test","hello world",null).Substring(1,8)}}, - {"g_injectXListXa_bXcX_substringX1_1X", new List, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Substring(1,1)}}, + {"g_injectXListXa_bXcX_substringX1_2X", new List, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Substring(1,2)}}, {"g_V_hasLabelXpersonX_valueXnameX_substringX2X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values("name").Substring(2)}}, - {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_3X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values("name").Substring(1,3)}}, + {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_4X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values("name").Substring(1,4)}}, {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_0X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values("name").Substring(1,0)}}, {"g_V_hasLabelXpersonX_valueXnameX_substringXneg3X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values("name").Substring(-3)}}, + {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_neg1X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values("name").Substring(1,-1)}}, + {"g_V_hasLabelXsoftwareX_valueXnameX_substringXneg4_2X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values("name").Substring(-4,2)}}, + {"g_V_hasLabelXsoftwareX_valueXnameX_substringXneg3_neg1X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values("name").Substring(-3,-1)}}, {"g_V_age_sum", new List, ITraversal>> {(g,p) =>g.V().Values("age").Sum()}}, {"g_V_foo_sum", new List, ITraversal>> {(g,p) =>g.V().Values("foo").Sum()}}, {"g_V_age_fold_sumXlocalX", new List, ITraversal>> {(g,p) =>g.V().Values("age").Fold().Sum(Scope.Local)}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index 04edf87036a..9b94834b330 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -976,11 +976,14 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_hasLabelXpersonX_valueXnameX_splitXnullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").Split(nil)}}, "g_V_hasLabelXpersonX_valueXnameX_splitXaX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").Split("a")}}, "g_injectXthat_this_testX_substringX1_8X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("test", "hello world", nil).Substring(1, 8)}}, - "g_injectXListXa_bXcX_substringX1_1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Substring(1, 1)}}, + "g_injectXListXa_bXcX_substringX1_2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Substring(1, 2)}}, "g_V_hasLabelXpersonX_valueXnameX_substringX2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Substring(2)}}, - "g_V_hasLabelXsoftwareX_valueXnameX_substringX1_3X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Substring(1, 3)}}, + "g_V_hasLabelXsoftwareX_valueXnameX_substringX1_4X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Substring(1, 4)}}, "g_V_hasLabelXsoftwareX_valueXnameX_substringX1_0X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Substring(1, 0)}}, "g_V_hasLabelXpersonX_valueXnameX_substringXneg3X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").Substring(-3)}}, + "g_V_hasLabelXsoftwareX_valueXnameX_substringX1_neg1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Substring(1, -1)}}, + "g_V_hasLabelXsoftwareX_valueXnameX_substringXneg4_2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Substring(-4, 2)}}, + "g_V_hasLabelXsoftwareX_valueXnameX_substringXneg3_neg1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Substring(-3, -1)}}, "g_V_age_sum": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Sum()}}, "g_V_foo_sum": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("foo").Sum()}}, "g_V_age_fold_sumXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Fold().Sum(gremlingo.Scope.Local)}}, diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js index 63087102dc2..97557d86e73 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js @@ -996,11 +996,14 @@ const gremlins = { g_V_hasLabelXpersonX_valueXnameX_splitXnullX: [function({g}) { return g.V().hasLabel("person").values("name").split(null) }], g_V_hasLabelXpersonX_valueXnameX_splitXaX: [function({g}) { return g.V().hasLabel("person").values("name").split("a") }], g_injectXthat_this_testX_substringX1_8X: [function({g}) { return g.inject("test","hello world",null).substring(1,8) }], - g_injectXListXa_bXcX_substringX1_1X: [function({g, xx1}) { return g.inject(xx1).substring(1,1) }], + g_injectXListXa_bXcX_substringX1_2X: [function({g, xx1}) { return g.inject(xx1).substring(1,2) }], g_V_hasLabelXpersonX_valueXnameX_substringX2X: [function({g}) { return g.V().hasLabel("software").values("name").substring(2) }], - g_V_hasLabelXsoftwareX_valueXnameX_substringX1_3X: [function({g}) { return g.V().hasLabel("software").values("name").substring(1,3) }], + g_V_hasLabelXsoftwareX_valueXnameX_substringX1_4X: [function({g}) { return g.V().hasLabel("software").values("name").substring(1,4) }], g_V_hasLabelXsoftwareX_valueXnameX_substringX1_0X: [function({g}) { return g.V().hasLabel("software").values("name").substring(1,0) }], g_V_hasLabelXpersonX_valueXnameX_substringXneg3X: [function({g}) { return g.V().hasLabel("person").values("name").substring(-3) }], + g_V_hasLabelXsoftwareX_valueXnameX_substringX1_neg1X: [function({g}) { return g.V().hasLabel("software").values("name").substring(1,-1) }], + g_V_hasLabelXsoftwareX_valueXnameX_substringXneg4_2X: [function({g}) { return g.V().hasLabel("software").values("name").substring(-4,2) }], + g_V_hasLabelXsoftwareX_valueXnameX_substringXneg3_neg1X: [function({g}) { return g.V().hasLabel("software").values("name").substring(-3,-1) }], g_V_age_sum: [function({g}) { return g.V().values("age").sum() }], g_V_foo_sum: [function({g}) { return g.V().values("foo").sum() }], g_V_age_fold_sumXlocalX: [function({g}) { return g.V().values("age").fold().sum(Scope.local) }], diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index ed106ef94d0..a03e0a8bd32 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -978,11 +978,14 @@ 'g_V_hasLabelXpersonX_valueXnameX_splitXnullX': [(lambda g:g.V().hasLabel('person').name.split(None))], 'g_V_hasLabelXpersonX_valueXnameX_splitXaX': [(lambda g:g.V().hasLabel('person').name.split('a'))], 'g_injectXthat_this_testX_substringX1_8X': [(lambda g:g.inject('test','hello world',None).substring(1,8))], - 'g_injectXListXa_bXcX_substringX1_1X': [(lambda g, xx1=None:g.inject(xx1).substring(1,1))], + 'g_injectXListXa_bXcX_substringX1_2X': [(lambda g, xx1=None:g.inject(xx1).substring(1,2))], 'g_V_hasLabelXpersonX_valueXnameX_substringX2X': [(lambda g:g.V().hasLabel('software').name.substring(2))], - 'g_V_hasLabelXsoftwareX_valueXnameX_substringX1_3X': [(lambda g:g.V().hasLabel('software').name.substring(1,3))], + 'g_V_hasLabelXsoftwareX_valueXnameX_substringX1_4X': [(lambda g:g.V().hasLabel('software').name.substring(1,4))], 'g_V_hasLabelXsoftwareX_valueXnameX_substringX1_0X': [(lambda g:g.V().hasLabel('software').name.substring(1,0))], 'g_V_hasLabelXpersonX_valueXnameX_substringXneg3X': [(lambda g:g.V().hasLabel('person').name.substring(-3))], + 'g_V_hasLabelXsoftwareX_valueXnameX_substringX1_neg1X': [(lambda g:g.V().hasLabel('software').name.substring(1,-1))], + 'g_V_hasLabelXsoftwareX_valueXnameX_substringXneg4_2X': [(lambda g:g.V().hasLabel('software').name.substring(-4,2))], + 'g_V_hasLabelXsoftwareX_valueXnameX_substringXneg3_neg1X': [(lambda g:g.V().hasLabel('software').name.substring(-3,-1))], 'g_V_age_sum': [(lambda g:g.V().age.sum_())], 'g_V_foo_sum': [(lambda g:g.V().foo.sum_())], 'g_V_age_fold_sumXlocalX': [(lambda g:g.V().age.fold().sum_(Scope.local))], diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Substring.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Substring.feature index 7aac365d6ca..48b77803557 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Substring.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Substring.feature @@ -29,16 +29,16 @@ Feature: Step - substring() Then the result should be unordered | result | | est | - | ello wor | + | ello wo | | null | @GraphComputerVerificationInjectionNotSupported - Scenario: g_injectXListXa_bXcX_substringX1_1X + Scenario: g_injectXListXa_bXcX_substringX1_2X Given the empty graph And using the parameter xx1 defined as "l[aa,bb]" And the traversal of """ - g.inject(xx1).substring(1, 1) + g.inject(xx1).substring(1, 2) """ When iterated to list Then the traversal will raise an error with message containing text of "The substring() step can only take string as argument" @@ -55,11 +55,11 @@ Feature: Step - substring() | p | | pple | - Scenario: g_V_hasLabelXsoftwareX_valueXnameX_substringX1_3X + Scenario: g_V_hasLabelXsoftwareX_valueXnameX_substringX1_4X Given the modern graph And the traversal of """ - g.V().hasLabel("software").values("name").substring(1, 3) + g.V().hasLabel("software").values("name").substring(1, 4) """ When iterated to list Then the result should be unordered @@ -92,3 +92,39 @@ Feature: Step - substring() | osh | | das | | ter | + +Scenario: g_V_hasLabelXsoftwareX_valueXnameX_substringX1_neg1X + Given the modern graph + And the traversal of + """ + g.V().hasLabel("software").values("name").substring(1, -1) + """ + When iterated to list + Then the result should be unordered + | result | + | o | + | ippl | + +Scenario: g_V_hasLabelXsoftwareX_valueXnameX_substringXneg4_2X + Given the modern graph + And the traversal of + """ + g.V().hasLabel("software").values("name").substring(-4, 2) + """ + When iterated to list + Then the result should be unordered + | result | + | lo | + | str[] | + +Scenario: g_V_hasLabelXsoftwareX_valueXnameX_substringXneg3_neg1X + Given the modern graph + And the traversal of + """ + g.V().hasLabel("software").values("name").substring(-3, -1) + """ + When iterated to list + Then the result should be unordered + | result | + | lo | + | pl | \ No newline at end of file