You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ke...@apache.org on 2025/10/17 21:55:26 UTC
(tinkerpop) 01/01: TINKERPOP-3200 Made repeat() act as a global parent
This is an automated email from the ASF dual-hosted git repository.
kenhuuu pushed a commit to branch TINKERPOP-3200
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 8b09f7a449adfdb785601383e9fd209c4f104aac
Author: Ken Hu <10...@users.noreply.github.com>
AuthorDate: Tue Oct 14 15:31:47 2025 -0700
TINKERPOP-3200 Made repeat() act as a global parent
Co-authored-by: Cole-Greer <Co...@improving.com>
---
CHANGELOG.asciidoc | 1 +
docs/src/dev/provider/gremlin-semantics.asciidoc | 5 +-
docs/src/upgrade/release-3.8.x.asciidoc | 185 +++++++++++++++++++++
.../process/traversal/step/branch/RepeatStep.java | 43 +++--
.../gremlin/test/features/branch/Repeat.feature | 181 +++++++++++++++++++-
.../gremlin/test/features/integrated/Paths.feature | 13 +-
6 files changed, 404 insertions(+), 24 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index dde7953b08..e457b44e41 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -95,6 +95,7 @@ This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>.
* Removed the `@RemoteOnly` testing tag in Gherkin as lambda tests have all been moved to the Java test suite.
* Updated gremlin-javascript to use GraphBinary as default instead of GraphSONv3
* Added the `asNumber()` step to perform number conversion.
+* Changed `repeat()` to make `repeatTraversal` global rather than a mix of local and global.
* Renamed many types in the grammar for consistent use of terms "Literal", "Argument", and "Varargs"
* Changed `gremlin-net` so that System.Text.Json is only listed as an explicit dependency when it is not available from the framework.
* Fixed translation of numeric literals for Go losing type definitions
diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc
index dff6cda769..8df5684db6 100644
--- a/docs/src/dev/provider/gremlin-semantics.asciidoc
+++ b/docs/src/dev/provider/gremlin-semantics.asciidoc
@@ -1974,7 +1974,7 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#project-step[reference]
[[repeat-step]]
=== repeat()
-*Description:* Iteratively applies a traversal (the "loop body") to each incoming traverser until a stopping
+*Description:* Iteratively applies a traversal (the "loop body") to all incoming traversers until a stopping
condition is met. Optionally, it can emit traversers on each iteration according to an emit predicate. The
repeat step supports loop naming and a loop counter via `loops()`.
@@ -2015,6 +2015,9 @@ predicates are evaluated before the first iteration (pre) or after each iteratio
`do/while` semantics respectively:
- Pre-check / pre-emit: when the modulator appears before `repeat(...)`.
- Post-check / post-emit: when the modulator appears after `repeat(...)`.
+- Global traversal scope: The `repeatTraversal` is a global child. This means all traversers entering the repeat body
+are processed together as a unified stream with global semantics. `Barrier` (`order()`, `sample()`, etc.) steps within
+the repeat traversal operate across all traversers collectively rather than in isolation per traverser.
- Loop counter semantics:
- The loop counter for a given named or unnamed repeat is incremented once per completion of the loop body (i.e.,
after the body finishes), not before. Therefore, `loops()` reflects the number of completed iterations.
diff --git a/docs/src/upgrade/release-3.8.x.asciidoc b/docs/src/upgrade/release-3.8.x.asciidoc
index 65b5e6a71b..fd2bfed87f 100644
--- a/docs/src/upgrade/release-3.8.x.asciidoc
+++ b/docs/src/upgrade/release-3.8.x.asciidoc
@@ -220,6 +220,135 @@ gremlin> g.inject([Float.MAX_VALUE, Float.MAX_VALUE], [Double.MAX_VALUE, Double.
See link:https://issues.apache.org/jira/browse/TINKERPOP-3115[TINKERPOP-3115]
+==== repeat() Step Global Children Semantics Change
+
+The `repeat()` step has been updated to treat the repeat traversal as a global child in all cases. Previously, the
+repeat traversal behaved as a hybrid between local and global semantics, which could lead to unexpected results in
+certain scenarios. The repeat traversal started off as a local child but as traversers were added back per iteration,
+it behaved more like a global child.
+
+With this change, the repeat traversal now consistently operates with global semantics, meaning that all traversers
+are processed together rather than being processed per traverser. This provides more predictable behavior and aligns
+with the semantics of other steps.
+
+[source,text]
+----
+// In 3.7.x and earlier, the order would be local to the first traverser.
+// Notice how the results are grouped by marko, then vadas, then lop
+gremlin> g.withoutStrategies(RepeatUnrollStrategy).V(1, 2, 3).
+......1> repeat(both().simplePath().order().by("name")).times(2).path().by("name")
+==>[marko,lop,josh]
+==>[marko,josh,lop]
+==>[marko,lop,peter]
+==>[marko,josh,ripple]
+==>[vadas,marko,josh]
+==>[vadas,marko,lop]
+==>[lop,marko,josh]
+==>[lop,josh,marko]
+==>[lop,josh,ripple]
+==>[lop,marko,vadas]
+
+// In 3.8.0, the aggregate now consistently uses global semantics
+// The traversers are now ordered so the traversers from the final iteration are ordered first then by
+// the traversers from previous iterations
+gremlin> g.withoutStrategies(RepeatUnrollStrategy).V(1, 2, 3).
+......1> repeat(both().simplePath().order().by("name")).times(2).path().by("name")
+==>[marko,lop,josh]
+==>[vadas,marko,josh]
+==>[lop,marko,josh]
+==>[marko,josh,lop]
+==>[vadas,marko,lop]
+==>[lop,josh,marko]
+==>[marko,lop,peter]
+==>[marko,josh,ripple]
+==>[lop,josh,ripple]
+==>[lop,marko,vadas]
+----
+
+This change may affect traversals that relied on the previous hybrid behavior, particularly those using side effects
+or barrier steps within `repeat()`. Review any traversals using `repeat()` with steps like `aggregate()`, `store()`,
+or other barrier steps to ensure they produce the expected results. Note that there is no way to exactly replicate the
+old behavior anymore. The following examples show why:
+
+[source,text]
+----
+// In 3.7.x
+gremlin> g.V().repeat(both().order().by("name")).times(1).path()
+==>[v[1],v[4]]
+==>[v[1],v[3]]
+==>[v[1],v[2]]
+==>[v[2],v[1]]
+==>[v[3],v[4]]
+==>[v[3],v[1]]
+==>[v[3],v[6]]
+==>[v[4],v[3]]
+==>[v[4],v[1]]
+==>[v[4],v[5]]
+==>[v[5],v[4]]
+==>[v[6],v[3]]
+
+// In 3.8.0, if there is a single iteration then adding a local() can give the same result
+gremlin> g.V().repeat(local(both().order().by("name"))).times(1).path()
+==>[v[1],v[4]]
+==>[v[1],v[3]]
+==>[v[1],v[2]]
+==>[v[2],v[1]]
+==>[v[3],v[4]]
+==>[v[3],v[1]]
+==>[v[3],v[6]]
+==>[v[4],v[3]]
+==>[v[4],v[1]]
+==>[v[4],v[5]]
+==>[v[5],v[4]]
+==>[v[6],v[3]]
+
+
+// In 3.7.x
+gremlin> g.V().repeat(local(both().simplePath().order().by("name"))).times(2).path()
+==>[v[1],v[3],v[4]]
+==>[v[1],v[4],v[3]]
+==>[v[1],v[3],v[6]]
+==>[v[1],v[4],v[5]]
+==>[v[2],v[1],v[4]]
+==>[v[2],v[1],v[3]]
+==>[v[3],v[1],v[4]]
+==>[v[3],v[4],v[1]]
+==>[v[3],v[4],v[5]]
+==>[v[3],v[1],v[2]]
+==>[v[4],v[1],v[3]]
+==>[v[4],v[3],v[1]]
+==>[v[4],v[3],v[6]]
+==>[v[4],v[1],v[2]]
+==>[v[5],v[4],v[3]]
+==>[v[5],v[4],v[1]]
+==>[v[6],v[3],v[4]]
+==>[v[6],v[3],v[1]]
+
+// In 3.8.0, if there are multiple iterations then the local() will affect each iteration which gives different results
+// than in 3.7.x (shown above)
+gremlin> g.V().repeat(local(both().simplePath().order().by("name"))).times(2).path()
+==>[v[1],v[4],v[3]]
+==>[v[1],v[4],v[5]]
+==>[v[1],v[3],v[4]]
+==>[v[1],v[3],v[6]]
+==>[v[2],v[1],v[4]]
+==>[v[2],v[1],v[3]]
+==>[v[3],v[4],v[1]]
+==>[v[3],v[4],v[5]]
+==>[v[3],v[1],v[4]]
+==>[v[3],v[1],v[2]]
+==>[v[4],v[3],v[1]]
+==>[v[4],v[3],v[6]]
+==>[v[4],v[1],v[3]]
+==>[v[4],v[1],v[2]]
+==>[v[5],v[4],v[3]]
+==>[v[5],v[4],v[1]]
+==>[v[6],v[3],v[4]]
+==>[v[6],v[3],v[1]]
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3200[TINKERPOP-3200]
+
==== Prefer OffsetDateTime
The default implementation for date type in Gremlin is now changed from the `java.util.Date` to the more encompassing
@@ -927,6 +1056,62 @@ The `ChooseStep` now provides a `ChooseSemantics` enum which helps indicate if t
See: link:https://issues.apache.org/jira/browse/TINKERPOP-3178[TINKERPOP-3178]
+===== repeat() Step Global Children Semantics Change
+
+The `RepeatStep` has been updated to consistently treat the repeat traversal as a global child rather than using
+hybrid local/global semantics. This change affects how the repeat traversal processes traversers and interacts with
+the parent traversal.
+
+Previously, `RepeatStep` would start with local semantics for the first iteration and then switch to global semantics
+for the subsequent iterations, which created inconsistencies in how side effects and barriers behaved within the repeat
+traversal. The biggest change will be to `Barrier` steps in the repeat traversal as they will now have access to all
+the starting traversers.
+
+[source,text]
+----
+// In 3.7.x and earlier, the order would be local to the first traverser.
+// Notice how the results are grouped by marko, then vadas, then lop
+gremlin> g.withoutStrategies(RepeatUnrollStrategy).V(1, 2, 3).
+......1> repeat(both().simplePath().order().by("name")).times(2).path().by("name")
+==>[marko,lop,josh]
+==>[marko,josh,lop]
+==>[marko,lop,peter]
+==>[marko,josh,ripple]
+==>[vadas,marko,josh]
+==>[vadas,marko,lop]
+==>[lop,marko,josh]
+==>[lop,josh,marko]
+==>[lop,josh,ripple]
+==>[lop,marko,vadas]
+
+// In 3.8.0, the aggregate now consistently uses global semantics
+// The traversers are now ordered so the traversers from the final iteration are ordered first then by
+// the traversers from previous iterations
+gremlin> g.withoutStrategies(RepeatUnrollStrategy).V(1, 2, 3).
+......1> repeat(both().simplePath().order().by("name")).times(2).path().by("name")
+==>[marko,lop,josh]
+==>[vadas,marko,josh]
+==>[lop,marko,josh]
+==>[marko,josh,lop]
+==>[vadas,marko,lop]
+==>[lop,josh,marko]
+==>[marko,lop,peter]
+==>[marko,josh,ripple]
+==>[lop,josh,ripple]
+==>[lop,marko,vadas]
+----
+
+Providers implementing custom optimizations or strategies around `RepeatStep` should verify that their
+implementations account for the repeat traversal being a global child. This particularly affects:
+
+- Strategies that analyze or transform repeat traversals
+- Optimizations that depend on the scope semantics of child traversals
+
+The last point about optimizations may be particularly important for providers that have memory constraints as this
+change may bring about higher memory usage due to more traversers needing to be held in memory.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3200[TINKERPOP-3200]
+
===== Prefer OffsetDateTime
The default implementation for date type in Gremlin is now changed from the deprecated `java.util.Date` to the more
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..2abbbc87cc 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
@@ -21,9 +21,12 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.branch;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+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.util.FastNoSuchElementException;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
@@ -43,6 +46,7 @@ public final class RepeatStep<S> extends ComputerAwareStep<S, S> implements Trav
private Traversal.Admin<S, S> repeatTraversal = null;
private Traversal.Admin<S, ?> untilTraversal = null;
private Traversal.Admin<S, ?> emitTraversal = null;
+ private boolean first = true;
private String loopName = null;
public boolean untilFirst = false;
public boolean emitFirst = false;
@@ -206,25 +210,40 @@ public final class RepeatStep<S> extends ComputerAwareStep<S, S> implements Trav
throw new IllegalStateException("The repeat()-traversal was not defined: " + this);
while (true) {
- if (this.repeatTraversal.getEndStep().hasNext()) {
+ if (!first && this.repeatTraversal.getEndStep().hasNext()) {
return this.repeatTraversal.getEndStep();
} else {
- final Traverser.Admin<S> start = this.starts.next();
- start.initialiseLoops(this.getId(), this.loopName);
- if (doUntil(start, true)) {
- start.resetLoops();
- return IteratorUtils.of(start);
- }
- this.repeatTraversal.addStart(start);
- if (doEmit(start, true)) {
- final Traverser.Admin<S> emitSplit = start.split();
- emitSplit.resetLoops();
- return IteratorUtils.of(emitSplit);
+ this.first = false;
+ if (!TraversalHelper.getStepsOfAssignableClassRecursively(Barrier.class, repeatTraversal).isEmpty()) {
+ // If the repeatTraversal has a Barrier then make sure that all starts are added to the
+ // repeatTraversal before it is iterated so that RepeatStep always has "global" children.
+ if (!this.starts.hasNext())
+ throw FastNoSuchElementException.instance();
+ while (this.starts.hasNext()) {
+ processTraverser(this.starts.next());
+ }
+ } else {
+ return processTraverser(this.starts.next());
}
}
}
}
+ private Iterator<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);
+ }
+ this.repeatTraversal.addStart(start);
+ if (doEmit(start, true)) {
+ final Traverser.Admin<S> emitSplit = start.split();
+ emitSplit.resetLoops();
+ return IteratorUtils.of(emitSplit);
+ }
+ return Collections.emptyIterator();
+ }
+
@Override
protected Iterator<Traverser.Admin<S>> computerAlgorithm() throws NoSuchElementException {
if (null == this.repeatTraversal)
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 9fbdde3ef5..51834c060c 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
@@ -429,4 +429,183 @@ Feature: Step - repeat()
When iterated to list
Then the result should be unordered
| result |
- | marko |
\ No newline at end of file
+ | marko |
+
+ # Proper handling of empty results if repeat doesn't output traversers
+ Scenario: g_V_repeatXboth_hasXnot_productiveXX_timesX3X_constantX1X
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().repeat(__.both().has("not", "productive")).times(3).constant(1)
+ """
+ When iterated to list
+ Then the result should be empty
+
+ # Proper handling of empty results if no traverser goes into repeat
+ Scenario: g_V_hasXnot_productiveX_repeatXbothX_timesX3X_constantX1X
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().has("not", "productive").repeat(__.both()).times(3).constant(1)
+ """
+ When iterated to list
+ Then the result should be empty
+
+ # Each iteration of repeat traversal that ends in a barrier leads to BFS style processing
+ @InsertionOrderingRequired
+ Scenario: g_VX1_2_3X_repeatXboth_barrierX_emit_timesX2X_path
+ Given the modern graph
+ And using the parameter vid1 defined as "v[marko].id"
+ And using the parameter vid2 defined as "v[vadas].id"
+ And using the parameter vid3 defined as "v[lop].id"
+ And the traversal of
+ """
+ g.V(vid1, vid2, vid3).repeat(__.both().barrier()).emit().times(2).path()
+ """
+ When iterated to list
+ Then the result should be ordered
+ | result |
+ | p[v[marko],v[lop]] |
+ | p[v[marko],v[vadas]] |
+ | p[v[marko],v[josh]] |
+ | p[v[vadas],v[marko]] |
+ | p[v[lop],v[marko]] |
+ | p[v[lop],v[josh]] |
+ | p[v[lop],v[peter]] |
+ | p[v[marko],v[lop],v[marko]] |
+ | p[v[marko],v[lop],v[josh]] |
+ | p[v[marko],v[lop],v[peter]] |
+ | p[v[marko],v[vadas],v[marko]] |
+ | p[v[marko],v[josh],v[ripple]] |
+ | p[v[marko],v[josh],v[lop]] |
+ | p[v[marko],v[josh],v[marko]] |
+ | p[v[vadas],v[marko],v[lop]] |
+ | p[v[vadas],v[marko],v[vadas]] |
+ | p[v[vadas],v[marko],v[josh]] |
+ | p[v[lop],v[marko],v[lop]] |
+ | p[v[lop],v[marko],v[vadas]] |
+ | p[v[lop],v[marko],v[josh]] |
+ | p[v[lop],v[josh],v[ripple]] |
+ | p[v[lop],v[josh],v[lop]] |
+ | p[v[lop],v[josh],v[marko]] |
+ | p[v[lop],v[peter],v[lop]] |
+
+ # Global children should be ordered by last loop first
+ @GraphComputerVerificationOrderingNotSupported
+ Scenario: g_V_order_byXname_descX_repeatXboth_simplePath_order_byXname_descXX_timesX2X_path
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().order().by("name", Order.desc).repeat(__.both().simplePath().order().by("name", Order.desc)).times(2).path()
+ """
+ When iterated to list
+ Then the result should be ordered
+ | result |
+ | p[v[lop],v[marko],v[vadas]] |
+ | p[v[josh],v[marko],v[vadas]] |
+ | p[v[marko],v[josh],v[ripple]] |
+ | p[v[lop],v[josh],v[ripple]] |
+ | p[v[marko],v[lop],v[peter]] |
+ | p[v[josh],v[lop],v[peter]] |
+ | p[v[peter],v[lop],v[marko]] |
+ | p[v[josh],v[lop],v[marko]] |
+ | p[v[ripple],v[josh],v[marko]] |
+ | p[v[lop],v[josh],v[marko]] |
+ | p[v[vadas],v[marko],v[lop]] |
+ | p[v[josh],v[marko],v[lop]] |
+ | p[v[ripple],v[josh],v[lop]] |
+ | p[v[marko],v[josh],v[lop]] |
+ | p[v[vadas],v[marko],v[josh]] |
+ | p[v[lop],v[marko],v[josh]] |
+ | p[v[peter],v[lop],v[josh]] |
+ | p[v[marko],v[lop],v[josh]] |
+
+ # Nested repeat should maintain globalness
+ @GraphComputerVerificationOrderingNotSupported
+ Scenario: g_V_repeatXboth_repeatXorder_byXnameXX_timesX1XX_timesX1X
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().repeat(__.both().repeat(__.order().by("name")).times(1)).times(1)
+ """
+ When iterated to list
+ Then the result should be ordered
+ | result |
+ | v[josh] |
+ | v[josh] |
+ | v[josh] |
+ | v[lop] |
+ | v[lop] |
+ | v[lop] |
+ | v[marko] |
+ | v[marko] |
+ | v[marko] |
+ | v[peter] |
+ | v[ripple] |
+ | v[vadas] |
+
+ # Nested local inside repeat should prevent global children from parent repeat
+ @GraphComputerVerificationStarGraphExceeded
+ Scenario: g_V_order_byXname_descX_repeatXlocalXout_order_byXnameXXX_timesX1X
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().order().by("name", Order.desc).repeat(__.local(__.out().order().by("name"))).times(1)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | v[lop] |
+ | v[josh] |
+ | v[lop] |
+ | v[vadas] |
+ | v[lop] |
+ | v[ripple] |
+
+ # Local child traversal should be applied per loop
+ @GraphComputerVerificationStarGraphExceeded
+ Scenario: g_V_order_byXnameX_repeatXlocalXboth_simplePath_order_byXnameXXX_timesX2X_path
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().order().by("name").repeat(__.local(__.both().simplePath().order().by("name"))).times(2).path()
+ """
+ When iterated to list
+ Then the result should be ordered
+ | result |
+ | p[v[josh],v[lop],v[marko]] |
+ | p[v[josh],v[lop],v[peter]] |
+ | p[v[josh],v[marko],v[lop]] |
+ | p[v[josh],v[marko],v[vadas]] |
+ | p[v[lop],v[josh],v[marko]] |
+ | p[v[lop],v[josh],v[ripple]] |
+ | p[v[lop],v[marko],v[josh]] |
+ | p[v[lop],v[marko],v[vadas]] |
+ | p[v[marko],v[josh],v[lop]] |
+ | p[v[marko],v[josh],v[ripple]] |
+ | p[v[marko],v[lop],v[josh]] |
+ | p[v[marko],v[lop],v[peter]] |
+ | p[v[peter],v[lop],v[josh]] |
+ | p[v[peter],v[lop],v[marko]] |
+ | p[v[ripple],v[josh],v[lop]] |
+ | p[v[ripple],v[josh],v[marko]] |
+ | p[v[vadas],v[marko],v[josh]] |
+ | p[v[vadas],v[marko],v[lop]] |
+
+ # Branching step with global children should remain global in repeat traversal
+ @GraphComputerVerificationOrderingNotSupported
+ Scenario: g_V_repeatXunionXoutXknowsX_order_byXnameX_inXcreatedX_order_byXnameXXX_timesX1X
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().repeat(__.union(__.out("knows").order().by("name"), __.in("created").order().by("name"))).times(1)
+ """
+ When iterated to list
+ Then the result should be ordered
+ | result |
+ | v[josh] |
+ | v[vadas] |
+ | v[josh] |
+ | v[josh] |
+ | v[marko] |
+ | v[peter] |
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/integrated/Paths.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/integrated/Paths.feature
index 172754f81a..381ab656ee 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/integrated/Paths.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/integrated/Paths.feature
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-@StepClassIntegrated
+@StepClassIntegrated @StepPaths
Feature: Step - paths
@GraphComputerVerificationReferenceOnly @InsertionOrderingRequired
@@ -38,6 +38,7 @@ Feature: Step - paths
by(__.select(Pop.all, "v")).as("t").
filter(__.select(Pop.all, "p").count(local).as("l").
select(Pop.last, "t").select(Pop.all, "p").dedup(Scope.local).count(Scope.local).where(P.eq("l"))).
+ where("src", P.neq("tgt")).
select(Pop.last, "t").
not(__.select(Pop.all, "p").as("p").count(local).as("l").
select(Pop.all, "x").unfold().filter(select(keys).where(P.eq("t")).by(__.select("src", "tgt"))).
@@ -63,18 +64,12 @@ Feature: Step - paths
| l[peter,lop,josh]|
| l[vadas,marko]|
| l[ripple,josh]|
- | l[marko]|
- | l[josh]|
- | l[ripple]|
| l[josh,ripple]|
| l[peter,lop]|
| l[vadas,marko,josh]|
| l[lop,josh,ripple]|
| l[marko,josh]|
| l[lop,marko,vadas]|
- | l[lop]|
- | l[peter]|
- | l[vadas]|
| l[marko,josh,ripple]|
| l[marko,vadas]|
| l[vadas,marko,lop]|
@@ -103,11 +98,9 @@ Feature: Step - paths
When iterated to list
Then the result should be unordered
| result |
- | m[{"song":"TOMORROW IS A LONG TIME","artists":["Bob_Dylan"]}] |
- | m[{"song":"JOHN BROWN","artists":["Bob_Dylan"]}] |
+ | m[{"song":"I WANT YOU","artists":["Bob_Dylan"]}] |
| m[{"song":"BABY BLUE","artists":["Unknown"]}] |
| m[{"song":"CANDYMAN","artists":["Garcia","Hunter"]}] |
| m[{"song":"BIG RIVER","artists":["Weir","Johnny_Cash"]}] |
| m[{"song":"TERRAPIN STATION","artists":["Garcia","Hunter"]}] |
| m[{"song":"DRUMS","artists":["Grateful_Dead"]}] |
-