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

kenhuuu pushed a commit to branch TINKERPOP-3196
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 9b8cf3e02e72d034d686bae396b4c3e4c31dd839
Author: Ken Hu <[email protected]>
AuthorDate: Sun Oct 19 21:15:50 2025 -0700

    TINKERPOP-3196 Split bulked traversers for LocalStep
    
    Local was behaving as a per traverser traversal when it should be a per
    object traversal. This means that there should be no bulked traversers
    that are added as starts to local traversals.
---
 .../process/traversal/step/branch/LocalStep.java   | 22 +++++-
 .../gremlin/test/features/branch/Local.feature     | 89 +++++++++++++++++++++-
 2 files changed, 107 insertions(+), 4 deletions(-)

diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/LocalStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/LocalStep.java
index f0f08a5ee4..ba9475f784 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/LocalStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/LocalStep.java
@@ -23,6 +23,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser;
 import 
org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
@@ -38,6 +39,7 @@ public final class LocalStep<S, E> extends AbstractStep<S, E> 
implements Travers
 
     private Traversal.Admin<S, E> localTraversal;
     private boolean first = true;
+    private Traverser.Admin<S> currentStart = EmptyTraverser.instance();
 
     public LocalStep(final Traversal.Admin traversal, final Traversal.Admin<S, 
E> localTraversal) {
         super(traversal);
@@ -58,20 +60,34 @@ public final class LocalStep<S, E> extends AbstractStep<S, 
E> implements Travers
     protected Traverser.Admin<E> processNextStart() throws 
NoSuchElementException {
         if (this.first) {
             this.first = false;
-            this.localTraversal.addStart(this.starts.next());
+            this.localTraversal.addStart(nextStart());
         }
         while (true) {
             if (this.localTraversal.hasNext())
                 return this.localTraversal.nextTraverser();
-            else if (this.starts.hasNext()) {
+            else if (hasStartRemaining()) {
                 this.localTraversal.reset();
-                this.localTraversal.addStart(this.starts.next());
+                this.localTraversal.addStart(nextStart());
             } else {
                 throw FastNoSuchElementException.instance();
             }
         }
     }
 
+    private boolean hasStartRemaining() {
+        return (currentStart.bulk() > 0L) || this.starts.hasNext();
+    }
+
+    private Traverser.Admin<S> nextStart() throws NoSuchElementException {
+        if (currentStart.bulk() == 0L) {
+            currentStart = starts.next();
+        }
+        final Traverser.Admin<S> split = currentStart.split();
+        split.setBulk(1L);
+        currentStart.setBulk(currentStart.bulk() - 1L);
+        return split;
+    }
+
     @Override
     public void reset() {
         super.reset();
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Local.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Local.feature
index 703e1e549e..b946d1a4a0 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Local.feature
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Local.feature
@@ -183,4 +183,91 @@ Feature: Step - local()
       | m[{"name":"marko","project":"lop"}]   |
       | m[{"name":"josh","project":"lop"}]    |
       | m[{"name":"peter","project":"lop"}]   |
-      | m[{"name":"josh","project":"ripple"}] |
\ No newline at end of file
+      | m[{"name":"josh","project":"ripple"}] |
+
+  # Barrier should show that bulked traversers don't affect local() traversal
+  Scenario: g_V_in_barrier_localXcountX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().in().barrier().local(__.count())
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[1].l |
+      | d[1].l |
+      | d[1].l |
+      | d[1].l |
+      | d[1].l |
+      | d[1].l |
+
+  # Path of traversers isn't hidden in local
+  @GraphComputerVerificationStarGraphExceeded
+  Scenario: g_V_localXout_in_simplePathX_path
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().local(__.out().in().simplePath()).path()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | p[v[josh],v[lop],v[marko]] |
+      | p[v[josh],v[lop],v[peter]] |
+      | p[v[marko],v[lop],v[josh]] |
+      | p[v[marko],v[lop],v[peter]] |
+      | p[v[peter],v[lop],v[marko]] |
+      | p[v[peter],v[lop],v[josh]] |
+  
+  # Traverser's sack value should be carried over from local traversal
+  Scenario: g_withSackX0LX_V_in_barrier_localXsackXsumX_byXageXX_sack
+    Given the modern graph
+    And the traversal of
+      """
+      g.withSack(0L).V().in().barrier().local(__.sack(sum).by("age")).sack()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].l |
+      | d[29].l |
+      | d[29].l |
+      | d[32].l |
+      | d[32].l |
+      | d[35].l |
+
+  # Nested local should return proper local count
+  Scenario: g_V_localXout_localXcountXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().local(__.out().local(__.count()))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[1].l |
+      | d[1].l |
+      | d[1].l |
+      | d[1].l |
+      | d[1].l |
+      | d[1].l |
+
+  # Local should be applied to union's global child
+  Scenario: g_V_unionXoutE_count_localXinE_countXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().union(__.outE().count(), __.local(inE().count()))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[6].l |
+      | d[0].l |
+      | d[1].l |
+      | d[3].l |
+      | d[1].l |
+      | d[1].l |
+      | d[0].l |

Reply via email to