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

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

commit 319d57588c6e42fece394368dffab27a3de5c076
Author: Ken Hu <[email protected]>
AuthorDate: Fri Dec 19 13:50:01 2025 -0800

    TINKERPOP-3218 Fix bug in pre-repeat emit()/until()
---
 .../process/traversal/step/branch/RepeatStep.java  | 22 +++--
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  5 ++
 gremlin-go/driver/cucumber/gremlin.go              |  5 ++
 .../gremlin-javascript/test/cucumber/gremlin.js    |  5 ++
 gremlin-python/src/main/python/radish/gremlin.py   |  5 ++
 .../gremlin/test/features/branch/Repeat.feature    | 98 ++++++++++++++++++++++
 6 files changed, 134 insertions(+), 6 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 74cb7851bb..36aac12847 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
@@ -25,6 +25,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.Barrier;
 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.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.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
@@ -219,11 +220,20 @@ public final class RepeatStep<S> extends 
ComputerAwareStep<S, S> implements Trav
                     // repeatTraversal before it is iterated so that 
RepeatStep always has "global" children.
                     if (!this.starts.hasNext())
                         throw FastNoSuchElementException.instance();
+
+                    final List<Traverser.Admin<S>> traversers = new 
ArrayList<>();
                     while (this.starts.hasNext()) {
-                        processTraverser(this.starts.next());
+                        final Traverser.Admin<S> processedTraverser = 
processTraverser(this.starts.next());
+                        if 
(!processedTraverser.equals(EmptyTraverser.instance())) {
+                            traversers.add(processedTraverser);
+                        }
                     }
+                    if (!traversers.isEmpty()) return traversers.iterator();
                 } else {
-                    return processTraverser(this.starts.next());
+                    final Traverser.Admin<S> processedTraverser = 
processTraverser(this.starts.next());
+                    if (!processedTraverser.equals(EmptyTraverser.instance())) 
{
+                        return IteratorUtils.of(processedTraverser);
+                    }
                 }
             }
         }
@@ -253,19 +263,19 @@ public final class RepeatStep<S> extends 
ComputerAwareStep<S, S> implements Trav
         }
     }
 
-    private Iterator<Traverser.Admin<S>> processTraverser(final 
Traverser.Admin<S> start) {
+    private Traverser.Admin<S> processTraverser(final Traverser.Admin<S> 
start) {
         start.initialiseLoops(this.getId(), this.loopName);
         if (doUntil(start, true)) {
             start.resetLoops();
-            return IteratorUtils.of(start);
+            return start;
         }
         this.repeatTraversal.addStart(start);
         if (doEmit(start, true)) {
             final Traverser.Admin<S> emitSplit = start.split();
             emitSplit.resetLoops();
-            return IteratorUtils.of(emitSplit);
+            return emitSplit;
         }
-        return Collections.emptyIterator();
+        return EmptyTraverser.instance();
     }
 
     /////////////////////////
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 4cfd7d1daf..0a6cfbc701 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -181,6 +181,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                
{"g_VX3X_repeatXout_order_byXperformances_descX_limitX5X_tailX1XX_timesX2X_valuesXnameX",
 new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V(p["vid3"]).Repeat(__.Out().Order().By("performances", 
Order.Desc).Limit<object>(5).Tail<object>(1)).Times(2).Values<object>("name")}},
 
                
{"g_VX3X_repeatXoutE_order_byXweightX_tailX2X_inVX_timesX2X_valuesXnameX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V(p["vid3"]).Repeat(__.OutE().Order().By("weight").Tail<object>(2).InV()).Times(2).Values<object>("name")}},
 
                
{"g_VX3X_repeatXoutE_order_byXweight_descX_limitX2X_inVX_timesX2X_valuesXnameX",
 new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V(p["vid3"]).Repeat(__.OutE().Order().By("weight", 
Order.Desc).Limit<object>(2).InV()).Times(2).Values<object>("name")}}, 
+               {"g_V_emit_repeatXout_order_byXnameXX_timesX2X_valuesXnameX", 
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().Emit().Repeat(__.Out().Order().By("name")).Times(2).Values<object>("name")}},
 
+               
{"g_V_emitXhasLabelXpersonXX_repeatXout_order_byXnameXX_timesX2X_valuesXnameX", 
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().Emit(__.HasLabel("person")).Repeat(__.Out().Order().By("name")).Times(2).Values<object>("name")}},
 
