This is an automated email from the ASF dual-hosted git repository.
colegreer pushed a commit to branch 3.8-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/3.8-dev by this push:
new c778125311 [TINKERPOP-3207] Add Barriers to GroupSideEffect,
GroupCountSideEffect, TreeSideEffect, and Subgraph Steps (#3254)
c778125311 is described below
commit c778125311b1b1f8ddc5393344214d89dce2486d
Author: Cole Greer <[email protected]>
AuthorDate: Mon Oct 27 18:24:00 2025 -0700
[TINKERPOP-3207] Add Barriers to GroupSideEffect, GroupCountSideEffect,
TreeSideEffect, and Subgraph Steps (#3254)
Introduces SideEffectBarrierStep which adds a LocalBarrier to
SideEffectStep. All of GroupSideEffect, GroupCountSideEffect, TreeSideEffect,
and Subgraph steps are converted over to this new base class. This
implementation on the existing design of AggregateGlobalStep relative to the
non-blocking AggregateLocalStep.
---
CHANGELOG.asciidoc | 1 +
docs/src/dev/provider/gremlin-semantics.asciidoc | 3 +-
docs/src/reference/the-traversal.asciidoc | 9 +-
docs/src/upgrade/release-3.8.x.asciidoc | 43 ++++++++++
.../step/sideEffect/GroupCountSideEffectStep.java | 2 +-
.../step/sideEffect/GroupSideEffectStep.java | 2 +-
.../step/sideEffect/SideEffectBarrierStep.java | 99 ++++++++++++++++++++++
.../traversal/step/sideEffect/SubgraphStep.java | 2 +-
.../step/sideEffect/TreeSideEffectStep.java | 2 +-
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 9 +-
gremlin-go/driver/cucumber/gremlin.go | 9 +-
.../test/cucumber/feature-steps.js | 3 +
.../gremlin-javascript/test/cucumber/gremlin.js | 9 +-
gremlin-python/src/main/python/radish/gremlin.py | 9 +-
.../gremlin/driver/remote/RemoteWorld.java | 5 ++
.../traversal/step/sideEffect/GroupCountTest.java | 11 ++-
.../gremlin/test/features/sideEffect/Group.feature | 32 +++++++
.../test/features/sideEffect/GroupCount.feature | 38 ++++++++-
.../test/features/sideEffect/Subgraph.feature | 17 ++++
.../gremlin/test/features/sideEffect/Tree.feature | 35 +++++++-
.../spark/SparkGraphFeatureIntegrateTest.java | 4 +
.../optimization/SparkInterceptorStrategyTest.java | 2 +-
.../tinkerpop/gremlin/tinkergraph/TinkerWorld.java | 5 ++
23 files changed, 326 insertions(+), 25 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 2f5d1ddf14..fdfb4cb6ba 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -99,6 +99,7 @@ This release also includes changes from <<release-3-7-XXX,
3.7.XXX>>.
* Removed `P.getOriginalValue()` in favor of `P.getValue()`.
* Simplified comparability semantics from ternary boolean logic to binary
logic.
* Introduced `NotP` class for negation of `P`.
+* Add barriers to `group(String)`, `groupCount(String)`, `tree(String)` and
`subgraph(String)` steps.
* Increase minimum Java version from 1.8 to 11 for building and running.
* Moved all lambda oriented Gremlin tests to `LambdaStepTest` in the Java test
suite.
* Removed the `@RemoteOnly` testing tag in Gherkin as lambda tests have all
been moved to the Java test suite.
diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc
b/docs/src/dev/provider/gremlin-semantics.asciidoc
index a760c85059..12bcf64107 100644
--- a/docs/src/dev/provider/gremlin-semantics.asciidoc
+++ b/docs/src/dev/provider/gremlin-semantics.asciidoc
@@ -1576,7 +1576,8 @@ key.
The `groupCount()` step can be used as both a map step and a side-effect step.
As a map step, it returns a `Map` with
the counted objects as keys and their counts as values. As a side-effect step,
it stores the counts in a side-effect and
-passes the traverser to the next step unchanged.
+passes the traverser to the next step unchanged. Note that both the map and
side-effect forms of this step are barriers
+must fully iterate the traversal before returning any results.
*Exceptions*
diff --git a/docs/src/reference/the-traversal.asciidoc
b/docs/src/reference/the-traversal.asciidoc
index 6b7f1ffe7e..d563c38b3d 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -5015,20 +5015,19 @@ g.V().has('name','josh').out('created').values('name').
<1> When the `tree()` is created, vertex 3 and 5 are unique and thus, form
unique branches in the tree structure.
<2> When the `tree()` is `by()`-modulated by `label`, then vertex 3 and 5 are
both "software" and thus are merged to a single node in the tree.
-The `tree()` step can also take a side-effect key as an argument. When using
this form, the `Tree` is constructed
-lazily, such that it becomes possible to assess its contents as each traverser
passes through.
+The `tree()` step can also take a side-effect key as an argument. When using
this form, the `Tree` is is built up in a
+side-effect as each traverser passes through. The `Tree` can later be accessed
by either `select()` or `cap()`.
[gremlin-groovy,modern]
----
g.V().has('name','josh').out('created').values('name').tree('x').select('x')
----
-You can use `cap()` step to force `tree()` to consume the traversal stream
eagerly and output results similar to prior
-examples.
+It is possible to force lazy construction of the tree by embedding inside a
`local()` step.
[gremlin-groovy,modern]
----
-g.V().has('name','josh').out('created').values('name').tree('x').cap('x')
+g.V().has('name','josh').out('created').values('name').local(tree('x')).select('x')
----
*Additional References*
diff --git a/docs/src/upgrade/release-3.8.x.asciidoc
b/docs/src/upgrade/release-3.8.x.asciidoc
index fe3dc2f0bf..e5e321522e 100644
--- a/docs/src/upgrade/release-3.8.x.asciidoc
+++ b/docs/src/upgrade/release-3.8.x.asciidoc
@@ -885,6 +885,49 @@ g.with_strategies(OptionsStrategy(options=myOptions))
g.with_strategies(OptionsStrategy(**myOptions))
----
+==== Add barrier to most SideEffect steps
+
+Prior to 3.8.0, the `group(String)`, `groupCount(String)`, `tree(String)` and
`subgraph(String)` steps were non-blocking,
+in that they allowed traversers to pass through without fully iterating the
traversal and fully computing the side
+effect. Consider the following example:
+
+[source, groovy]
+----
+// 3.7.4
+gremlin> g.V().groupCount("x").select("x")
+==>[v[1]:1]
+==>[v[1]:1,v[2]:1]
+==>[v[1]:1,v[2]:1,v[3]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1,v[6]:1]
+----
+
+As of 3.8.0, all of these steps now implement `LocalBarrier`, meaning that the
traversal is fully iterated before any
+results are passed. This guarantees that a traversal will produce the same
results regardless of it is evaluated in a
+lazy (DFS) or eager (BFS) fashion. Any usages which are reliant on the
previous "one-at-a-time" accumulation of results
+can still achieve this by embedding the side effect step inside a `local()`
step.
+
+[source, groovy]
+----
+// 3.8.0
+gremlin> g.V().groupCount("x").select("x")
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1,v[6]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1,v[6]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1,v[6]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1,v[6]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1,v[6]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1,v[6]:1]
+
+gremlin> g.V().local(groupCount("x")).select("x")
+==>[v[1]:1]
+==>[v[1]:1,v[2]:1]
+==>[v[1]:1,v[2]:1,v[3]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1]
+==>[v[1]:1,v[2]:1,v[3]:1,v[4]:1,v[5]:1,v[6]:1]
+----
+
==== choose() Semantics
Several enhancements and clarifications have been made to the `choose()` step
in TinkerPop 3.8.0 to improve its behavior
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountSideEffectStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountSideEffectStep.java
index 186153647d..7674b1dbe6 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountSideEffectStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountSideEffectStep.java
@@ -38,7 +38,7 @@ import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public class GroupCountSideEffectStep<S, E> extends SideEffectStep<S>
implements SideEffectCapable<Map<E, Long>, Map<E, Long>>, TraversalParent,
ByModulating {
+public class GroupCountSideEffectStep<S, E> extends SideEffectBarrierStep<S>
implements SideEffectCapable<Map<E, Long>, Map<E, Long>>, TraversalParent,
ByModulating {
private Traversal.Admin<S, E> keyTraversal = null;
private String sideEffectKey;
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupSideEffectStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupSideEffectStep.java
index 7fe9626495..3afb36dd28 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupSideEffectStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupSideEffectStep.java
@@ -47,7 +47,7 @@ import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class GroupSideEffectStep<S, K, V> extends SideEffectStep<S>
+public final class GroupSideEffectStep<S, K, V> extends
SideEffectBarrierStep<S>
implements SideEffectCapable<Map<K, ?>, Map<K, V>>, TraversalParent,
ByModulating, Grouping<S, K, V> {
private char state = 'k';
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectBarrierStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectBarrierStep.java
new file mode 100644
index 0000000000..8511134e8c
--- /dev/null
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectBarrierStep.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.LocalBarrier;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
+import
org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
+
+import java.util.NoSuchElementException;
+
+public abstract class SideEffectBarrierStep<S> extends AbstractStep<S, S>
implements LocalBarrier<S> {
+ protected TraverserSet<S> barrier;
+
+ public SideEffectBarrierStep(final Traversal.Admin traversal) {
+ super(traversal);
+ this.barrier = (TraverserSet<S>)
this.traversal.getTraverserSetSupplier().get();
+ }
+
+ protected abstract void sideEffect(final Traverser.Admin<S> traverser);
+
+ @Override
+ protected Traverser.Admin<S> processNextStart() {
+ if (this.barrier.isEmpty()) {
+ this.processAllStarts();
+ }
+ return this.barrier.remove();
+ }
+
+ @Override
+ public void processAllStarts() {
+ while (this.starts.hasNext()) {
+ final Traverser.Admin<S> traverser = this.starts.next();
+ sideEffect(traverser);
+
+ traverser.setStepId(this.getNextStep().getId());
+ // when barrier is reloaded, the traversers should be at the next
step
+ this.barrier.add(traverser);
+ }
+ }
+
+ @Override
+ public boolean hasNextBarrier() {
+ if (this.barrier.isEmpty()) {
+ this.processAllStarts();
+ }
+ return !this.barrier.isEmpty();
+ }
+
+ @Override
+ public TraverserSet<S> nextBarrier() throws NoSuchElementException {
+ if (this.barrier.isEmpty()) {
+ this.processAllStarts();
+ }
+ if (this.barrier.isEmpty())
+ throw FastNoSuchElementException.instance();
+ else {
+ final TraverserSet<S> temp = this.barrier;
+ this.barrier = (TraverserSet<S>)
this.traversal.getTraverserSetSupplier().get();
+ return temp;
+ }
+ }
+
+ @Override
+ public void addBarrier(TraverserSet<S> barrier) {
+ this.barrier.addAll(barrier);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ this.barrier.clear();
+ }
+
+ @Override
+ public SideEffectBarrierStep<S> clone() {
+ final SideEffectBarrierStep<S> clone = (SideEffectBarrierStep<S>)
super.clone();
+ clone.barrier = (TraverserSet<S>)
this.traversal.getTraverserSetSupplier().get();
+ return clone;
+ }
+}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SubgraphStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SubgraphStep.java
index 0bd81970ef..3d10239bd9 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SubgraphStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SubgraphStep.java
@@ -45,7 +45,7 @@ import java.util.Set;
* @author Stephen Mallette (http://stephen.genoprime.com)
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class SubgraphStep extends SideEffectStep<Edge> implements
SideEffectCapable {
+public final class SubgraphStep extends SideEffectBarrierStep<Edge> implements
SideEffectCapable {
private static final Set<TraverserRequirement> REQUIREMENTS = EnumSet.of(
TraverserRequirement.OBJECT,
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java
index 1f34bf210f..2a89268920 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java
@@ -41,7 +41,7 @@ import java.util.function.Supplier;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class TreeSideEffectStep<S> extends SideEffectStep<S> implements
SideEffectCapable<Tree, Tree>, TraversalParent, ByModulating, PathProcessor {
+public final class TreeSideEffectStep<S> extends SideEffectBarrierStep<S>
implements SideEffectCapable<Tree, Tree>, TraversalParent, ByModulating,
PathProcessor {
private TraversalRing<Object, Object> traversalRing;
private String sideEffectKey;
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index b85468c14c..05174930af 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -1953,6 +1953,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_group_by_byXout_label_limitX0X_foldX_selectXvaluesX_unfold", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Group<object,
object>().By().By(__.Out().Label().Limit<object>(0).Fold()).Select<object>(Column.Values).Unfold<object>()}},
{"g_V_group_by_byXout_label_limitX10X_foldX_selectXvaluesX_unfold_orderXlocalX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Group<object,
object>().By().By(__.Out().Label().Limit<object>(10).Fold()).Select<object>(Column.Values).Unfold<object>().Order(Scope.Local)}},
{"g_V_group_by_byXout_label_tailX10X_foldX_selectXvaluesX_unfold_orderXlocalX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Group<object,
object>().By().By(__.Out().Label().Tail<object>(10).Fold()).Select<object>(Column.Values).Unfold<object>().Order(Scope.Local)}},
+ {"g_V_groupXaX_byXnameX_by_selectXaX_countXlocalX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Group("a").By("name").By().Select<object>("a").Count(Scope.Local)}},
+ {"g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Local<object>(__.Group("a").By("name").By().Select<object>("a").Count(Scope.Local))}},
{"g_V_outXcreatedX_groupCount_byXnameX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Out("created").GroupCount<object>().By("name")}},
{"g_V_groupCount_byXageX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.V().GroupCount<object>().By("age")}},
{"g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.WithStrategies(new
ProductiveByStrategy()).V().GroupCount<object>().By("age")}},
@@ -1967,10 +1969,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_unionXrepeatXoutX_timesX2X_groupCountXmX_byXlangXX__repeatXinX_timesX2X_groupCountXmX_byXnameXX_capXmX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Union<object>(__.Repeat(__.Out()).Times(2).GroupCount("m").By("lang"),
__.Repeat(__.In()).Times(2).GroupCount("m").By("name")).Cap<object>("m")}},
{"g_V_outXcreatedX_groupCountXxX_capXxX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Out("created").GroupCount("x").Cap<object>("x")}},
{"g_V_groupCount_byXbothE_countX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().GroupCount<object>().By(__.BothE().Count())}},
-
{"g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Both().GroupCount("a").Out().Cap<object>("a").Select<object>(Column.Keys).Unfold<object>().Both().GroupCount("a").Cap<object>("a")}},
+
{"g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Both().Local<object>(__.GroupCount("a")).Out().Cap<object>("a").Select<object>(Column.Keys).Unfold<object>().Both().Local<object>(__.GroupCount("a")).Cap<object>("a")}},
{"g_V_hasXperson_name_markoX_bothXknowsX_groupCount_byXvaluesXnameX_foldX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Has("person", "name",
"marko").Both("knows").GroupCount<object>().By(__.Values<object>("name").Fold())}},
{"g_V_outXcreatedX_groupCount_byXnameX_byXageX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Out("created").GroupCount<object>().By("name").By("age")}},
{"g_V_outXcreatedX_groupCountXxX_byXnameX_byXageX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Out("created").GroupCount("x").By("name").By("age")}},
+ {"g_V_groupCountXaX_selectXaX_countXlocalX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().GroupCount("a").Select<object>("a").Count(Scope.Local)}},
+ {"g_V_localXgroupCountXaX_selectXaX_countXlocalXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Local<object>(__.GroupCount("a").Select<object>("a").Count(Scope.Local))}},
{"g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V(p["vid1"]).Out().Values<object>("name").Inject("daniel").As("a").Map<object>(__.Length()).Path()}},
{"g_injectXnull_1_3_nullX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null, 1, 3,
null)}},
{"g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(10, 20, null, 20, 10,
10).GroupCount("x").Dedup().As("y").Project<object>("a",
"b").By().By(__.Select<object>("x").Select<object>(__.Select<object>("y")))}},
@@ -2050,6 +2054,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_VX1X_outEXknowsX_subgraphXsgX_name_capXsgX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V(p["vid1"]).OutE("knows").Subgraph("sg").Values<object>("name").Cap<object>("sg")}},
{"g_V_repeatXbothEXcreatedX_subgraphXsgX_outVX_timesX5X_name_dedup_capXsgX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Repeat(__.BothE("created").Subgraph("sg").OutV()).Times(5).Values<object>("name").Dedup().Cap<object>("sg")}},
{"g_V_outEXnoexistX_subgraphXsgXcapXsgX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().OutE("noexist").Subgraph("sg").Cap<object>("sg")}},
+ {"g_E_hasXweight_0_5X_subgraphXaX_selectXaX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.E().Has("weight", 0.4).Subgraph("a").Select<object>("a")}},
{"g_VX1X_out_out_tree_byXnameX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V(p["vid1"]).Out().Out().Tree<object>().By("name")}},
{"g_VX1X_out_out_tree", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.V(p["vid1"]).Out().Out().Tree<object>()}},
{"g_V_out_tree_byXageX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.V().Out().Tree<object>().By("age")}},
@@ -2060,6 +2065,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_VX1X_out_out_out_tree", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.V().Out().Out().Out().Tree<object>()}},
{"g_VX1X_outE_inV_bothE_otherV_tree", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V(p["vid1"]).OutE().InV().BothE().OtherV().Tree<object>()}},
{"g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V(p["vid1"]).OutE().InV().BothE().OtherV().Tree<object>().By("name").By(T.Label)}},
+ {"g_V_out_treeXaX_selectXaX_countXlocalX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Out().Tree("a").Select<object>("a").Count(Scope.Local)}},
+
{"g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Out().Local<object>(__.Tree("a").Select<object>("a").Count(Scope.Local))}},
};
public static ITraversal UseTraversal(string scenarioName,
GraphTraversalSource g, IDictionary<string, object> parameters,
IDictionary<string, object> sideEffects)
diff --git a/gremlin-go/driver/cucumber/gremlin.go
b/gremlin-go/driver/cucumber/gremlin.go
index 131b6b48b2..47668c7fa7 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -1923,6 +1923,8 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_V_group_by_byXout_label_limitX0X_foldX_selectXvaluesX_unfold": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Group().By().By(gremlingo.T__.Out().Label().Limit(0).Fold()).Select(gremlingo.Column.Values).Unfold()}},
"g_V_group_by_byXout_label_limitX10X_foldX_selectXvaluesX_unfold_orderXlocalX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Group().By().By(gremlingo.T__.Out().Label().Limit(10).Fold()).Select(gremlingo.Column.Values).Unfold().Order(gremlingo.Scope.Local)}},
"g_V_group_by_byXout_label_tailX10X_foldX_selectXvaluesX_unfold_orderXlocalX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Group().By().By(gremlingo.T__.Out().Label().Tail(10).Fold()).Select(gremlingo.Column.Values).Unfold().Order(gremlingo.Scope.Local)}},
+ "g_V_groupXaX_byXnameX_by_selectXaX_countXlocalX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Group("a").By("name").By().Select("a").Count(gremlingo.Scope.Local)}},
+ "g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Local(gremlingo.T__.Group("a").By("name").By().Select("a").Count(gremlingo.Scope.Local))}},
"g_V_outXcreatedX_groupCount_byXnameX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Out("created").GroupCount().By("name")}},
"g_V_groupCount_byXageX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().GroupCount().By("age")}},
"g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.WithStrategies(gremlingo.ProductiveByStrategy()).V().GroupCount().By("age")}},
@@ -1937,10 +1939,12 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_V_unionXrepeatXoutX_timesX2X_groupCountXmX_byXlangXX__repeatXinX_timesX2X_groupCountXmX_byXnameXX_capXmX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Union(gremlingo.T__.Repeat(gremlingo.T__.Out()).Times(2).GroupCount("m").By("lang"),
gremlingo.T__.Repeat(gremlingo.T__.In()).Times(2).GroupCount("m").By("name")).Cap("m")}},
"g_V_outXcreatedX_groupCountXxX_capXxX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Out("created").GroupCount("x").Cap("x")}},
"g_V_groupCount_byXbothE_countX": {func(g *gremlingo.GraphTraversalSource,
p map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().GroupCount().By(gremlingo.T__.BothE().Count())}},
-
"g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Both().GroupCount("a").Out().Cap("a").Select(gremlingo.Column.Keys).Unfold().Both().GroupCount("a").Cap("a")}},
+
"g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Both().Local(gremlingo.T__.GroupCount("a")).Out().Cap("a").Select(gremlingo.Column.Keys).Unfold().Both().Local(gremlingo.T__.GroupCount("a")).Cap("a")}},
"g_V_hasXperson_name_markoX_bothXknowsX_groupCount_byXvaluesXnameX_foldX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.V().Has("person", "name",
"marko").Both("knows").GroupCount().By(gremlingo.T__.Values("name").Fold())}},
"g_V_outXcreatedX_groupCount_byXnameX_byXageX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Out("created").GroupCount().By("name").By("age")}},
"g_V_outXcreatedX_groupCountXxX_byXnameX_byXageX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Out("created").GroupCount("x").By("name").By("age")}},
+ "g_V_groupCountXaX_selectXaX_countXlocalX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().GroupCount("a").Select("a").Count(gremlingo.Scope.Local)}},
+ "g_V_localXgroupCountXaX_selectXaX_countXlocalXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Local(gremlingo.T__.GroupCount("a").Select("a").Count(gremlingo.Scope.Local))}},
"g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V(p["vid1"]).Out().Values("name").Inject("daniel").As("a").Map(gremlingo.T__.Length()).Path()}},
"g_injectXnull_1_3_nullX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil, 1, 3,
nil)}},
"g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.Inject(10, 20, nil, 20, 10,
10).GroupCount("x").Dedup().As("y").Project("a",
"b").By().By(gremlingo.T__.Select("x").Select(gremlingo.T__.Select("y")))}},
@@ -2020,6 +2024,7 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_VX1X_outEXknowsX_subgraphXsgX_name_capXsgX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V(p["vid1"]).OutE("knows").Subgraph("sg").Values("name").Cap("sg")}},
"g_V_repeatXbothEXcreatedX_subgraphXsgX_outVX_timesX5X_name_dedup_capXsgX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Repeat(gremlingo.T__.BothE("created").Subgraph("sg").OutV()).Times(5).Values("name").Dedup().Cap("sg")}},
"g_V_outEXnoexistX_subgraphXsgXcapXsgX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().OutE("noexist").Subgraph("sg").Cap("sg")}},
+ "g_E_hasXweight_0_5X_subgraphXaX_selectXaX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.E().Has("weight",
0.4).Subgraph("a").Select("a")}},
"g_VX1X_out_out_tree_byXnameX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V(p["vid1"]).Out().Out().Tree().By("name")}},
"g_VX1X_out_out_tree": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V(p["vid1"]).Out().Out().Tree()}},
"g_V_out_tree_byXageX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().Out().Tree().By("age")}},
@@ -2030,6 +2035,8 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_VX1X_out_out_out_tree": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().Out().Out().Out().Tree()}},
"g_VX1X_outE_inV_bothE_otherV_tree": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V(p["vid1"]).OutE().InV().BothE().OtherV().Tree()}},
"g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V(p["vid1"]).OutE().InV().BothE().OtherV().Tree().By("name").By(gremlingo.T.Label)}},
+ "g_V_out_treeXaX_selectXaX_countXlocalX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Out().Tree("a").Select("a").Count(gremlingo.Scope.Local)}},
+ "g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Out().Local(gremlingo.T__.Tree("a").Select("a").Count(gremlingo.Scope.Local))}},
}
func GetTraversal(scenarioName string, g *gremlingo.GraphTraversalSource,
parameters map[string]interface{}, sideEffects map[string]interface{})
(*gremlingo.GraphTraversal, error) {
diff --git
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
index 10eea4d716..4bd8f2303c 100644
---
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
+++
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
@@ -84,6 +84,7 @@ const ignoredScenarios = {
'g_VX1X_outEXknowsX_subgraphXsgX_name_capXsgX': new
IgnoreError(ignoreReason.subgraphStepNotSupported),
'g_V_repeatXbothEXcreatedX_subgraphXsgX_outVX_timesX5X_name_dedup_capXsgX':
new IgnoreError(ignoreReason.subgraphStepNotSupported),
'g_V_outEXnoexistX_subgraphXsgXcapXsgX': new
IgnoreError(ignoreReason.subgraphStepNotSupported),
+ 'g_E_hasXweight_0_5X_subgraphXaX_selectXaX': new
IgnoreError(ignoreReason.subgraphStepNotSupported),
// javascript doesn't have tree() step yet
'g_VX1X_out_out_tree_byXnameX': new
IgnoreError(ignoreReason.treeStepNotSupported),
'g_VX1X_out_out_tree': new IgnoreError(ignoreReason.treeStepNotSupported),
@@ -95,6 +96,8 @@ const ignoredScenarios = {
'g_VX1X_out_out_out_tree': new
IgnoreError(ignoreReason.treeStepNotSupported),
'g_VX1X_outE_inV_bothE_otherV_tree': new
IgnoreError(ignoreReason.treeStepNotSupported),
'g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX': new
IgnoreError(ignoreReason.treeStepNotSupported),
+ 'g_V_out_treeXaX_selectXaX_countXlocalX': new
IgnoreError(ignoreReason.treeStepNotSupported),
+ 'g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX': new
IgnoreError(ignoreReason.treeStepNotSupported),
// An associative array containing the scenario name as key, for example:
'g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX': new
IgnoreError(ignoreReason.nullKeysInMapNotSupportedWell),
'g_V_shortestPath_edgesIncluded': new
IgnoreError(ignoreReason.needsFurtherInvestigation),
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 c527e97179..f76429d77b 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
@@ -1954,6 +1954,8 @@ const gremlins = {
g_V_group_by_byXout_label_limitX0X_foldX_selectXvaluesX_unfold:
[function({g}) { return
g.V().group().by().by(__.out().label().limit(0).fold()).select(Column.values).unfold()
}],
g_V_group_by_byXout_label_limitX10X_foldX_selectXvaluesX_unfold_orderXlocalX:
[function({g}) { return
g.V().group().by().by(__.out().label().limit(10).fold()).select(Column.values).unfold().order(Scope.local)
}],
g_V_group_by_byXout_label_tailX10X_foldX_selectXvaluesX_unfold_orderXlocalX:
[function({g}) { return
g.V().group().by().by(__.out().label().tail(10).fold()).select(Column.values).unfold().order(Scope.local)
}],
+ g_V_groupXaX_byXnameX_by_selectXaX_countXlocalX: [function({g}) { return
g.V().group("a").by("name").by().select("a").count(Scope.local) }],
+ g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX: [function({g}) {
return
g.V().local(__.group("a").by("name").by().select("a").count(Scope.local)) }],
g_V_outXcreatedX_groupCount_byXnameX: [function({g}) { return
g.V().out("created").groupCount().by("name") }],
g_V_groupCount_byXageX: [function({g}) { return
g.V().groupCount().by("age") }],
g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX:
[function({g}) { return g.withStrategies(new
ProductiveByStrategy()).V().groupCount().by("age") }],
@@ -1968,10 +1970,12 @@ const gremlins = {
g_V_unionXrepeatXoutX_timesX2X_groupCountXmX_byXlangXX__repeatXinX_timesX2X_groupCountXmX_byXnameXX_capXmX:
[function({g}) { return
g.V().union(__.repeat(__.out()).times(2).groupCount("m").by("lang"),
__.repeat(__.in_()).times(2).groupCount("m").by("name")).cap("m") }],
g_V_outXcreatedX_groupCountXxX_capXxX: [function({g}) { return
g.V().out("created").groupCount("x").cap("x") }],
g_V_groupCount_byXbothE_countX: [function({g}) { return
g.V().groupCount().by(__.bothE().count()) }],
-
g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX:
[function({g}) { return
g.V().both().groupCount("a").out().cap("a").select(Column.keys).unfold().both().groupCount("a").cap("a")
}],
+
g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX:
[function({g}) { return
g.V().both().local(__.groupCount("a")).out().cap("a").select(Column.keys).unfold().both().local(__.groupCount("a")).cap("a")
}],
g_V_hasXperson_name_markoX_bothXknowsX_groupCount_byXvaluesXnameX_foldX:
[function({g}) { return g.V().has("person", "name",
"marko").both("knows").groupCount().by(__.values("name").fold()) }],
g_V_outXcreatedX_groupCount_byXnameX_byXageX: [function({g}) { return
g.V().out("created").groupCount().by("name").by("age") }],
g_V_outXcreatedX_groupCountXxX_byXnameX_byXageX: [function({g}) { return
g.V().out("created").groupCount("x").by("name").by("age") }],
+ g_V_groupCountXaX_selectXaX_countXlocalX: [function({g}) { return
g.V().groupCount("a").select("a").count(Scope.local) }],
+ g_V_localXgroupCountXaX_selectXaX_countXlocalXX: [function({g}) { return
g.V().local(__.groupCount("a").select("a").count(Scope.local)) }],
g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path: [function({g,
vid1}) { return
g.V(vid1).out().values("name").inject("daniel").as("a").map(__.length()).path()
}],
g_injectXnull_1_3_nullX: [function({g}) { return g.inject(null, 1, 3,
null) }],
g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX:
[function({g}) { return g.inject(10, 20, null, 20, 10,
10).groupCount("x").dedup().as("y").project("a",
"b").by().by(__.select("x").select(__.select("y"))) }],
@@ -2051,6 +2055,7 @@ const gremlins = {
g_VX1X_outEXknowsX_subgraphXsgX_name_capXsgX: [function({g, vid1}) {
return g.V(vid1).outE("knows").subgraph("sg").values("name").cap("sg") }],
g_V_repeatXbothEXcreatedX_subgraphXsgX_outVX_timesX5X_name_dedup_capXsgX:
[function({g}) { return
g.V().repeat(__.bothE("created").subgraph("sg").outV()).times(5).values("name").dedup().cap("sg")
}],
g_V_outEXnoexistX_subgraphXsgXcapXsgX: [function({g}) { return
g.V().outE("noexist").subgraph("sg").cap("sg") }],
+ g_E_hasXweight_0_5X_subgraphXaX_selectXaX: [function({g}) { return
g.E().has("weight", 0.4).subgraph("a").select("a") }],
g_VX1X_out_out_tree_byXnameX: [function({g, vid1}) { return
g.V(vid1).out().out().tree().by("name") }],
g_VX1X_out_out_tree: [function({g, vid1}) { return
g.V(vid1).out().out().tree() }],
g_V_out_tree_byXageX: [function({g}) { return g.V().out().tree().by("age")
}],
@@ -2061,6 +2066,8 @@ const gremlins = {
g_VX1X_out_out_out_tree: [function({g}) { return
g.V().out().out().out().tree() }],
g_VX1X_outE_inV_bothE_otherV_tree: [function({g, vid1}) { return
g.V(vid1).outE().inV().bothE().otherV().tree() }],
g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX: [function({g, vid1})
{ return g.V(vid1).outE().inV().bothE().otherV().tree().by("name").by(T.label)
}],
+ g_V_out_treeXaX_selectXaX_countXlocalX: [function({g}) { return
g.V().out().tree("a").select("a").count(Scope.local) }],
+ g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX:
[function({g}) { return
g.V().out().local(__.tree("a").select("a").count(Scope.local)) }],
}
exports.gremlin = gremlins
diff --git a/gremlin-python/src/main/python/radish/gremlin.py
b/gremlin-python/src/main/python/radish/gremlin.py
index 34d0ecf07d..403114e6b9 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -1926,6 +1926,8 @@ world.gremlins = {
'g_V_group_by_byXout_label_limitX0X_foldX_selectXvaluesX_unfold': [(lambda
g:g.V().group().by().by(__.out().label().limit(0).fold()).select(Column.values).unfold())],
'g_V_group_by_byXout_label_limitX10X_foldX_selectXvaluesX_unfold_orderXlocalX':
[(lambda
g:g.V().group().by().by(__.out().label().limit(10).fold()).select(Column.values).unfold().order(Scope.local))],
'g_V_group_by_byXout_label_tailX10X_foldX_selectXvaluesX_unfold_orderXlocalX':
[(lambda
g:g.V().group().by().by(__.out().label().tail(10).fold()).select(Column.values).unfold().order(Scope.local))],
+ 'g_V_groupXaX_byXnameX_by_selectXaX_countXlocalX': [(lambda
g:g.V().group('a').by('name').by().select('a').count(Scope.local))],
+ 'g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX': [(lambda
g:g.V().local(__.group('a').by('name').by().select('a').count(Scope.local)))],
'g_V_outXcreatedX_groupCount_byXnameX': [(lambda
g:g.V().out('created').group_count().by('name'))],
'g_V_groupCount_byXageX': [(lambda g:g.V().group_count().by('age'))],
'g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX': [(lambda
g:g.with_strategies(ProductiveByStrategy()).V().group_count().by('age'))],
@@ -1940,10 +1942,12 @@ world.gremlins = {
'g_V_unionXrepeatXoutX_timesX2X_groupCountXmX_byXlangXX__repeatXinX_timesX2X_groupCountXmX_byXnameXX_capXmX':
[(lambda
g:g.V().union(__.repeat(__.out()).times(2).group_count('m').by('lang'),
__.repeat(__.in_()).times(2).group_count('m').by('name')).cap('m'))],
'g_V_outXcreatedX_groupCountXxX_capXxX': [(lambda
g:g.V().out('created').group_count('x').cap('x'))],
'g_V_groupCount_byXbothE_countX': [(lambda
g:g.V().group_count().by(__.both_e().count()))],
-
'g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX':
[(lambda
g:g.V().both().group_count('a').out().cap('a').select(Column.keys).unfold().both().group_count('a').cap('a'))],
+
'g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX':
[(lambda
g:g.V().both().local(__.group_count('a')).out().cap('a').select(Column.keys).unfold().both().local(__.group_count('a')).cap('a'))],
'g_V_hasXperson_name_markoX_bothXknowsX_groupCount_byXvaluesXnameX_foldX':
[(lambda g:g.V().has('person', 'name',
'marko').both('knows').group_count().by(__.values('name').fold()))],
'g_V_outXcreatedX_groupCount_byXnameX_byXageX': [(lambda
g:g.V().out('created').group_count().by('name').by('age'))],
'g_V_outXcreatedX_groupCountXxX_byXnameX_byXageX': [(lambda
g:g.V().out('created').group_count('x').by('name').by('age'))],
+ 'g_V_groupCountXaX_selectXaX_countXlocalX': [(lambda
g:g.V().group_count('a').select('a').count(Scope.local))],
+ 'g_V_localXgroupCountXaX_selectXaX_countXlocalXX': [(lambda
g:g.V().local(__.group_count('a').select('a').count(Scope.local)))],
'g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path': [(lambda g,
vid1=None:g.V(vid1).out().values('name').inject('daniel').as_('a').map(__.length()).path())],
'g_injectXnull_1_3_nullX': [(lambda g:g.inject(None, 1, 3, None))],
'g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX':
[(lambda g:g.inject(10, 20, None, 20, 10,
10).group_count('x').dedup().as_('y').project('a',
'b').by().by(__.select('x').select(__.select('y'))))],
@@ -2023,6 +2027,7 @@ world.gremlins = {
'g_VX1X_outEXknowsX_subgraphXsgX_name_capXsgX': [(lambda g,
vid1=None:g.V(vid1).out_e('knows').subgraph('sg').values('name').cap('sg'))],
'g_V_repeatXbothEXcreatedX_subgraphXsgX_outVX_timesX5X_name_dedup_capXsgX':
[(lambda
g:g.V().repeat(__.both_e('created').subgraph('sg').out_v()).times(5).values('name').dedup().cap('sg'))],
'g_V_outEXnoexistX_subgraphXsgXcapXsgX': [(lambda
g:g.V().out_e('noexist').subgraph('sg').cap('sg'))],
+ 'g_E_hasXweight_0_5X_subgraphXaX_selectXaX': [(lambda
g:g.E().has('weight', 0.4).subgraph('a').select('a'))],
'g_VX1X_out_out_tree_byXnameX': [(lambda g,
vid1=None:g.V(vid1).out().out().tree().by('name'))],
'g_VX1X_out_out_tree': [(lambda g,
vid1=None:g.V(vid1).out().out().tree())],
'g_V_out_tree_byXageX': [(lambda g:g.V().out().tree().by('age'))],
@@ -2033,4 +2038,6 @@ world.gremlins = {
'g_VX1X_out_out_out_tree': [(lambda g:g.V().out().out().out().tree())],
'g_VX1X_outE_inV_bothE_otherV_tree': [(lambda g,
vid1=None:g.V(vid1).out_e().in_v().both_e().other_v().tree())],
'g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX': [(lambda g,
vid1=None:g.V(vid1).out_e().in_v().both_e().other_v().tree().by('name').by(T.label))],
+ 'g_V_out_treeXaX_selectXaX_countXlocalX': [(lambda
g:g.V().out().tree('a').select('a').count(Scope.local))],
+ 'g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX': [(lambda
g:g.V().out().local(__.tree('a').select('a').count(Scope.local)))],
}
diff --git
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/RemoteWorld.java
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/RemoteWorld.java
index de0ff4b211..f82e3e81da 100644
---
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/RemoteWorld.java
+++
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/RemoteWorld.java
@@ -147,6 +147,11 @@ public abstract class RemoteWorld implements World {
case
"g_V_repeatXboth_repeatXorder_byXnameXX_timesX1XX_timesX1X":
case
"g_V_order_byXname_descX_repeatXboth_simplePath_order_byXname_descXX_timesX2X_path":
throw new AssumptionViolatedException("GraphComputer
doesn't order within repeat");
+ // TINKERPOP-3209
+ case "g_V_localXgroupCountXaX_selectXaX_countXlocalXX":
+ case "g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX":
+ case
"g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX":
+ throw new AssumptionViolatedException("Selecting side
effects inside local() produces inconsistent results in OLAP (See
TINKERPOP-3209)");
default:
// Do nothing
}
diff --git
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java
index 1051e2a4b3..081f3fa864 100644
---
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java
+++
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java
@@ -32,7 +32,6 @@ import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
@@ -74,7 +73,7 @@ public abstract class GroupCountTest extends
AbstractGremlinProcessTest {
public abstract Traversal<Vertex, Long>
get_g_V_unionXoutXknowsX__outXcreatedX_inXcreatedXX_groupCount_selectXvaluesX_unfold_sum();
- public abstract Traversal<Vertex, Map<Vertex, Long>>
get_g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX();
+ public abstract Traversal<Vertex, Map<Vertex, Long>>
get_g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX();
public abstract Traversal<Vertex, String>
get_g_V_both_groupCountXaX_byXlabelX_asXbX_barrier_whereXselectXaX_selectXsoftwareX_isXgtX2XXX_selectXbX_name();
@@ -237,8 +236,8 @@ public abstract class GroupCountTest extends
AbstractGremlinProcessTest {
@Test
@LoadGraphWith(MODERN)
- public void
g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX()
{
- final Traversal<Vertex, Map<Vertex, Long>> traversal =
get_g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX();
+ public void
g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX()
{
+ final Traversal<Vertex, Map<Vertex, Long>> traversal =
get_g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX();
printTraversalForm(traversal);
// [{v[1]=6, v[2]=2, v[3]=6, v[4]=6, v[5]=2, v[6]=2}]
final Map<Vertex, Long> map = traversal.next();
@@ -334,8 +333,8 @@ public abstract class GroupCountTest extends
AbstractGremlinProcessTest {
}
@Override
- public Traversal<Vertex, Map<Vertex, Long>>
get_g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX()
{
- return
g.V().both().groupCount("a").out().cap("a").select(Column.keys).unfold().both().groupCount("a").cap("a");
+ public Traversal<Vertex, Map<Vertex, Long>>
get_g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX()
{
+ return
g.V().both().local(__.groupCount("a")).out().cap("a").select(Column.keys).unfold().both().local(__.groupCount("a")).cap("a");
}
@Override
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Group.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Group.feature
index f31fdd51fc..2140d41717 100644
---
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Group.feature
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Group.feature
@@ -365,3 +365,35 @@ Feature: Step - group()
| l[software,software] |
| l[] |
| l[software] |
+
+ Scenario: g_V_groupXaX_byXnameX_by_selectXaX_countXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().group("a").by("name").by().select("a").count(local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[6].l |
+ | d[6].l |
+ | d[6].l |
+ | d[6].l |
+ | d[6].l |
+ | d[6].l |
+
+ Scenario: g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().local(group("a").by("name").by().select("a").count(local))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[1].l |
+ | d[2].l |
+ | d[3].l |
+ | d[4].l |
+ | d[5].l |
+ | d[6].l |
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/GroupCount.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/GroupCount.feature
index 861ce5fbc4..3dda98023f 100644
---
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/GroupCount.feature
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/GroupCount.feature
@@ -205,11 +205,11 @@ Feature: Step - groupCount()
| result |
| m[{"d[1].l":"d[3].l","d[3].l":"d[3].l"}] |
- Scenario:
g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX
+ Scenario:
g_V_both_localXgroupCountXaXX_out_capXaX_selectXkeysX_unfold_both_localXgroupCountXaXX_capXaX
Given the modern graph
And the traversal of
"""
-
g.V().both().groupCount("a").out().cap("a").select(Column.keys).unfold().both().groupCount("a").cap("a")
+
g.V().both().local(groupCount("a")).out().cap("a").select(Column.keys).unfold().both().local(groupCount("a")).cap("a")
"""
When iterated to list
Then the result should be unordered
@@ -243,4 +243,36 @@ Feature: Step - groupCount()
g.V().out("created").groupCount("x").by("name").by("age")
"""
When iterated to list
- Then the traversal will raise an error with message containing text of
"GroupCount step can only have one by modulator"
\ No newline at end of file
+ Then the traversal will raise an error with message containing text of
"GroupCount step can only have one by modulator"
+
+ Scenario: g_V_groupCountXaX_selectXaX_countXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().groupCount("a").select("a").count(local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[6].l |
+ | d[6].l |
+ | d[6].l |
+ | d[6].l |
+ | d[6].l |
+ | d[6].l |
+
+ Scenario: g_V_localXgroupCountXaX_selectXaX_countXlocalXX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().local(groupCount("a").select("a").count(local))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[1].l |
+ | d[2].l |
+ | d[3].l |
+ | d[4].l |
+ | d[5].l |
+ | d[6].l |
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Subgraph.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Subgraph.feature
index cc27852fa1..74ec88233f 100644
---
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Subgraph.feature
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Subgraph.feature
@@ -68,3 +68,20 @@ Feature: Step - subgraph()
| edges |
And the result should be a subgraph with the following
| vertices |
+
+ Scenario: g_E_hasXweight_0_5X_subgraphXaX_selectXaX
+ Given the modern graph
+ And the traversal of
+ """
+ g.E().has("weight", 0.4).subgraph("a").select("a")
+ """
+ When iterated next
+ Then the result should be a subgraph with the following
+ | edges |
+ | e[marko-created->lop] |
+ | e[josh-created->lop] |
+ And the result should be a subgraph with the following
+ | vertices |
+ | v[marko] |
+ | v[lop] |
+ | v[josh] |
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Tree.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Tree.feature
index 93219940f6..8f85c68226 100644
---
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Tree.feature
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Tree.feature
@@ -205,4 +205,37 @@ Feature: Step - tree()
|--marko
|--josh
|--peter
- """
\ No newline at end of file
+ """
+
+ Scenario: g_V_out_treeXaX_selectXaX_countXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().out().tree("a").select("a").count(local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[3].l |
+ | d[3].l |
+ | d[3].l |
+ | d[3].l |
+ | d[3].l |
+ | d[3].l |
+
+ @InsertionOrderingRequired
+ Scenario: g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().out().local(tree("a").select("a").count(local))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[1].l |
+ | d[1].l |
+ | d[1].l |
+ | d[2].l |
+ | d[2].l |
+ | d[3].l |
diff --git
a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/SparkGraphFeatureIntegrateTest.java
b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/SparkGraphFeatureIntegrateTest.java
index c8ca8ed6e4..3492acb5ec 100644
---
a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/SparkGraphFeatureIntegrateTest.java
+++
b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/SparkGraphFeatureIntegrateTest.java
@@ -68,6 +68,7 @@ public class SparkGraphFeatureIntegrateTest {
private static final String skipReasonLength = "Spark-Gremlin is
OLAP-oriented and for OLTP operations, linear-scan joins are required. This
particular tests takes many minutes to execute.";
private static final String skipReasonOrdering = "GraphComputer doesn't
order within repeat.";
+ private static final String skipReasonTINKERPOP3209 = "Selecting side
effects inside local() produces inconsistent results in OLAP (See
TINKERPOP-3209)";
private static final List<Pair<String, String>> skip = new
ArrayList<Pair<String,String>>() {{
add(Pair.with("g_V_both_both_count", skipReasonLength));
@@ -86,6 +87,9 @@ public class SparkGraphFeatureIntegrateTest {
add(Pair.with("g_V_repeatXunionXoutXknowsX_order_byXnameX_inXcreatedX_order_byXnameXXX_timesX1X",
skipReasonOrdering));
add(Pair.with("g_V_repeatXboth_repeatXorder_byXnameXX_timesX1XX_timesX1X",
skipReasonOrdering));
add(Pair.with("g_V_order_byXname_descX_repeatXboth_simplePath_order_byXname_descXX_timesX2X_path",
skipReasonOrdering));
+ add(Pair.with("g_V_localXgroupCountXaX_selectXaX_countXlocalXX",
skipReasonTINKERPOP3209));
+
add(Pair.with("g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX",
skipReasonTINKERPOP3209));
+
add(Pair.with("g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX",
skipReasonTINKERPOP3209));
}};
public static final class ServiceModule extends AbstractModule {
diff --git
a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/traversal/strategy/optimization/SparkInterceptorStrategyTest.java
b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/traversal/strategy/optimization/SparkInterceptorStrategyTest.java
index 6cdaf81dfb..87fc2add7a 100644
---
a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/traversal/strategy/optimization/SparkInterceptorStrategyTest.java
+++
b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/traversal/strategy/optimization/SparkInterceptorStrategyTest.java
@@ -78,7 +78,7 @@ public class SparkInterceptorStrategyTest extends
AbstractSparkTest {
assertTrue(g.getStrategies().getStrategy(SparkInterceptorStrategy.class).isPresent());
assertTrue(g.V().count().explain().toString().contains(SparkInterceptorStrategy.class.getSimpleName()));
/// groupCount(m)-test
- Traversal.Admin<Vertex, Long> traversal =
g.V().groupCount("m").by(T.label).count().asAdmin();
+ Traversal.Admin<Vertex, Long> traversal =
g.V().local(__.groupCount("m").by(T.label)).count().asAdmin();
test(SparkStarBarrierInterceptor.class, 6l, traversal);
assertEquals(1, traversal.getSideEffects().keys().size());
assertTrue(traversal.getSideEffects().exists("m"));
diff --git
a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java
b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java
index db32d7618b..e3c89e0e6e 100644
---
a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java
+++
b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java
@@ -314,6 +314,11 @@ public abstract class TinkerWorld implements World {
case
"g_V_repeatXboth_repeatXorder_byXnameXX_timesX1XX_timesX1X":
case
"g_V_order_byXname_descX_repeatXboth_simplePath_order_byXname_descXX_timesX2X_path":
throw new AssumptionViolatedException("GraphComputer
doesn't order within repeat");
+ // TINKERPOP-3209
+ case "g_V_localXgroupCountXaX_selectXaX_countXlocalXX":
+ case "g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX":
+ case
"g_V_out_order_byXnameX_localXtreeXaX_selectXaX_countXlocalXX":
+ throw new AssumptionViolatedException("Selecting side
effects inside local() produces inconsistent results in OLAP (See
TINKERPOP-3209)");
default:
// Do nothing
}