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

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


The following commit(s) were added to refs/heads/3.8-dev by this push:
     new f422d780ca TINKERPOP-3150 Prevent sample step from having multiple by 
modulators… (#3114)
f422d780ca is described below

commit f422d780ca46b37c48d4dae0fbd25d4dbc24f8d2
Author: Peter Tribe <peter.tr...@improving.com>
AuthorDate: Mon May 12 17:34:02 2025 -0600

    TINKERPOP-3150 Prevent sample step from having multiple by modulators… 
(#3114)
    
    * TINKERPOP-3150 Prevent sample step from having multiple by modulators
---
 CHANGELOG.asciidoc                                            |  1 +
 .../process/traversal/step/filter/SampleGlobalStep.java       | 11 +++++++++--
 .../process/traversal/step/filter/SampleGlobalStepTest.java   |  6 ++++++
 .../test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs       |  1 +
 gremlin-go/driver/cucumber/gremlin.go                         |  1 +
 .../javascript/gremlin-javascript/test/cucumber/gremlin.js    |  1 +
 gremlin-python/src/main/python/radish/gremlin.py              |  1 +
 .../tinkerpop/gremlin/test/features/filter/Sample.feature     |  9 +++++++++
 8 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 7271c32e2f..2af24acb1e 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -83,6 +83,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 [[release-3-7-4]]
 === TinkerPop 3.7.4 (NOT OFFICIALLY RELEASED YET)
 
+* Changed sample step to not allow multiple by modulators.
 * Added log entry in `WsAndHttpChannelizerHandler` to catch general errors 
that escape the handlers.
 * Added a `MessageSizeEstimator` implementation to cover `Frame` allowing 
Gremlin Server to better estimate message sizes for the direct buffer.
 * Improved logging around triggers of the `writeBufferHighWaterMark` so that 
they occur more than once but do not excessively fill the logs.
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
index 82d566f4b2..fbc3e8d66b 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
@@ -43,7 +43,8 @@ import java.util.Set;
  */
 public final class SampleGlobalStep<S> extends CollectingBarrierStep<S> 
implements TraversalParent, ByModulating, Seedable {
 
-    private Traversal.Admin<S, Number> probabilityTraversal = new 
ConstantTraversal<>(1.0d);
+    private boolean detectedMutipleBy = false;
+    private Traversal.Admin<S, Number> probabilityTraversal = new 
ConstantTraversal<>(1.0d);;
     private final int amountToSample;
     private final Random random = new Random();
 
@@ -68,13 +69,18 @@ public final class SampleGlobalStep<S> extends 
CollectingBarrierStep<S> implemen
 
     @Override
     public void modulateBy(final Traversal.Admin<?, ?> probabilityTraversal) {
+        if (this.detectedMutipleBy)
+            throw new IllegalStateException("Sample step can only have one by 
modulator");
+        this.detectedMutipleBy = true;
         this.probabilityTraversal = this.integrateChild(probabilityTraversal);
     }
 
     @Override
     public void replaceLocalChild(final Traversal.Admin<?, ?> oldTraversal, 
final Traversal.Admin<?, ?> newTraversal) {
-        if (null != this.probabilityTraversal && 
this.probabilityTraversal.equals(oldTraversal))
+        if (null != this.probabilityTraversal && 
this.probabilityTraversal.equals(oldTraversal)) {
+            this.detectedMutipleBy = true;
             this.probabilityTraversal = this.integrateChild(newTraversal);
+        }
     }
 
     @Override
@@ -155,6 +161,7 @@ public final class SampleGlobalStep<S> extends 
CollectingBarrierStep<S> implemen
     public SampleGlobalStep<S> clone() {
         final SampleGlobalStep<S> clone = (SampleGlobalStep<S>) super.clone();
         clone.probabilityTraversal = this.probabilityTraversal.clone();
+        clone.detectedMutipleBy = this.detectedMutipleBy;
         return clone;
     }
 
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStepTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStepTest.java
index 3d032eefc4..eebea0a40f 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStepTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStepTest.java
@@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Scope;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
+import org.apache.tinkerpop.gremlin.structure.T;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -59,4 +60,9 @@ public class SampleGlobalStepTest extends StepTest {
             assertTrue(list.containsAll(result));
         }
     }
+
+    @Test(expected = IllegalStateException.class)
+    public void shouldThrowForMultipleByModulators() {
+        __.V().sample(1).by("age").by(T.id);
+    }
 }
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index bf205d5068..336e367b65 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -342,6 +342,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"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_V_sampleX1X_byXageX_byXT_idX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Sample(1).By("age").By(T.Id)}}, 
                {"g_V_rangeX2_1X", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Range<object>(2, 1)}}, 
                {"g_V_rangeX3_2X", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Range<object>(3, 2)}}, 
                {"g_E_sampleX1X", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.E().Sample(1)}}, 
