You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2024/02/06 13:27:06 UTC

(tinkerpop) branch master updated (963bece33c -> b282194056)

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

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


    from 963bece33c Loosen error message validation a bit by ignoring case.
     add a2b2550838 TINKERPOP-3045: EarlyLimitStrategy is too aggresive to promote Limit and thus causing incorrect results https://issues.apache.org/jira/browse/TINKERPOP-3045
     new 0d119be3bb Follow-on to #2475 to fixup compile/test issues CTR
     new 6bfb0365f1 Merge branch '3.6-dev' into 3.7-dev
     new b282194056 Merge branch '3.7-dev'

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.asciidoc                                 |   1 +
 .../strategy/optimization/EarlyLimitStrategy.java  |   6 +-
 .../strategy/util/StepOutputArityPredictor.java    | 361 +++++++++++++++++++++
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |   1 +
 gremlin-go/driver/cucumber/gremlin.go              |   1 +
 .../gremlin-javascript/test/cucumber/gremlin.js    |   1 +
 gremlin-python/src/main/python/radish/gremlin.py   |   1 +
 .../gremlin/test/features/filter/Range.feature     |  15 +-
 8 files changed, 385 insertions(+), 2 deletions(-)
 create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/util/StepOutputArityPredictor.java


(tinkerpop) 01/03: Follow-on to #2475 to fixup compile/test issues CTR

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0d119be3bbb09cfc73536fc0a2f5337d9011667e
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Tue Feb 6 07:42:22 2024 -0500

    Follow-on to #2475 to fixup compile/test issues CTR
---
 CHANGELOG.asciidoc                                 |  1 +
 .../strategy/util/StepOutputArityPredictor.java    | 95 ++++++++++++++--------
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  1 +
 gremlin-go/driver/cucumber/gremlin.go              |  1 +
 .../gremlin-javascript/test/cucumber/gremlin.js    |  1 +
 gremlin-python/src/main/python/radish/gremlin.py   |  1 +
 .../gremlin/test/features/filter/Range.feature     |  5 +-
 7 files changed, 71 insertions(+), 34 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 8a77efd16c..e886f0a7ae 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,6 +31,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Fixed bug in `JavaTranslator` where `has(String, null)` could call `has(String, Traversal)` to generate an error.
 * Fixed issue where server errors weren't being properly parsed when sending bytecode over HTTP.
 * Improved bulkset contains check for elements if all elements in bulkset are of the same type
+* Fixed bug in `EarlyLimitStrategy` which was too aggressive when promoting `limit()` before `map()`.
 
 [[release-3-6-6]]
 === TinkerPop 3.6.6 (November 20, 2023)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/util/StepOutputArityPredictor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/util/StepOutputArityPredictor.java
index 6da09f6ae8..1e0157bd51 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/util/StepOutputArityPredictor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/util/StepOutputArityPredictor.java
@@ -18,8 +18,9 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.strategy.util;
 
-
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
@@ -79,12 +80,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.ReducingBarrierS
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.T;
 
-import com.google.common.collect.ImmutableMap;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NonNull;
-
 /**
  * Output Arity Predictor for Tinkerpop steps
  */