+               
{"g_V_untilXloops_isX2XX_repeatXout_order_byXnameXX_valuesXnameX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().Until(__.Loops().Is(2)).Repeat(__.Out().Order().By("name")).Values<object>("name")}},
 
+               {"g_V_emit_repeatXdedupX_timesX1X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Emit().Repeat(__.Dedup()).Times(1)}}, 
+               {"g_V_emit_repeatXdedupX_timesX2X", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Emit().Repeat(__.Dedup()).Times(2)}}, 
                {"g_unionXX", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Union<object>()}}, 
                {"g_unionXV_name", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.Union<object>(__.V().Values<object>("name"))}}, 
                {"g_unionXVXv1X_VX4XX_name", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Union<object>(__.V(p["vid1"]), 
__.V(p["vid4"])).Values<object>("name")}}, 
diff --git a/gremlin-go/driver/cucumber/gremlin.go 
b/gremlin-go/driver/cucumber/gremlin.go
index 2d7865a468..d975424664 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -151,6 +151,11 @@ var translationMap = map[string][]func(g 
*gremlingo.GraphTraversalSource, p map[
     
"g_VX3X_repeatXout_order_byXperformances_descX_limitX5X_tailX1XX_timesX2X_valuesXnameX":
 {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V(p["vid3"]).Repeat(gremlingo.T__.Out().Order().By("performances", 
gremlingo.Order.Desc).Limit(5).Tail(1)).Times(2).Values("name")}}, 
     "g_VX3X_repeatXoutE_order_byXweightX_tailX2X_inVX_timesX2X_valuesXnameX": 
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V(p["vid3"]).Repeat(gremlingo.T__.OutE().Order().By("weight").Tail(2).InV()).Times(2).Values("name")}},
 
     
"g_VX3X_repeatXoutE_order_byXweight_descX_limitX2X_inVX_timesX2X_valuesXnameX": 
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V(p["vid3"]).Repeat(gremlingo.T__.OutE().Order().By("weight", 
gremlingo.Order.Desc).Limit(2).InV()).Times(2).Values("name")}}, 
+    "g_V_emit_repeatXout_order_byXnameXX_timesX2X_valuesXnameX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Emit().Repeat(gremlingo.T__.Out().Order().By("name")).Times(2).Values("name")}},
 
+    
"g_V_emitXhasLabelXpersonXX_repeatXout_order_byXnameXX_timesX2X_valuesXnameX": 
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Emit(gremlingo.T__.HasLabel("person")).Repeat(gremlingo.T__.Out().Order().By("name")).Times(2).Values("name")}},
 
+    "g_V_untilXloops_isX2XX_repeatXout_order_byXnameXX_valuesXnameX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Until(gremlingo.T__.Loops().Is(2)).Repeat(gremlingo.T__.Out().Order().By("name")).Values("name")}},
 
+    "g_V_emit_repeatXdedupX_timesX1X": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Emit().Repeat(gremlingo.T__.Dedup()).Times(1)}}, 
+    "g_V_emit_repeatXdedupX_timesX2X": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Emit().Repeat(gremlingo.T__.Dedup()).Times(2)}}, 
     "g_unionXX": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.Union()}}, 
     "g_unionXV_name": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return 
g.Union(gremlingo.T__.V().Values("name"))}}, 
     "g_unionXVXv1X_VX4XX_name": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return 
