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 8e69dbd573770e2631b191443d8e096a9af08e36 Author: Ken Hu <[email protected]> AuthorDate: Tue Oct 14 15:31:47 2025 -0700 TINKERPOP-3200 Made repeat() act as a global parent Test tag @GraphComputerVerificationOrderingNotSupported temporarily added as there is an unrelated bug in OLAP repeat() that prevents these from being tested in the same way as OLTP. Co-authored-by: Cole-Greer <[email protected]> --- 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.Net.IntegrationTest/Gherkin/Gremlin.cs | 14 +- gremlin-go/driver/cucumber/gremlin.go | 14 +- .../gremlin-javascript/test/cucumber/gremlin.js | 14 +- gremlin-python/src/main/python/radish/gremlin.py | 14 +- .../apache/tinkerpop/gremlin/features/World.java | 3 +- .../gremlin/test/features/branch/Repeat.feature | 181 +++++++++++++++++++- .../gremlin/test/features/integrated/Paths.feature | 11 +- .../integrated/RepeatUnrollStrategy.feature | 32 ++-- 12 files changed, 470 insertions(+), 47 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-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 9ab8812476..698b6ca21c 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -151,6 +151,14 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX0X_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("person", "name", "marko").Repeat(__.Out("created")).Times(0).Values<object>("name")}}, {"g_V_haxXperson_name_markoX_timesX1X_repeatXoutXcreatedXX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("person", "name", "marko").Times(1).Repeat(__.Out("created")).Values<object>("name")}}, {"g_V_haxXperson_name_markoX_timesX0X_repeatXoutXcreatedXX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("person", "name", "marko").Times(0).Repeat(__.Out("created")).Values<object>("name")}}, + {"g_V_repeatXboth_hasXnot_productiveXX_timesX3X_constantX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Both().Has("not", "productive")).Times(3).Constant<object>(1)}}, + {"g_V_hasXnot_productiveX_repeatXbothX_timesX3X_constantX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("not", "productive").Repeat(__.Both()).Times(3).Constant<object>(1)}}, + {"g_VX1_2_3X_repeatXboth_barrierX_emit_timesX2X_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"], p["vid2"], p["vid3"]).Repeat(__.Both().Barrier()).Emit().Times(2).Path()}}, + {"g_V_order_byXname_descX_repeatXboth_simplePath_order_byXname_descXX_timesX2X_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Order().By("name", Order.Desc).Repeat(__.Both().SimplePath().Order().By("name", Order.Desc)).Times(2).Path()}}, + {"g_V_repeatXboth_repeatXorder_byXnameXX_timesX1XX_timesX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Both().Repeat(__.Order().By("name")).Times(1)).Times(1)}}, + {"g_V_order_byXname_descX_repeatXlocalXout_order_byXnameXXX_timesX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Order().By("name", Order.Desc).Repeat(__.Local<object>(__.Out().Order().By("name"))).Times(1)}}, + {"g_V_order_byXnameX_repeatXlocalXboth_simplePath_order_byXnameXXX_timesX2X_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Order().By("name").Repeat(__.Local<object>(__.Both().SimplePath().Order().By("name"))).Times(2).Path()}}, + {"g_V_repeatXunionXoutXknowsX_order_byXnameX_inXcreatedX_order_byXnameXXX_timesX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Union<object>(__.Out("knows").Order().By("name"), __.In("created").Order().By("name"))).Times(1)}}, {"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")}}, @@ -563,7 +571,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_withoutStrategiesXPathProcessorStrategyX_V_asXaX_selectXaX_byXvaluesXnameXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithoutStrategies(typeof(PathProcessorStrategy)).V().As("a").Select<object>("a").By(__.Values<object>("name"))}}, {"g_withStrategiesXPathRetractionStrategyX_V", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new PathRetractionStrategy()).V()}}, {"g_withoutStrategiesXPathRetractionStrategyX_V", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithoutStrategies(typeof(PathRetractionStrategy)).V()}}, - {"g_V_shortestpath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("v").Both().As("v").Project<object>("src", "tgt", "p").By(__.Select<object>(Pop.First, "v")).By(__.Select<object>(Pop.Last, "v")).By(__.Select<object>(Pop.All, "v")).As("triple").Group("x").By(__.Select<object>("src", "tgt")).By(__.Select<object>("p").Fold()).Select<object>("tgt").Barrier().Repeat(__.Both().As("v").Project<object>("src", "tgt", "p").By(__.Se [...] + {"g_V_shortestpath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("v").Both().As("v").Project<object>("src", "tgt", "p").By(__.Select<object>(Pop.First, "v")).By(__.Select<object>(Pop.Last, "v")).By(__.Select<object>(Pop.All, "v")).As("triple").Group("x").By(__.Select<object>("src", "tgt")).By(__.Select<object>("p").Fold()).Select<object>("tgt").Barrier().Repeat(__.Both().As("v").Project<object>("src", "tgt", "p").By(__.Se [...] {"g_V_playlist_paths", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new SeedStrategy(seed: 99999)).V().Has("name", "Bob_Dylan").In("sungBy").As("a").Repeat(__.Out().Order().By(Order.Shuffle).SimplePath().From("a")).Until(__.Out("writtenBy").Has("name", "Johnny_Cash")).Limit<object>(1).As("b").Repeat(__.Out().Order().By(Order.Shuffle).As("c").SimplePath().From("b").To("c")).Until(__.Out("sungBy").Has("name", "Gratef [...] {"g_withStrategiesXProductiveByStrategyX_V_group_byXageX_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy()).V().Group<object, object>().By("age").By("name")}}, {"g_withoutStrategiesXProductiveByStrategyX_V_group_byXageX_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithoutStrategies(typeof(ProductiveByStrategy)).V().Group<object, object>().By("age").By("name")}}, @@ -594,8 +602,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXbothE_otherV_hasXage_ltX30XXX_timesX2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithoutStrategies(typeof(RepeatUnrollStrategy)).V().Repeat(__.BothE().OtherV().Has("age", P.Lt(30))).Times(2)}}, {"g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_limitX1XX_timesX2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new RepeatUnrollStrategy()).V().Repeat(__.Both().Limit<object>(1)).Times(2)}}, {"g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_limitX1XX_timesX2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithoutStrategies(typeof(RepeatUnrollStrategy)).V().Repeat(__.Both().Limit<object>(1)).Times(2)}}, - {"g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new RepeatUnrollStrategy()).V().Order().Repeat(__.Both().Order().Aggregate("x")).Times(2).Limit<object>(5)}}, - {"g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithoutStrategies(typeof(RepeatUnrollStrategy)).V().Order().Repeat(__.Both().Order().Aggregate("x")).Times(2).Limit<object>(5)}}, + {"g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new RepeatUnrollStrategy()).V().Order().Repeat(__.Both().Order().Aggregate("x")).Times(2).Limit<object>(10)}}, + {"g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithoutStrategies(typeof(RepeatUnrollStrategy)).V().Order().Repeat(__.Both().Order().Aggregate("x")).Times(2).Limit<object>(10)}}, {"g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new RepeatUnrollStrategy()).V().Repeat(__.Both().Sample(1)).Times(2)}}, {"g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithoutStrategies(typeof(RepeatUnrollStrategy)).V().Repeat(__.Both().Sample(1)).Times(2)}}, {"g_withStrategiesXReservedKeysVerificationStrategyXthrowException_trueXX_addVXpersonX_propertyXid_123X_propertyXname_markoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ReservedKeysVerificationStrategy(throwException: true)).AddV("person").Property("id", 123).Property("name", "marko")}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index 3c3d92fc6b..acaeb5389d 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -121,6 +121,14 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX0X_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "marko").Repeat(gremlingo.T__.Out("created")).Times(0).Values("name")}}, "g_V_haxXperson_name_markoX_timesX1X_repeatXoutXcreatedXX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "marko").Times(1).Repeat(gremlingo.T__.Out("created")).Values("name")}}, "g_V_haxXperson_name_markoX_timesX0X_repeatXoutXcreatedXX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "marko").Times(0).Repeat(gremlingo.T__.Out("created")).Values("name")}}, + "g_V_repeatXboth_hasXnot_productiveXX_timesX3X_constantX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Repeat(gremlingo.T__.Both().Has("not", "productive")).Times(3).Constant(1)}}, + "g_V_hasXnot_productiveX_repeatXbothX_timesX3X_constantX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("not", "productive").Repeat(gremlingo.T__.Both()).Times(3).Constant(1)}}, + "g_VX1_2_3X_repeatXboth_barrierX_emit_timesX2X_path": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"], p["vid2"], p["vid3"]).Repeat(gremlingo.T__.Both().Barrier()).Emit().Times(2).Path()}}, + "g_V_order_byXname_descX_repeatXboth_simplePath_order_byXname_descXX_timesX2X_path": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Order().By("name", gremlingo.Order.Desc).Repeat(gremlingo.T__.Both().SimplePath().Order().By("name", gremlingo.Order.Desc)).Times(2).Path()}}, + "g_V_repeatXboth_repeatXorder_byXnameXX_timesX1XX_timesX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Repeat(gremlingo.T__.Both().Repeat(gremlingo.T__.Order().By("name")).Times(1)).Times(1)}}, + "g_V_order_byXname_descX_repeatXlocalXout_order_byXnameXXX_timesX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Order().By("name", gremlingo.Order.Desc).Repeat(gremlingo.T__.Local(gremlingo.T__.Out().Order().By("name"))).Times(1)}}, + "g_V_order_byXnameX_repeatXlocalXboth_simplePath_order_byXnameXXX_timesX2X_path": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Order().By("name").Repeat(gremlingo.T__.Local(gremlingo.T__.Both().SimplePath().Order().By("name"))).Times(2).Path()}}, + "g_V_repeatXunionXoutXknowsX_order_byXnameX_inXcreatedX_order_byXnameXXX_timesX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Repeat(gremlingo.T__.Union(gremlingo.T__.Out("knows").Order().By("name"), gremlingo.T__.In("created").Order().By("name"))).Times(1)}}, "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")}}, @@ -533,7 +541,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_withoutStrategiesXPathProcessorStrategyX_V_asXaX_selectXaX_byXvaluesXnameXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithoutStrategies(gremlingo.PathProcessorStrategy()).V().As("a").Select("a").By(gremlingo.T__.Values("name"))}}, "g_withStrategiesXPathRetractionStrategyX_V": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.PathRetractionStrategy()).V()}}, "g_withoutStrategiesXPathRetractionStrategyX_V": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithoutStrategies(gremlingo.PathRetractionStrategy()).V()}}, - "g_V_shortestpath": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().As("v").Both().As("v").Project("src", "tgt", "p").By(gremlingo.T__.Select(gremlingo.Pop.First, "v")).By(gremlingo.T__.Select(gremlingo.Pop.Last, "v")).By(gremlingo.T__.Select(gremlingo.Pop.All, "v")).As("triple").Group("x").By(gremlingo.T__.Select("src", "tgt")).By(gremlingo.T__.Select("p").Fold()).Select("tgt").Barrier().Repeat(gremlingo.T__.Both().As("v").P [...] + "g_V_shortestpath": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().As("v").Both().As("v").Project("src", "tgt", "p").By(gremlingo.T__.Select(gremlingo.Pop.First, "v")).By(gremlingo.T__.Select(gremlingo.Pop.Last, "v")).By(gremlingo.T__.Select(gremlingo.Pop.All, "v")).As("triple").Group("x").By(gremlingo.T__.Select("src", "tgt")).By(gremlingo.T__.Select("p").Fold()).Select("tgt").Barrier().Repeat(gremlingo.T__.Both().As("v").P [...] "g_V_playlist_paths": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.SeedStrategy(gremlingo.SeedStrategyConfig{Seed: 99999})).V().Has("name", "Bob_Dylan").In("sungBy").As("a").Repeat(gremlingo.T__.Out().Order().By(gremlingo.Order.Shuffle).SimplePath().From("a")).Until(gremlingo.T__.Out("writtenBy").Has("name", "Johnny_Cash")).Limit(1).As("b").Repeat(gremlingo.T__.Out().Order().By(gremlingo.Order.Shuffle) [...] "g_withStrategiesXProductiveByStrategyX_V_group_byXageX_byXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.ProductiveByStrategy()).V().Group().By("age").By("name")}}, "g_withoutStrategiesXProductiveByStrategyX_V_group_byXageX_byXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithoutStrategies(gremlingo.ProductiveByStrategy()).V().Group().By("age").By("name")}}, @@ -564,8 +572,8 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXbothE_otherV_hasXage_ltX30XXX_timesX2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithoutStrategies(gremlingo.RepeatUnrollStrategy()).V().Repeat(gremlingo.T__.BothE().OtherV().Has("age", gremlingo.P.Lt(30))).Times(2)}}, "g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_limitX1XX_timesX2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.RepeatUnrollStrategy()).V().Repeat(gremlingo.T__.Both().Limit(1)).Times(2)}}, "g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_limitX1XX_timesX2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithoutStrategies(gremlingo.RepeatUnrollStrategy()).V().Repeat(gremlingo.T__.Both().Limit(1)).Times(2)}}, - "g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.RepeatUnrollStrategy()).V().Order().Repeat(gremlingo.T__.Both().Order().Aggregate("x")).Times(2).Limit(5)}}, - "g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithoutStrategies(gremlingo.RepeatUnrollStrategy()).V().Order().Repeat(gremlingo.T__.Both().Order().Aggregate("x")).Times(2).Limit(5)}}, + "g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.RepeatUnrollStrategy()).V().Order().Repeat(gremlingo.T__.Both().Order().Aggregate("x")).Times(2).Limit(10)}}, + "g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithoutStrategies(gremlingo.RepeatUnrollStrategy()).V().Order().Repeat(gremlingo.T__.Both().Order().Aggregate("x")).Times(2).Limit(10)}}, "g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.RepeatUnrollStrategy()).V().Repeat(gremlingo.T__.Both().Sample(1)).Times(2)}}, "g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithoutStrategies(gremlingo.RepeatUnrollStrategy()).V().Repeat(gremlingo.T__.Both().Sample(1)).Times(2)}}, "g_withStrategiesXReservedKeysVerificationStrategyXthrowException_trueXX_addVXpersonX_propertyXid_123X_propertyXname_markoX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.ReservedKeysVerificationStrategy(gremlingo.ReservedKeysVerificationStrategyConfig{ThrowException: true})).AddV("person").Property("id", 123).Property("name", "marko")}}, 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 e01d05fbc1..3da1ae986d 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 @@ -152,6 +152,14 @@ const gremlins = { g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX0X_name: [function({g}) { return g.V().has("person", "name", "marko").repeat(__.out("created")).times(0).values("name") }], g_V_haxXperson_name_markoX_timesX1X_repeatXoutXcreatedXX_name: [function({g}) { return g.V().has("person", "name", "marko").times(1).repeat(__.out("created")).values("name") }], g_V_haxXperson_name_markoX_timesX0X_repeatXoutXcreatedXX_name: [function({g}) { return g.V().has("person", "name", "marko").times(0).repeat(__.out("created")).values("name") }], + g_V_repeatXboth_hasXnot_productiveXX_timesX3X_constantX1X: [function({g}) { return g.V().repeat(__.both().has("not", "productive")).times(3).constant(1) }], + g_V_hasXnot_productiveX_repeatXbothX_timesX3X_constantX1X: [function({g}) { return g.V().has("not", "productive").repeat(__.both()).times(3).constant(1) }], + g_VX1_2_3X_repeatXboth_barrierX_emit_timesX2X_path: [function({g, vid3, vid2, vid1}) { return g.V(vid1, vid2, vid3).repeat(__.both().barrier()).emit().times(2).path() }], + g_V_order_byXname_descX_repeatXboth_simplePath_order_byXname_descXX_timesX2X_path: [function({g}) { return g.V().order().by("name", Order.desc).repeat(__.both().simplePath().order().by("name", Order.desc)).times(2).path() }], + g_V_repeatXboth_repeatXorder_byXnameXX_timesX1XX_timesX1X: [function({g}) { return g.V().repeat(__.both().repeat(__.order().by("name")).times(1)).times(1) }], + g_V_order_byXname_descX_repeatXlocalXout_order_byXnameXXX_timesX1X: [function({g}) { return g.V().order().by("name", Order.desc).repeat(__.local(__.out().order().by("name"))).times(1) }], + g_V_order_byXnameX_repeatXlocalXboth_simplePath_order_byXnameXXX_timesX2X_path: [function({g}) { return g.V().order().by("name").repeat(__.local(__.both().simplePath().order().by("name"))).times(2).path() }], + g_V_repeatXunionXoutXknowsX_order_byXnameX_inXcreatedX_order_byXnameXXX_timesX1X: [function({g}) { return g.V().repeat(__.union(__.out("knows").order().by("name"), __.in_("created").order().by("name"))).times(1) }], 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") }], @@ -564,7 +572,7 @@ const gremlins = { g_withoutStrategiesXPathProcessorStrategyX_V_asXaX_selectXaX_byXvaluesXnameXX: [function({g}) { return g.withoutStrategies(PathProcessorStrategy).V().as("a").select("a").by(__.values("name")) }], g_withStrategiesXPathRetractionStrategyX_V: [function({g}) { return g.withStrategies(new PathRetractionStrategy()).V() }], g_withoutStrategiesXPathRetractionStrategyX_V: [function({g}) { return g.withoutStrategies(PathRetractionStrategy).V() }], - g_V_shortestpath: [function({g}) { return g.V().as("v").both().as("v").project("src", "tgt", "p").by(__.select(Pop.first, "v")).by(__.select(Pop.last, "v")).by(__.select(Pop.all, "v")).as("triple").group("x").by(__.select("src", "tgt")).by(__.select("p").fold()).select("tgt").barrier().repeat(__.both().as("v").project("src", "tgt", "p").by(__.select(Pop.first, "v")).by(__.select(Pop.last, "v")).by(__.select(Pop.all, "v")).as("t").filter(__.select(Pop.all, "p").count(Scope.local).as(" [...] + g_V_shortestpath: [function({g}) { return g.V().as("v").both().as("v").project("src", "tgt", "p").by(__.select(Pop.first, "v")).by(__.select(Pop.last, "v")).by(__.select(Pop.all, "v")).as("triple").group("x").by(__.select("src", "tgt")).by(__.select("p").fold()).select("tgt").barrier().repeat(__.both().as("v").project("src", "tgt", "p").by(__.select(Pop.first, "v")).by(__.select(Pop.last, "v")).by(__.select(Pop.all, "v")).as("t").filter(__.select(Pop.all, "p").count(Scope.local).as(" [...] g_V_playlist_paths: [function({g}) { return g.withStrategies(new SeedStrategy({seed: 99999})).V().has("name", "Bob_Dylan").in_("sungBy").as("a").repeat(__.out().order().by(Order.shuffle).simplePath().from_("a")).until(__.out("writtenBy").has("name", "Johnny_Cash")).limit(1).as("b").repeat(__.out().order().by(Order.shuffle).as("c").simplePath().from_("b").to("c")).until(__.out("sungBy").has("name", "Grateful_Dead")).limit(1).path().from_("a").unfold().project("song", "artists").by("na [...] g_withStrategiesXProductiveByStrategyX_V_group_byXageX_byXnameX: [function({g}) { return g.withStrategies(new ProductiveByStrategy()).V().group().by("age").by("name") }], g_withoutStrategiesXProductiveByStrategyX_V_group_byXageX_byXnameX: [function({g}) { return g.withoutStrategies(ProductiveByStrategy).V().group().by("age").by("name") }], @@ -595,8 +603,8 @@ const gremlins = { g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXbothE_otherV_hasXage_ltX30XXX_timesX2X: [function({g}) { return g.withoutStrategies(RepeatUnrollStrategy).V().repeat(__.bothE().otherV().has("age", P.lt(30))).times(2) }], g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_limitX1XX_timesX2X: [function({g}) { return g.withStrategies(new RepeatUnrollStrategy()).V().repeat(__.both().limit(1)).times(2) }], g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_limitX1XX_timesX2X: [function({g}) { return g.withoutStrategies(RepeatUnrollStrategy).V().repeat(__.both().limit(1)).times(2) }], - g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X: [function({g}) { return g.withStrategies(new RepeatUnrollStrategy()).V().order().repeat(__.both().order().aggregate("x")).times(2).limit(5) }], - g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X: [function({g}) { return g.withoutStrategies(RepeatUnrollStrategy).V().order().repeat(__.both().order().aggregate("x")).times(2).limit(5) }], + g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X: [function({g}) { return g.withStrategies(new RepeatUnrollStrategy()).V().order().repeat(__.both().order().aggregate("x")).times(2).limit(10) }], + g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X: [function({g}) { return g.withoutStrategies(RepeatUnrollStrategy).V().order().repeat(__.both().order().aggregate("x")).times(2).limit(10) }], g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X: [function({g}) { return g.withStrategies(new RepeatUnrollStrategy()).V().repeat(__.both().sample(1)).times(2) }], g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X: [function({g}) { return g.withoutStrategies(RepeatUnrollStrategy).V().repeat(__.both().sample(1)).times(2) }], g_withStrategiesXReservedKeysVerificationStrategyXthrowException_trueXX_addVXpersonX_propertyXid_123X_propertyXname_markoX: [function({g}) { return g.withStrategies(new ReservedKeysVerificationStrategy({throwException: true})).addV("person").property("id", 123).property("name", "marko") }], diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 3fd2da2666..9c6be0d1a7 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -124,6 +124,14 @@ world.gremlins = { 'g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX0X_name': [(lambda g:g.V().has('person', 'name', 'marko').repeat(__.out('created')).times(0).values('name'))], 'g_V_haxXperson_name_markoX_timesX1X_repeatXoutXcreatedXX_name': [(lambda g:g.V().has('person', 'name', 'marko').times(1).repeat(__.out('created')).values('name'))], 'g_V_haxXperson_name_markoX_timesX0X_repeatXoutXcreatedXX_name': [(lambda g:g.V().has('person', 'name', 'marko').times(0).repeat(__.out('created')).values('name'))], + 'g_V_repeatXboth_hasXnot_productiveXX_timesX3X_constantX1X': [(lambda g:g.V().repeat(__.both().has('not', 'productive')).times(3).constant(1))], + 'g_V_hasXnot_productiveX_repeatXbothX_timesX3X_constantX1X': [(lambda g:g.V().has('not', 'productive').repeat(__.both()).times(3).constant(1))], + 'g_VX1_2_3X_repeatXboth_barrierX_emit_timesX2X_path': [(lambda g, vid3=None,vid2=None,vid1=None:g.V(vid1, vid2, vid3).repeat(__.both().barrier()).emit().times(2).path())], + 'g_V_order_byXname_descX_repeatXboth_simplePath_order_byXname_descXX_timesX2X_path': [(lambda g:g.V().order().by('name', Order.desc).repeat(__.both().simple_path().order().by('name', Order.desc)).times(2).path())], + 'g_V_repeatXboth_repeatXorder_byXnameXX_timesX1XX_timesX1X': [(lambda g:g.V().repeat(__.both().repeat(__.order().by('name')).times(1)).times(1))], + 'g_V_order_byXname_descX_repeatXlocalXout_order_byXnameXXX_timesX1X': [(lambda g:g.V().order().by('name', Order.desc).repeat(__.local(__.out().order().by('name'))).times(1))], + 'g_V_order_byXnameX_repeatXlocalXboth_simplePath_order_byXnameXXX_timesX2X_path': [(lambda g:g.V().order().by('name').repeat(__.local(__.both().simple_path().order().by('name'))).times(2).path())], + 'g_V_repeatXunionXoutXknowsX_order_byXnameX_inXcreatedX_order_byXnameXXX_timesX1X': [(lambda g:g.V().repeat(__.union(__.out('knows').order().by('name'), __.in_('created').order().by('name'))).times(1))], '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'))], @@ -536,7 +544,7 @@ world.gremlins = { 'g_withoutStrategiesXPathProcessorStrategyX_V_asXaX_selectXaX_byXvaluesXnameXX': [(lambda g:g.without_strategies(*[GremlinType('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy')]).V().as_('a').select('a').by(__.values('name')))], 'g_withStrategiesXPathRetractionStrategyX_V': [(lambda g:g.with_strategies(PathRetractionStrategy()).V())], 'g_withoutStrategiesXPathRetractionStrategyX_V': [(lambda g:g.without_strategies(*[GremlinType('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy')]).V())], - 'g_V_shortestpath': [(lambda g:g.V().as_('v').both().as_('v').project('src', 'tgt', 'p').by(__.select(Pop.first, 'v')).by(__.select(Pop.last, 'v')).by(__.select(Pop.all_, 'v')).as_('triple').group('x').by(__.select('src', 'tgt')).by(__.select('p').fold()).select('tgt').barrier().repeat(__.both().as_('v').project('src', 'tgt', 'p').by(__.select(Pop.first, 'v')).by(__.select(Pop.last, 'v')).by(__.select(Pop.all_, 'v')).as_('t').filter_(__.select(Pop.all_, 'p').count(Scope.local).as_('l [...] + 'g_V_shortestpath': [(lambda g:g.V().as_('v').both().as_('v').project('src', 'tgt', 'p').by(__.select(Pop.first, 'v')).by(__.select(Pop.last, 'v')).by(__.select(Pop.all_, 'v')).as_('triple').group('x').by(__.select('src', 'tgt')).by(__.select('p').fold()).select('tgt').barrier().repeat(__.both().as_('v').project('src', 'tgt', 'p').by(__.select(Pop.first, 'v')).by(__.select(Pop.last, 'v')).by(__.select(Pop.all_, 'v')).as_('t').filter_(__.select(Pop.all_, 'p').count(Scope.local).as_('l [...] 'g_V_playlist_paths': [(lambda g:g.with_strategies(SeedStrategy(seed=99999)).V().has('name', 'Bob_Dylan').in_('sungBy').as_('a').repeat(__.out().order().by(Order.shuffle).simple_path().from_('a')).until(__.out('writtenBy').has('name', 'Johnny_Cash')).limit(1).as_('b').repeat(__.out().order().by(Order.shuffle).as_('c').simple_path().from_('b').to('c')).until(__.out('sungBy').has('name', 'Grateful_Dead')).limit(1).path().from_('a').unfold().project('song', 'artists').by('name').by(__.c [...] 'g_withStrategiesXProductiveByStrategyX_V_group_byXageX_byXnameX': [(lambda g:g.with_strategies(ProductiveByStrategy()).V().group().by('age').by('name'))], 'g_withoutStrategiesXProductiveByStrategyX_V_group_byXageX_byXnameX': [(lambda g:g.without_strategies(*[GremlinType('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().group().by('age').by('name'))], @@ -567,8 +575,8 @@ world.gremlins = { 'g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXbothE_otherV_hasXage_ltX30XXX_timesX2X': [(lambda g:g.without_strategies(*[GremlinType('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy')]).V().repeat(__.both_e().other_v().has('age', P.lt(30))).times(2))], 'g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_limitX1XX_timesX2X': [(lambda g:g.with_strategies(RepeatUnrollStrategy()).V().repeat(__.both().limit(1)).times(2))], 'g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_limitX1XX_timesX2X': [(lambda g:g.without_strategies(*[GremlinType('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy')]).V().repeat(__.both().limit(1)).times(2))], - 'g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X': [(lambda g:g.with_strategies(RepeatUnrollStrategy()).V().order().repeat(__.both().order().aggregate('x')).times(2).limit(5))], - 'g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X': [(lambda g:g.without_strategies(*[GremlinType('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy')]).V().order().repeat(__.both().order().aggregate('x')).times(2).limit(5))], + 'g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X': [(lambda g:g.with_strategies(RepeatUnrollStrategy()).V().order().repeat(__.both().order().aggregate('x')).times(2).limit(10))], + 'g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X': [(lambda g:g.without_strategies(*[GremlinType('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy')]).V().order().repeat(__.both().order().aggregate('x')).times(2).limit(10))], 'g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X': [(lambda g:g.with_strategies(RepeatUnrollStrategy()).V().repeat(__.both().sample(1)).times(2))], 'g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X': [(lambda g:g.without_strategies(*[GremlinType('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy')]).V().repeat(__.both().sample(1)).times(2))], 'g_withStrategiesXReservedKeysVerificationStrategyXthrowException_trueXX_addVXpersonX_propertyXid_123X_propertyXname_markoX': [(lambda g:g.with_strategies(ReservedKeysVerificationStrategy(throw_exception=True)).add_v('person').property('id', 123).property('name', 'marko'))], diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/World.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/World.java index 4ed0480098..d0b0cc366f 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/World.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/World.java @@ -45,7 +45,8 @@ public interface World { "not @GraphComputerVerificationMidVNotSupported and not @GraphComputerVerificationElementSupported and " + "not @GraphComputerVerificationInjectionNotSupported and " + "not @GraphComputerVerificationStarGraphExceeded and not @GraphComputerVerificationReferenceOnly and " + - "not @TinkerServiceRegistry and not @InsertionOrderingRequired"; + "not @TinkerServiceRegistry and not @InsertionOrderingRequired and " + + "not @GraphComputerVerificationOrderingNotSupported"; /** * Gets a {@link GraphTraversalSource} that is backed by the specified {@link GraphData}. For {@code null}, the 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..30c1ec5b4f 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 @@ -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"]}] | - diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/integrated/RepeatUnrollStrategy.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/integrated/RepeatUnrollStrategy.feature index 9ca44f3f0f..0f72ca3c14 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/integrated/RepeatUnrollStrategy.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/integrated/RepeatUnrollStrategy.feature @@ -211,11 +211,11 @@ Feature: Step - RepeatUnrollStrategy # this traversal is not expected to be unrolled by the strategy but should have consistent semantics compared to traversal without the strategy applied @WithRepeatUnrollStrategy @GraphComputerVerificationStrategyNotSupported - Scenario: g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X + Scenario: g_withStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X Given the modern graph And the traversal of """ - g.withStrategies(RepeatUnrollStrategy).V().order().repeat(both().order().aggregate('x')).times(2).limit(5) + g.withStrategies(RepeatUnrollStrategy).V().order().repeat(both().order().aggregate('x')).times(2).limit(10) """ When iterated to list Then the result should be unordered @@ -223,15 +223,20 @@ Feature: Step - RepeatUnrollStrategy | v[marko] | | v[marko] | | v[marko] | - | v[lop] | - | v[josh] | + | v[marko] | + | v[marko] | + | v[marko] | + | v[marko] | + | v[vadas] | + | v[vadas] | + | v[vadas] | @GraphComputerVerificationStrategyNotSupported - Scenario: g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX5X + Scenario: g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_aggregateXxXX_timesX2X_limitX10X Given the modern graph And the traversal of """ - g.withoutStrategies(RepeatUnrollStrategy).V().order().repeat(both().order().aggregate('x')).times(2).limit(5) + g.withoutStrategies(RepeatUnrollStrategy).V().order().repeat(both().order().aggregate('x')).times(2).limit(10) """ When iterated to list Then the result should be unordered @@ -239,9 +244,14 @@ Feature: Step - RepeatUnrollStrategy | v[marko] | | v[marko] | | v[marko] | - | v[lop] | - | v[josh] | - + | v[marko] | + | v[marko] | + | v[marko] | + | v[marko] | + | v[vadas] | + | v[vadas] | + | v[vadas] | + # this traversal is not expected to be unrolled by the strategy but should have consistent semantics compared to traversal without the strategy applied @WithRepeatUnrollStrategy @GraphComputerVerificationStrategyNotSupported @@ -252,7 +262,7 @@ Feature: Step - RepeatUnrollStrategy g.withStrategies(RepeatUnrollStrategy).V().repeat(both().sample(1)).times(2) """ When iterated to list - Then the result should have a count of 6 + Then the result should have a count of 1 @GraphComputerVerificationStrategyNotSupported Scenario: g_withoutStrategiesXRepeatUnrollStrategyX_V_repeatXboth_sampleX1XX_timesX2X @@ -262,4 +272,4 @@ Feature: Step - RepeatUnrollStrategy g.withoutStrategies(RepeatUnrollStrategy).V().repeat(both().sample(1)).times(2) """ When iterated to list - Then the result should have a count of 6 + Then the result should have a count of 1