@@ -236,29 +231,34 @@ public class StepOutputArityPredictor {
     final private static Map<String, Function<Traversal, Arity>> SPECIAL_TRAVERSAL_TO_ARITY_FUNCTION_MAP;
 
     static {
-        final ImmutableMap.Builder<String, Function<Step, Arity>> builder1 = ImmutableMap.builder();
-        STEP_CLASSES_WITH_DEFINITELY_SINGULAR_ARITY_BEHAVIOR.forEach(
-                elem -> builder1.put(elem.getSimpleName(), RETURN_DEFINITELY_SINGLE_ARITY_FOR_STEP_INPUT));
+        final Map<String, Function<Step, Arity>> tempMap1 = new HashMap<>();
+        for (Class elem : STEP_CLASSES_WITH_DEFINITELY_SINGULAR_ARITY_BEHAVIOR) {
+            tempMap1.put(elem.getSimpleName(), RETURN_DEFINITELY_SINGLE_ARITY_FOR_STEP_INPUT);
+        }
 
-        STEP_CLASSES_WITH_OPTIONAL_SINGULAR_ARITY_BEHAVIOR.forEach(
-                elem -> builder1.put(elem.getSimpleName(), RETURN_MAY_BE_SINGLE_ARITY_FOR_STEP_INPUT));
+        for (Class elem : STEP_CLASSES_WITH_OPTIONAL_SINGULAR_ARITY_BEHAVIOR) {
+            tempMap1.put(elem.getSimpleName(), RETURN_MAY_BE_SINGLE_ARITY_FOR_STEP_INPUT);
+        }
 
-        builder1.put(EdgeVertexStep.class.getSimpleName(), StepOutputArityPredictor::wouldEdgeVertexStepHaveSingleResult);
-        builder1.put(TraversalMapStep.class.getSimpleName(), StepOutputArityPredictor::getOutputArityBehaviorForTraversalMapStep);
+        tempMap1.put(EdgeVertexStep.class.getSimpleName(), StepOutputArityPredictor::wouldEdgeVertexStepHaveSingleResult);
+        tempMap1.put(TraversalMapStep.class.getSimpleName(), StepOutputArityPredictor::getOutputArityBehaviorForTraversalMapStep);
 
-        STEP_TO_ARITY_FUNCTION_MAP = builder1.build();
+        STEP_TO_ARITY_FUNCTION_MAP = Collections.unmodifiableMap(tempMap1);
 
-        final ImmutableMap.Builder<String, Function<Traversal, Arity>> builder2 = ImmutableMap.builder();
-        builder2.put(ValueTraversal.class.getSimpleName(), StepOutputArityPredictor::getOutputArityBehaviorForValueTraversal);
-        builder2.put(TokenTraversal.class.getSimpleName(), StepOutputArityPredictor::getOutputArityBehaviorForTokenTraversal);
-        builder2.put(IdentityTraversal.class.getSimpleName(), RETURN_DEFINITELY_SINGLE_ARITY_FOR_TRAVERSAL_INPUT);
-        builder2.put(ColumnTraversal.class.getSimpleName(), RETURN_DEFINITELY_SINGLE_ARITY_FOR_TRAVERSAL_INPUT);
-        SPECIAL_TRAVERSAL_TO_ARITY_FUNCTION_MAP = builder2.build();
-    }
+        final Map<String, Function<Traversal, Arity>> tempMap2 = new HashMap<>();
+        tempMap2.put(ValueTraversal.class.getSimpleName(), StepOutputArityPredictor::getOutputArityBehaviorForValueTraversal);
+        tempMap2.put(TokenTraversal.class.getSimpleName(), StepOutputArityPredictor::getOutputArityBehaviorForTokenTraversal);
+        tempMap2.put(IdentityTraversal.class.getSimpleName(), RETURN_DEFINITELY_SINGLE_ARITY_FOR_TRAVERSAL_INPUT);
+        tempMap2.put(ColumnTraversal.class.getSimpleName(), RETURN_DEFINITELY_SINGLE_ARITY_FOR_TRAVERSAL_INPUT);
 
+        SPECIAL_TRAVERSAL_TO_ARITY_FUNCTION_MAP = Collections.unmodifiableMap(tempMap2);
+    }
 