diff --git a/gremlin-go/driver/cucumber/gremlin.go 
b/gremlin-go/driver/cucumber/gremlin.go
index 8d6dc28d90..6cb740ea7b 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -312,6 +312,7 @@ var translationMap = map[string][]func(g 
*gremlingo.GraphTraversalSource, p map[
     "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_V_sampleX1X_byXageX_byXT_idX": {func(g *gremlingo.GraphTraversalSource, 
p map[string]interface{}) *gremlingo.GraphTraversal {return 
g.V().Sample(1).By("age").By(gremlingo.T.Id)}}, 
     "g_V_rangeX2_1X": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Range(2, 1)}}, 
     "g_V_rangeX3_2X": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Range(3, 2)}}, 
     "g_E_sampleX1X": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.E().Sample(1)}}, 
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 3d24f23ace..fc9e6b0f05 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
@@ -342,6 +342,7 @@ const gremlins = {
     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_V_sampleX1X_byXageX_byXT_idX: [function({g}) { return 
g.V().sample(1).by("age").by(T.id) }], 
     g_V_rangeX2_1X: [function({g}) { return g.V().range(2, 1) }], 
     g_V_rangeX3_2X: [function({g}) { return g.V().range(3, 2) }], 
     g_E_sampleX1X: [function({g}) { return g.E().sample(1) }], 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py 
b/gremlin-python/src/main/python/radish/gremlin.py
index 438e94a04c..647c50a309 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -315,6 +315,7 @@ world.gremlins = {
     'g_V_hasLabelXpersonX_order_byXageX_valuesXnameX_skipX1X': [(lambda 
g:g.V().has_label('person').order().by('age').values('name').skip(1))], 
     'g_VX1X_valuesXageX_rangeXlocal_20_30X': [(lambda g, 
vid1=None:g.V(vid1).values('age').range_(Scope.local, 20, 30))], 
     'g_V_mapXin_hasIdX1XX_limitX2X_valuesXnameX': [(lambda g, 
vid1=None:g.V().map(__.in_().has_id(vid1)).limit(2).values('name'))], 
+    'g_V_sampleX1X_byXageX_byXT_idX': [(lambda 
g:g.V().sample(1).by('age').by(T.id_))], 
     'g_V_rangeX2_1X': [(lambda g:g.V().range_(2, 1))], 
     'g_V_rangeX3_2X': [(lambda g:g.V().range_(3, 2))], 
     'g_E_sampleX1X': [(lambda g:g.E().sample(1))], 
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Sample.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Sample.feature
index c18e01a8b5..3706652ae7 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Sample.feature
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Sample.feature
@@ -18,6 +18,15 @@
 @StepClassFilter @StepSample
 Feature: Step - sample()
 
+  Scenario: g_V_sampleX1X_byXageX_byXT_idX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().sample(1).by("age").by(T.id)
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"Sample step can only have one by modulator"
+
   Scenario: g_E_sampleX1X
     Given the modern graph
     And the traversal of

Reply via email to