This is an automated email from the ASF dual-hosted git repository.

xiazcy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/master by this push:
     new 81775cbcaf Change substring() to use `startIndex, endIndex` instead of 
`startIndex, length` (#2296)
81775cbcaf is described below

commit 81775cbcafc511df4536cb4cf3ddf7cf470453ee
Author: Ryan Tan <[email protected]>
AuthorDate: Wed Oct 18 15:59:44 2023 -0700

    Change substring() to use `startIndex, endIndex` instead of `startIndex, 
length` (#2296)
---
 docs/src/dev/provider/gremlin-semantics.asciidoc   |  8 ++--
 docs/src/upgrade/release-3.7.x.asciidoc            | 21 +++++-----
 .../traversal/dsl/graph/GraphTraversal.java        | 18 ++++-----
 .../gremlin/process/traversal/dsl/graph/__.java    |  4 +-
 .../process/traversal/step/map/SubstringStep.java  | 43 ++++++++++----------
 .../traversal/step/map/SubstringStepTest.java      | 28 +++++++++----
 .../Process/Traversal/GraphTraversal.cs            |  4 +-
 .../src/Gremlin.Net/Process/Traversal/__.cs        |  4 +-
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  7 +++-
 gremlin-go/driver/cucumber/gremlin.go              |  7 +++-
 .../gremlin-javascript/test/cucumber/gremlin.js    |  7 +++-
 gremlin-python/src/main/python/radish/gremlin.py   |  7 +++-
 .../gremlin/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 74b69e5329..c1825c261d 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 &leq; 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 &leq; 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 75206c7076..40ee470eaa 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 be9674e507..a6257a4eba 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 interface GraphTraversal<S, E> extends 
Traversal<S, E> {
     }
 
     /**
-     * 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 <a 
href="http://tinkerpop.apache.org/docs/${project.version}/reference/#replace-step";
 target="_blank">Reference Documentation - Substring Step</a>
      * @since 3.7.1
      */
-    public default GraphTraversal<S, String> 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<S, String> 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 f41f3506a6..cee80f1dad 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 class __ {
     /**
      * @see GraphTraversal#substring(int, int)
      */
-    public static <A> GraphTraversal<A, String> substring(final int 
startIndex, final int length) {
-        return __.<A>start().substring(startIndex, length);
+    public static <A> GraphTraversal<A, String> substring(final int 
startIndex, final int endIndex) {
+        return __.<A>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 e28f40557e..6638a54c2f 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 @@ import java.util.Set;
 
 /**
  * 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 @@ import java.util.Set;
 public final class SubstringStep<S> extends ScalarMapStep<S, String> {
 
     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 @@ public final class SubstringStep<S> extends 
ScalarMapStep<S, String> {
         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 final class SubstringStep<S> extends 
ScalarMapStep<S, String> {
     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 3af4e06e13..10dc5c288d 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 @@ public class SubstringStepTest extends StepTest {
 
     @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 91df6644f7..d232bff33d 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 @@ namespace Gremlin.Net.Process.Traversal
         /// <summary>
         ///     Adds the subgraph step to this <see 
cref="GraphTraversal{SType, EType}" />.
         /// </summary>
-        public GraphTraversal<TStart, string?> Substring (int startIndex, int 
length)
+        public GraphTraversal<TStart, string?> Substring (int startIndex, int 
endIndex)
         {
-            Bytecode.AddStep("substring", startIndex, length);
+            Bytecode.AddStep("substring", startIndex, endIndex);
             return Wrap<TStart, string?>(this);
         }
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs 
b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
index a0fdae1632..070644c89d 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
@@ -1381,9 +1381,9 @@ namespace Gremlin.Net.Process.Traversal
         /// <summary>
         ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds 
the substring step to that traversal.
         /// </summary>
-        public static GraphTraversal<object, string?> Substring(int 
startIndex, int length)
+        public static GraphTraversal<object, string?> Substring(int 
startIndex, int endIndex)
         {
-            return new GraphTraversal<object, string?>().Substring(startIndex, 
length);
+            return new GraphTraversal<object, string?>().Substring(startIndex, 
endIndex);
         }
 
         /// <summary>
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 9e8f820019..718d60d0f9 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -1010,11 +1010,14 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_hasLabelXpersonX_valueXnameX_splitXnullX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("person").Values<object>("name").Split(null)}}, 
                {"g_V_hasLabelXpersonX_valueXnameX_splitXaX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("person").Values<object>("name").Split("a")}}, 
                {"g_injectXthat_this_testX_substringX1_8X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject<object>("test","hello world",null).Substring(1,8)}}, 
-               {"g_injectXListXa_bXcX_substringX1_1X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject(p["xx1"]).Substring(1,1)}}, 
+               {"g_injectXListXa_bXcX_substringX1_2X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject(p["xx1"]).Substring(1,2)}}, 
                {"g_V_hasLabelXpersonX_valueXnameX_substringX2X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(2)}}, 
-               {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_3X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(1,3)}}, 
+               {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_4X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(1,4)}}, 
                {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_0X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(1,0)}}, 
                {"g_V_hasLabelXpersonX_valueXnameX_substringXneg3X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("person").Values<object>("name").Substring(-3)}}, 
+               {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_neg1X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(1,-1)}}, 
+               {"g_V_hasLabelXsoftwareX_valueXnameX_substringXneg4_2X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(-4,2)}}, 
+               {"g_V_hasLabelXsoftwareX_valueXnameX_substringXneg3_neg1X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(-3,-1)}}, 
                {"g_V_age_sum", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.V().Values<object>("age").Sum<object>()}}, 
                {"g_V_foo_sum", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.V().Values<object>("foo").Sum<object>()}}, 
                {"g_V_age_fold_sumXlocalX", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.V().Values<object>("age").Fold().Sum<object>(Scope.Local)}}, 
diff --git a/gremlin-go/driver/cucumber/gremlin.go 
b/gremlin-go/driver/cucumber/gremlin.go
index 5ff32812a9..b78f9629c0 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -981,11 +981,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 4bed5125c3..2fff0455c8 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
@@ -1001,11 +1001,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 304a12cebc..71665597ae 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -983,11 +983,14 @@ world.gremlins = {
     '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 7aac365d6c..48b7780355 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

Reply via email to