+    private static Arity getStepOutputArity(final Step step, final Arity inputArity) {
+        if (step == null) {
+            throw new NullPointerException("step is marked non-null but is null");
+        }
 
-    private static Arity getStepOutputArity(@NonNull final Step step, final Arity inputArity) {
         final String stepName = step.getClass().getSimpleName();
         final Arity result;
         // Step 1. Check if it is a STEP_TO_ARITY_FUNCTION_MAP member.
@@ -275,12 +275,19 @@ public class StepOutputArityPredictor {
     }
 
 
-    private static Arity getOutputArity(@NonNull final Traversal<?, ?> traversal) {
-        return getOutputArity(traversal, Arity.DEFINITELY_SINGULAR);
+    private static Arity getOutputArity(final Traversal<?, ?> traversal) {
+        if (traversal == null) {
+            throw new NullPointerException("traversal is marked non-null but is null");
+        }
 
+        return getOutputArity(traversal, Arity.DEFINITELY_SINGULAR);
     }
 
-    private static Arity getOutputArity(@NonNull final Traversal<?, ?> traversal, final Arity inputArity) {
+    private static Arity getOutputArity(final Traversal<?, ?> traversal, final Arity inputArity) {
+        if (traversal == null) {
+            throw new NullPointerException("traversal is marked non-null but is null");
+        }
+
         Arity result = inputArity;
         if (traversal instanceof AbstractLambdaTraversal<?, ?>) {
             if (SPECIAL_TRAVERSAL_TO_ARITY_FUNCTION_MAP.containsKey(traversal.getClass().getSimpleName())) {
@@ -297,32 +304,56 @@ public class StepOutputArityPredictor {
         return result;
     }
 
-    public static boolean hasSingularOutputArity(@NonNull final Traversal<?,?> traversal) {
+    public static boolean hasSingularOutputArity(final Traversal<?,?> traversal) {
+        if (traversal == null) {
+            throw new NullPointerException("traversal is marked non-null but is null");
+        }
+
         final Arity resultArity = getOutputArity(traversal);
         return resultArity.getPriority()==1;
     }
 
-    public static boolean hasAlwaysBoundResult(@NonNull final Traversal<?,?> traversal) {
+    public static boolean hasAlwaysBoundResult(final Traversal<?,?> traversal) {
+        if (traversal == null) {
+            throw new NullPointerException("traversal is marked non-null but is null");
+        }
+
         final Arity resultArity = getOutputArity(traversal);
         return resultArity.equals(Arity.DEFINITELY_SINGULAR);
     }
 
-    public static boolean hasMultiOutputArity(@NonNull final Traversal<?,?> traversal) {
+    public static boolean hasMultiOutputArity(final Traversal<?,?> traversal) {
+        if (traversal == null) {
+            throw new NullPointerException("traversal is marked non-null but is null");
+        }
+
         final Arity resultArity = getOutputArity(traversal);
         return resultArity.getPriority()!=1;
     }
 
-    public static boolean hasSingularOutputArity(@NonNull final Step<?,?> step) {
+    public static boolean hasSingularOutputArity(final Step<?,?> step) {
+        if (step == null) {
+            throw new NullPointerException("step is marked non-null but is null");
+        }
+
         final Arity resultArity = getStepOutputArity(step, Arity.DEFINITELY_SINGULAR);
         return resultArity.getPriority()==1;
     }
 
-    public static boolean hasAlwaysBoundResult(@NonNull final Step<?,?> step) {
+    public static boolean hasAlwaysBoundResult(final Step<?,?> step) {
+        if (step == null) {
+            throw new NullPointerException("step is marked non-null but is null");
+        }
+
         final Arity resultArity = getStepOutputArity(step, Arity.DEFINITELY_SINGULAR);
         return resultArity.equals(Arity.DEFINITELY_SINGULAR);
     }
 
-    public static boolean hasMultiOutputArity(@NonNull final Step<?,?> step) {
+    public static boolean hasMultiOutputArity(final Step<?,?> step) {
+        if (step == null) {
+            throw new NullPointerException("step is marked non-null but is null");
+        }
+
         final Arity resultArity = getStepOutputArity(step, Arity.DEFINITELY_SINGULAR);
         return resultArity.getPriority()!=1;
     }
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index ce4b3d75ab..5fc05ee05b 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -301,6 +301,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_asXaX_in_asXaX_in_asXaX_selectXmixed_aX_byXunfold_valuesXnameX_foldX_limitXlocal_2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").In().As("a").In().As("a").Select<object>(Pop.Mixed,"a").By(__.Unfold<object>().Values<object>("name").Fold()).Limit<object>(Scope.Local,2)}}, 
                {"g_V_hasLabelXpersonX_order_byXageX_valuesXnameX_skipX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Order().By("age").Values<object>("name").Skip<object>(1)}}, 
                {"g_VX1X_valuesXageX_rangeXlocal_20_30X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Range<object>(Scope.Local,20,30)}}, 
+               {"g_V_mapXin_hasIdX1XX_limitX2X_valuesXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Map<object>(__.In().HasId(p["vid1"])).Limit<object>(2).Values<object>("name")}}, 
                {"g_E_sampleX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.E().Sample(1)}}, 
                {"g_E_sampleX2X_byXweightX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.E().Sample(2).By("weight")}}, 
                {"g_V_localXoutE_sampleX1X_byXweightXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.OutE().Sample(1).By("weight"))}}, 
diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go
index 3a9378226d..801cafa8c5 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -272,6 +272,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
     "g_V_asXaX_in_asXaX_in_asXaX_selectXmixed_aX_byXunfold_valuesXnameX_foldX_limitXlocal_2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().As("a").In().As("a").In().As("a").Select(gremlingo.Pop.Mixed, "a").By(gremlingo.T__.Unfold().Values("name").Fold()).Limit(gremlingo.Scope.Local, 2)}}, 
     "g_V_hasLabelXpersonX_order_byXageX_valuesXnameX_skipX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Order().By("age").Values("name").Skip(1)}}, 
     "g_VX1X_valuesXageX_rangeXlocal_20_30X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Range(gremlingo.Scope.Local, 20, 30)}}, 
+    "g_V_mapXin_hasIdX1XX_limitX2X_valuesXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Map(gremlingo.T__.In().HasId(p["vid1"])).Limit(2).Values("name")}}, 
     "g_E_sampleX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().Sample(1)}}, 
     "g_E_sampleX2X_byXweightX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().Sample(2).By("weight")}}, 
     "g_V_localXoutE_sampleX1X_byXweightXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.OutE().Sample(1).By("weight"))}}, 
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 6b1fba4fb3..94e701723a 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
@@ -291,6 +291,7 @@ const gremlins = {
     g_V_asXaX_in_asXaX_in_asXaX_selectXmixed_aX_byXunfold_valuesXnameX_foldX_limitXlocal_2X: [function({g}) { return g.V().as("a").in_().as("a").in_().as("a").select(Pop.mixed,"a").by(__.unfold().values("name").fold()).limit(Scope.local,2) }], 
     g_V_hasLabelXpersonX_order_byXageX_valuesXnameX_skipX1X: [function({g}) { return g.V().hasLabel("person").order().by("age").values("name").skip(1) }], 
     g_VX1X_valuesXageX_rangeXlocal_20_30X: [function({g, vid1}) { return g.V(vid1).values("age").range(Scope.local,20,30) }], 
+    g_V_mapXin_hasIdX1XX_limitX2X_valuesXnameX: [function({g, vid1}) { return g.V().map(__.in_().hasId(vid1)).limit(2).values("name") }], 
     g_E_sampleX1X: [function({g}) { return g.E().sample(1) }], 
     g_E_sampleX2X_byXweightX: [function({g}) { return g.E().sample(2).by("weight") }], 
     g_V_localXoutE_sampleX1X_byXweightXX: [function({g}) { return g.V().local(__.outE().sample(1).by("weight")) }], 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 863c0bed78..eebd6d69d2 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -273,6 +273,7 @@ world.gremlins = {
     'g_V_asXaX_in_asXaX_in_asXaX_selectXmixed_aX_byXunfold_valuesXnameX_foldX_limitXlocal_2X': [(lambda g:g.V().as_('a').in_().as_('a').in_().as_('a').select(Pop.mixed,'a').by(__.unfold().name.fold()).limit(Scope.local,2))], 
     'g_V_hasLabelXpersonX_order_byXageX_valuesXnameX_skipX1X': [(lambda g:g.V().hasLabel('person').order().by('age').name.skip(1))], 
     'g_VX1X_valuesXageX_rangeXlocal_20_30X': [(lambda g, vid1=None:g.V(vid1).age.range_(Scope.local,20,30))], 
+    'g_V_mapXin_hasIdX1XX_limitX2X_valuesXnameX': [(lambda g, vid1=None:g.V().map(__.in_().hasId(vid1))[0:2].name)], 
     'g_E_sampleX1X': [(lambda g:g.E().sample(1))], 
     'g_E_sampleX2X_byXweightX': [(lambda g:g.E().sample(2).by('weight'))], 
     'g_V_localXoutE_sampleX1X_byXweightXX': [(lambda g:g.V().local(__.outE().sample(1).by('weight')))], 
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Range.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Range.feature
index 1087abb7d4..153e7b3ac9 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Range.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Range.feature
@@ -282,11 +282,12 @@ Feature: Step - range()
       | result |
       | d[29].i |
 
-  Scenario: g_V_mapXinX_limitX2X_valuesXnameX
+  Scenario: g_V_mapXin_hasIdX1XX_limitX2X_valuesXnameX
     Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
     And the traversal of
       """
-      g.V().map(__.in().hasId("1")).limit(2).values("name")
+      g.V().map(__.in().hasId(vid1)).limit(2).values("name")
       """
     When iterated to list
     Then the result should be unordered


(tinkerpop) 02/03: Merge branch '3.6-dev' into 3.7-dev

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 6bfb0365f14b5c69ca7af3397cd7b36ef91a7197
Merge: d3b7aa942e 0d119be3bb
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Tue Feb 6 07:43:39 2024 -0500

    Merge branch '3.6-dev' into 3.7-dev

 CHANGELOG.asciidoc                                 |   1 +
 .../strategy/optimization/EarlyLimitStrategy.java  |   6 +-
 .../strategy/util/StepOutputArityPredictor.java    | 361 +++++++++++++++++++++
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |   1 +
 gremlin-go/driver/cucumber/gremlin.go              |   1 +
 .../gremlin-javascript/test/cucumber/gremlin.js    |   1 +
 gremlin-python/src/main/python/radish/gremlin.py   |   1 +
 .../gremlin/test/features/filter/Range.feature     |  15 +-
 8 files changed, 385 insertions(+), 2 deletions(-)



(tinkerpop) 03/03: Merge branch '3.7-dev'

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b2821940560dc7c5e168590164c1bfb347b4a69e
Merge: 963bece33c 6bfb0365f1
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Tue Feb 6 07:43:57 2024 -0500

    Merge branch '3.7-dev'

 CHANGELOG.asciidoc                                 |   1 +
 .../strategy/optimization/EarlyLimitStrategy.java  |   6 +-
 .../strategy/util/StepOutputArityPredictor.java    | 361 +++++++++++++++++++++
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |   1 +
 gremlin-go/driver/cucumber/gremlin.go              |   1 +
 .../gremlin-javascript/test/cucumber/gremlin.js    |   1 +
 gremlin-python/src/main/python/radish/gremlin.py   |   1 +
 .../gremlin/test/features/filter/Range.feature     |  15 +-
 8 files changed, 385 insertions(+), 2 deletions(-)