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:07 UTC

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

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