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

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

commit 78b223f8cd6a08beceaf08352e95205059514098
Author: Andrea Child <[email protected]>
AuthorDate: Sun Aug 24 22:56:07 2025 -0700

    Q's first attempt to change limit in repeat to per-iteration counters.
---
 .../process/traversal/step/branch/RepeatStep.java  | 28 +++++++++++++++
 .../traversal/step/filter/RangeGlobalStep.java     | 40 +++++++++++++++++-----
 2 files changed, 60 insertions(+), 8 deletions(-)

diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java
index 17b8a2ef4e..3f27e71e54 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java
@@ -23,6 +23,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 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.ComputerAwareStep;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -67,6 +68,9 @@ public final class RepeatStep<S> extends ComputerAwareStep<S, 
S> implements Trav
         this.repeatTraversal = repeatTraversal; // .clone();
         this.repeatTraversal.addStep(new RepeatEndStep(this.repeatTraversal));
         this.integrateChild(this.repeatTraversal);
+        
+        // Enable per-iteration counters on any RangeGlobalStep instances 
within the repeat traversal
+        this.enablePerIterationCountersOnRangeSteps(this.repeatTraversal);
     }
 
 
@@ -125,6 +129,26 @@ public final class RepeatStep<S> extends 
ComputerAwareStep<S, S> implements Trav
         return emitFirst == this.emitFirst && null != this.emitTraversal && 
TraversalUtil.test(traverser, this.emitTraversal);
     }
 
+    /**
+     * Recursively enables per-iteration counters on all RangeGlobalStep 
instances within a traversal.
+     */
+    private void enablePerIterationCountersOnRangeSteps(final 
Traversal.Admin<?, ?> traversal) {
+        for (final Step<?, ?> step : traversal.getSteps()) {
+            if (step instanceof RangeGlobalStep) {
+                ((RangeGlobalStep<?>) step).enablePerIterationCounters();
+            }
+            if (step instanceof TraversalParent) {
+                final TraversalParent parent = (TraversalParent) step;
+                for (final Traversal.Admin<?, ?> childTraversal : 
parent.getGlobalChildren()) {
+                    
this.enablePerIterationCountersOnRangeSteps(childTraversal);
+                }
+                for (final Traversal.Admin<?, ?> childTraversal : 
parent.getLocalChildren()) {
+                    
this.enablePerIterationCountersOnRangeSteps(childTraversal);
+                }
+            }
+        }
+    }
+
     @Override
     public String toString() {
         if (this.untilFirst && this.emitFirst)
@@ -169,6 +193,10 @@ public final class RepeatStep<S> extends 
ComputerAwareStep<S, S> implements Trav
             clone.untilTraversal = this.untilTraversal.clone();
         if (null != this.emitTraversal)
             clone.emitTraversal = this.emitTraversal.clone();
+        
+        // Enable per-iteration counters on the cloned repeat traversal
+        clone.enablePerIterationCountersOnRangeSteps(clone.repeatTraversal);
+        
         return clone;
     }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
index 792ab2d645..5d6dbbab7c 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
@@ -32,6 +32,8 @@ import 
org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 
 import java.io.Serializable;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
@@ -47,6 +49,10 @@ public final class RangeGlobalStep<S> extends FilterStep<S> 
implements Ranging,
     private long high;
     private AtomicLong counter = new AtomicLong(0l);
     private boolean bypass;
+    
+    // Per-iteration counter tracking for repeat steps
+    private Map<String, AtomicLong> perIterationCounters = new HashMap<>();
+    private boolean usePerIterationCounters = false;
 
     public RangeGlobalStep(final Traversal.Admin traversal, final long low, 
final long high) {
         super(traversal);
@@ -61,31 +67,38 @@ public final class RangeGlobalStep<S> extends FilterStep<S> 
implements Ranging,
     protected boolean filter(final Traverser.Admin<S> traverser) {
         if (this.bypass) return true;
 
-        if (this.high != -1 && this.counter.get() >= this.high) {
+        // Determine which counter to use
+        AtomicLong currentCounter = this.counter;
+        if (usePerIterationCounters && traverser.loops() > 0) {
+            String iterationKey = traverser.getStepId() + ":" + 
traverser.loops();
+            currentCounter = 
perIterationCounters.computeIfAbsent(iterationKey, k -> new AtomicLong(0L));
+        }
+
+        if (this.high != -1 && currentCounter.get() >= this.high) {
             throw FastNoSuchElementException.instance();
         }
 
         long avail = traverser.bulk();
-        if (this.counter.get() + avail <= this.low) {
+        if (currentCounter.get() + avail <= this.low) {
             // Will not surpass the low w/ this traverser. Skip and filter the 
whole thing.
-            this.counter.getAndAdd(avail);
+            currentCounter.getAndAdd(avail);
             return false;
         }
 
         // Skip for the low and trim for the high. Both can happen at once.
 
         long toSkip = 0;
-        if (this.counter.get() < this.low) {
-            toSkip = this.low - this.counter.get();
+        if (currentCounter.get() < this.low) {
+            toSkip = this.low - currentCounter.get();
         }
 
         long toTrim = 0;
-        if (this.high != -1 && this.counter.get() + avail >= this.high) {
-            toTrim = this.counter.get() + avail - this.high;
+        if (this.high != -1 && currentCounter.get() + avail >= this.high) {
+            toTrim = currentCounter.get() + avail - this.high;
         }
 
         long toEmit = avail - toSkip - toTrim;
-        this.counter.getAndAdd(toSkip + toEmit);
+        currentCounter.getAndAdd(toSkip + toEmit);
         traverser.setBulk(toEmit);
 
         return true;
@@ -95,6 +108,15 @@ public final class RangeGlobalStep<S> extends FilterStep<S> 
implements Ranging,
     public void reset() {
         super.reset();
         this.counter.set(0l);
+        this.perIterationCounters.clear();
+    }
+
+    /**
+     * Enables per-iteration counter tracking for use within repeat steps.
+     * When enabled, separate counters are maintained for each repeat 
iteration.
+     */
+    public void enablePerIterationCounters() {
+        this.usePerIterationCounters = true;
     }
 
     @Override
@@ -116,6 +138,8 @@ public final class RangeGlobalStep<S> extends FilterStep<S> 
implements Ranging,
     public RangeGlobalStep<S> clone() {
         final RangeGlobalStep<S> clone = (RangeGlobalStep<S>) super.clone();
         clone.counter = new AtomicLong(0l);
+        clone.perIterationCounters = new HashMap<>();
+        clone.usePerIterationCounters = this.usePerIterationCounters;
         return clone;
     }
 

Reply via email to