g.Union(gremlingo.T__.V(p["vid1"]), 
gremlingo.T__.V(p["vid4"])).Values("name")}}, 
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 e2bb6f210d..e7e4dd0197 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
@@ -182,6 +182,11 @@ const gremlins = {
     
g_VX3X_repeatXout_order_byXperformances_descX_limitX5X_tailX1XX_timesX2X_valuesXnameX:
 [function({g, vid3}) { return 
g.V(vid3).repeat(__.out().order().by("performances", 
Order.desc).limit(5).tail(1)).times(2).values("name") }], 
     g_VX3X_repeatXoutE_order_byXweightX_tailX2X_inVX_timesX2X_valuesXnameX: 
[function({g, vid3}) { return 
g.V(vid3).repeat(__.outE().order().by("weight").tail(2).inV()).times(2).values("name")
 }], 
     
g_VX3X_repeatXoutE_order_byXweight_descX_limitX2X_inVX_timesX2X_valuesXnameX: 
[function({g, vid3}) { return g.V(vid3).repeat(__.outE().order().by("weight", 
Order.desc).limit(2).inV()).times(2).values("name") }], 
+    g_V_emit_repeatXout_order_byXnameXX_timesX2X_valuesXnameX: [function({g}) 
{ return 
g.V().emit().repeat(__.out().order().by("name")).times(2).values("name") }], 
+    
g_V_emitXhasLabelXpersonXX_repeatXout_order_byXnameXX_timesX2X_valuesXnameX: 
[function({g}) { return 
g.V().emit(__.hasLabel("person")).repeat(__.out().order().by("name")).times(2).values("name")
 }], 
+    g_V_untilXloops_isX2XX_repeatXout_order_byXnameXX_valuesXnameX: 
[function({g}) { return 
g.V().until(__.loops().is(2)).repeat(__.out().order().by("name")).values("name")
 }], 
+    g_V_emit_repeatXdedupX_timesX1X: [function({g}) { return 
g.V().emit().repeat(__.dedup()).times(1) }], 
+    g_V_emit_repeatXdedupX_timesX2X: [function({g}) { return 
g.V().emit().repeat(__.dedup()).times(2) }], 
     g_unionXX: [function({g}) { return g.union() }], 
     g_unionXV_name: [function({g}) { return g.union(__.V().values("name")) }], 
     g_unionXVXv1X_VX4XX_name: [function({g, vid4, vid1}) { return 
g.union(__.V(vid1), __.V(vid4)).values("name") }], 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py 
b/gremlin-python/src/main/python/radish/gremlin.py
index 94098b6c5c..d6456be0ac 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -154,6 +154,11 @@ world.gremlins = {
     
'g_VX3X_repeatXout_order_byXperformances_descX_limitX5X_tailX1XX_timesX2X_valuesXnameX':
 [(lambda g, vid3=None:g.V(vid3).repeat(__.out().order().by('performances', 
Order.desc).limit(5).tail(1)).times(2).values('name'))], 
     'g_VX3X_repeatXoutE_order_byXweightX_tailX2X_inVX_timesX2X_valuesXnameX': 
[(lambda g, 
vid3=None:g.V(vid3).repeat(__.out_e().order().by('weight').tail(2).in_v()).times(2).values('name'))],
 
     
'g_VX3X_repeatXoutE_order_byXweight_descX_limitX2X_inVX_timesX2X_valuesXnameX': 
[(lambda g, vid3=None:g.V(vid3).repeat(__.out_e().order().by('weight', 
Order.desc).limit(2).in_v()).times(2).values('name'))], 
+    'g_V_emit_repeatXout_order_byXnameXX_timesX2X_valuesXnameX': [(lambda 
g:g.V().emit().repeat(__.out().order().by('name')).times(2).values('name'))], 
+    
'g_V_emitXhasLabelXpersonXX_repeatXout_order_byXnameXX_timesX2X_valuesXnameX': 
[(lambda 
g:g.V().emit(__.has_label('person')).repeat(__.out().order().by('name')).times(2).values('name'))],
 
+    'g_V_untilXloops_isX2XX_repeatXout_order_byXnameXX_valuesXnameX': [(lambda 
g:g.V().until(__.loops().is_(2)).repeat(__.out().order().by('name')).values('name'))],
 
+    'g_V_emit_repeatXdedupX_timesX1X': [(lambda 
g:g.V().emit().repeat(__.dedup()).times(1))], 
+    'g_V_emit_repeatXdedupX_timesX2X': [(lambda 
g:g.V().emit().repeat(__.dedup()).times(2))], 
     'g_unionXX': [(lambda g:g.union())], 
     'g_unionXV_name': [(lambda g:g.union(__.V().values('name')))], 
     'g_unionXVXv1X_VX4XX_name': [(lambda g, 
vid4=None,vid1=None:g.union(__.V(vid1), __.V(vid4)).values('name'))], 
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature
index f0bc665737..07e6f23179 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature
@@ -831,3 +831,101 @@ Feature: Step - repeat()
       | result |
       | SUGAR MAGNOLIA |
       | AROUND AND AROUND |
+
+  Scenario: g_V_emit_repeatXout_order_byXnameXX_timesX2X_valuesXnameX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().emit().repeat(__.out().order().by("name")).times(2).values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | marko |
+      | josh |
+      | lop |
+      | ripple |
+      | lop |
+      | vadas |
+      | vadas |
+      | lop |
+      | josh |
+      | lop |
+      | ripple |
+      | ripple |
+      | peter |
+      | lop |
+
+  Scenario: 
g_V_emitXhasLabelXpersonXX_repeatXout_order_byXnameXX_timesX2X_valuesXnameX
+    Given the modern graph
+    And the traversal of
+      """
+      
g.V().emit(__.hasLabel("person")).repeat(__.out().order().by("name")).times(2).values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | marko |
+      | josh |
+      | lop |
+      | ripple |
+      | vadas |
+      | vadas |
+      | josh |
+      | peter |
+
+  Scenario: g_V_untilXloops_isX2XX_repeatXout_order_byXnameXX_valuesXnameX
+    Given the modern graph
+    And the traversal of
+      """
+      
g.V().until(__.loops().is(2)).repeat(__.out().order().by("name")).values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | lop |
+      | ripple |
+
+  Scenario: g_V_emit_repeatXdedupX_timesX1X
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().emit().repeat(__.dedup()).times(1)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |
+      | v[marko] |
+      | v[vadas] |
+      | v[vadas] |
+      | v[lop] |
+      | v[lop] |
+      | v[josh] |
+      | v[josh] |
+      | v[ripple] |
+      | v[ripple] |
+      | v[peter] |
+      | v[peter] |
+
+  Scenario: g_V_emit_repeatXdedupX_timesX2X
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().emit().repeat(__.dedup()).times(2)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |
+      | v[marko] |
+      | v[vadas] |
+      | v[vadas] |
+      | v[lop] |
+      | v[lop] |
+      | v[josh] |
+      | v[josh] |
+      | v[ripple] |
+      | v[ripple] |
+      | v[peter] |
+      | v[peter] |

Reply via email to