You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2025/06/13 11:57:22 UTC
(tinkerpop) branch GValueManager3.8-wip updated (dc692e9fe5 -> 35c38e8e91)
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a change to branch GValueManager3.8-wip
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
discard dc692e9fe5 wip - tests
discard 322dfb04e5 wip - tests
discard f2961640d4 wip - tests
discard 1d344550b9 wip - more tests
discard cb5133e247 wip-tests
discard 88981221c8 wip-f
new 35c38e8e91 wip strategy tests
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (dc692e9fe5)
\
N -- N -- N refs/heads/GValueManager3.8-wip (35c38e8e91)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../traversal/step/map/AddEdgeStartStep.java | 10 +-
.../process/traversal/step/map/AddEdgeStep.java | 10 +-
.../traversal/step/map/AddVertexStartStep.java | 10 +-
.../process/traversal/step/map/AddVertexStep.java | 10 +-
.../traversal/step/sideEffect/AddPropertyStep.java | 5 +
.../step/stepContract/AddElementContract.java | 1 +
.../step/stepContract/DefaultAddEdgeContract.java | 16 ++-
.../stepContract/DefaultAddPropertyContract.java | 8 +-
.../stepContract/DefaultAddVertexContract.java | 6 +
.../strategy/decoration/PartitionStrategy.java | 32 +++--
.../strategy/decoration/PartitionStrategyTest.java | 132 ++++++++++++++++++---
11 files changed, 203 insertions(+), 37 deletions(-)
(tinkerpop) 01/01: wip strategy tests
Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch GValueManager3.8-wip
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 35c38e8e91ebf4bb3235a425a0441864b3f5f21f
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Fri Jun 6 09:06:33 2025 -0400
wip strategy tests
---
.../gremlin/process/traversal/GValueManager.java | 64 +++-
.../tinkerpop/gremlin/process/traversal/P.java | 2 +-
.../gremlin/process/traversal/step/GValue.java | 5 +
.../process/traversal/step/TraversalParent.java | 6 +-
.../traversal/step/map/AddEdgeStartStep.java | 10 +-
.../process/traversal/step/map/AddEdgeStep.java | 10 +-
.../traversal/step/map/AddVertexStartStep.java | 10 +-
.../process/traversal/step/map/AddVertexStep.java | 10 +-
.../traversal/step/sideEffect/AddPropertyStep.java | 5 +
.../step/stepContract/AddElementContract.java | 1 +
.../step/stepContract/DefaultAddEdgeContract.java | 16 +-
.../stepContract/DefaultAddPropertyContract.java | 8 +-
.../stepContract/DefaultAddVertexContract.java | 6 +
.../strategy/decoration/ConnectiveStrategy.java | 5 +-
.../strategy/decoration/PartitionStrategy.java | 32 +-
.../optimization/InlineFilterStrategy.java | 69 +++-
.../strategy/optimization/OrderLimitStrategy.java | 7 +-
.../optimization/PathProcessorStrategy.java | 40 +-
.../optimization/RepeatUnrollStrategy.java | 26 +-
.../process/traversal/util/TraversalHelper.java | 2 +-
.../traversal/strategy/GValueManagerVerifier.java | 114 +++---
.../decoration/ConnectiveStrategyTest.java | 4 +-
.../strategy/decoration/PartitionStrategyTest.java | 132 ++++++-
.../AdjacentToIncidentStrategyTest.java | 29 +-
.../ByModulatorOptimizationStrategyTest.java | 14 +-
.../strategy/optimization/CountStrategyTest.java | 207 +++++-----
.../optimization/EarlyLimitStrategyTest.java | 150 +++-----
.../optimization/FilterRankingStrategyTest.java | 415 ++++++++++++---------
.../optimization/IdentityRemovalStrategyTest.java | 15 +-
.../IncidentToAdjacentStrategyTest.java | 19 +-
.../optimization/InlineFilterStrategyTest.java | 260 ++++++++-----
.../optimization/OrderLimitStrategyTest.java | 98 +++--
.../optimization/PathProcessorStrategyTest.java | 205 +++++++---
.../optimization/RepeatUnrollStrategyTest.java | 201 ++++++----
34 files changed, 1400 insertions(+), 797 deletions(-)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
index 4757f926e3..3b08966c03 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
@@ -32,6 +32,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.Predicat
import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.RangeContract;
import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.StepContract;
import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.TailContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import java.io.Serializable;
@@ -120,7 +121,19 @@ public final class GValueManager implements Serializable, Cloneable {
throw Traversal.Exceptions.traversalIsLocked();
}
if (stepRegistry.containsKey(sourceStep)) {
- stepRegistry.put(targetStep, stepRegistry.get(sourceStep));
+ // todo: oh boy - should has stuff even be handled this way?
+ // todo: is it ok that the StepContract can be tied to two different steps? like, can happen with Traversal.clone() where you gotta copy
+
+ // gotta merge HasContainerHolder since it already exists
+ if (targetStep instanceof HasContainerHolder && stepRegistry.containsKey(targetStep) &&
+ sourceStep instanceof HasContainerHolder) {
+ final HasContainerHolder targetHasContainer = (HasContainerHolder) stepRegistry.get(targetStep);
+ final HasContainerHolder sourceHasContainer = (HasContainerHolder) stepRegistry.get(sourceStep);
+ sourceHasContainer.getHasContainers().forEach(targetHasContainer::addHasContainer);
+ stepRegistry.put(targetStep, targetHasContainer);
+ } else {
+ stepRegistry.put(targetStep, stepRegistry.get(sourceStep));
+ }
}
}
@@ -133,6 +146,11 @@ public final class GValueManager implements Serializable, Cloneable {
return (T) stepRegistry.get(step);
}
+ public Collection<GValue<?>> getGValues(final Step step) {
+ if (!isParameterized(step)) return Collections.emptyList();
+ return extractGValues(getStepContract(step));
+ }
+
/**
* Determines whether the given step is parameterized by checking its presence
* in the step registry or the predicate registry.
@@ -145,6 +163,10 @@ public final class GValueManager implements Serializable, Cloneable {
return this.stepRegistry.containsKey(step);
}
+ public Set<Step> getSteps() {
+ return Collections.unmodifiableSet(stepRegistry.keySet());
+ }
+
/**
* Gets the set of variable names used in this traversal. Note that this set won't be consistent until
* {@link #lock()} is called first. Calls to this method prior to locking will force iteration through traversal
@@ -170,7 +192,7 @@ public final class GValueManager implements Serializable, Cloneable {
final Set<GValue> gvalues = new HashSet<>();
for (StepContract contract : stepRegistry.values()) {
extractGValues(contract).stream().
- filter(gv -> gv.getName() != null).forEach(gvalues::add);
+ filter(GValue::isVariable).forEach(gvalues::add);
}
return Collections.unmodifiableSet(gvalues);
}
@@ -212,10 +234,18 @@ public final class GValueManager implements Serializable, Cloneable {
}
/**
- * Determines whether the GValueManager contains any registered GValues, steps, or predicates.
+ * Determines whether the manager has step registrations.
*/
- public boolean isEmpty() {
- return gValueRegistry.isEmpty() && stepRegistry.isEmpty();
+ public boolean hasStepRegistrations() {
+ return stepRegistry.isEmpty();
+ }
+
+ /**
+ * Determines whether the manager has step registrations. This call is not consistent until {@link #lock()} is
+ * called.
+ */
+ public boolean hasVariables() {
+ return variableNames().isEmpty();
}
/**
@@ -272,10 +302,6 @@ public final class GValueManager implements Serializable, Cloneable {
}
}
- private Collection<GValue<?>> extractPredicateGValues(final P predicate) {
- return predicate.getGValueRegistry().getGValues();
- }
-
private Collection<GValue<?>> extractGValues(final StepContract contract) {
if (contract instanceof RangeContract) {
return extractGValue((RangeContract<GValue<Long>>) contract);
@@ -309,7 +335,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final RangeContract<GValue<Long>> contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
if (contract.getHighRange().isVariable()) {
results.add(contract.getHighRange());
}
@@ -335,7 +361,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final MergeElementContract<GValue<Map<?,?>>> contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
if (contract.getMergeMap() != null) {
results.add(contract.getMergeMap());
}
@@ -353,7 +379,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final ElementIdsContract<GValue<?>> contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
for (GValue gValue: contract.getIds()) {
if (gValue.isVariable()) {
results.add(gValue);
@@ -367,7 +393,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final AddVertexContract<GValue<String>, ?, GValue<?>> contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
if (contract.getLabel() != null) {
results.add(contract.getLabel());
}
@@ -382,7 +408,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final AddEdgeContract<GValue<String>, GValue<Vertex>, ?, GValue<?>> contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
if (contract.getLabel() != null) {
results.add(contract.getLabel());
}
@@ -405,7 +431,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final AddPropertyContract<?, GValue<?>> contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
results.add(contract.getValue());
for (GValue<?> value : contract.getProperties().values()) {
results.add(value);
@@ -418,7 +444,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final EdgeLabelContract<GValue<String>> contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
for (GValue gValue: contract.getEdgeLabels()) {
if (gValue.getName() != null) {
results.add(gValue);
@@ -432,7 +458,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final CallContract<GValue<Map<?,?>>> contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
if (contract.getStaticParams().getName() != null) {
results.add(contract.getStaticParams());
}
@@ -455,7 +481,7 @@ public final class GValueManager implements Serializable, Cloneable {
* manager.
*/
private Collection<GValue<?>> extractGValue(final HasContainerHolder contract) {
- Collection<GValue<?>> results = new ArrayList();
+ final Collection<GValue<?>> results = new ArrayList();
for (P<?> predicate : contract.getPredicates()) {
if (predicate != null) {
results.addAll(predicate.getGValueRegistry().getGValues());
@@ -466,7 +492,7 @@ public final class GValueManager implements Serializable, Cloneable {
private void registerGValues(final Collection<GValue<?>> gValues) {
for (GValue<?> gValue : gValues) {
- if (gValue.getName() != null) {
+ if (gValue.isVariable()) {
gValueRegistry.put(gValue.getName(), gValue);
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
index 6dee62840d..e6fa1d65d9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
@@ -306,7 +306,7 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable {
return gValueRegistry;
}
- protected class GValueRegistry<T> implements Serializable {
+ public class GValueRegistry<T> implements Serializable {
private Map<P, GValue<T>> gValueRegistry = new IdentityHashMap<>();
public GValueRegistry() {}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
index dbc78a6cc0..955184dcec 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
@@ -116,6 +116,11 @@ public class GValue<V> implements Serializable {
return Objects.hash(name, type, value);
}
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return new GValue<>(this.name, this.type, this.value);
+ }
+
/**
* Create a new {@code Var} from a particular value but without the specified name. If the argument provide is
* already a {@code GValue} then it is returned as-is.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
index 5473d3d3bd..e1cfe2b18b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
@@ -83,7 +84,10 @@ public interface TraversalParent extends AutoCloseable {
childTraversal.setParent(this);
childTraversal.getSideEffects().mergeInto(this.asStep().getTraversal().getSideEffects());
childTraversal.setSideEffects(this.asStep().getTraversal().getSideEffects());
- childTraversal.getGValueManager().mergeInto(this.asStep().getTraversal().getGValueManager());
+ final GValueManager childManager = childTraversal.getGValueManager();
+ final GValueManager parentManager = this.asStep().getTraversal().getGValueManager();
+ childManager.mergeInto(parentManager);
+ //childTraversal.getGValueManager().mergeInto(this.asStep().getTraversal().getGValueManager());
TraversalHelper.applyTraversalRecursively(t -> t.setGValueManager(this.asStep().getTraversal().getGValueManager()), childTraversal);
return (Traversal.Admin<S, E>) childTraversal;
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
index 7e83679519..f36126f5a9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
@@ -45,6 +45,7 @@ import org.apache.tinkerpop.gremlin.structure.util.Attachable;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -202,11 +203,16 @@ public class AddEdgeStartStep extends AbstractStep<Edge, Edge>
@Override
public Map<Object, Object> getProperties() {
- throw new UnsupportedOperationException("TODO:");
+ return Collections.unmodifiableMap(parameters.getRaw());
}
@Override
- public void addProperty(Object key, Object value) {
+ public void addProperty(final Object key, final Object value) {
configure(key, value);
}
+
+ @Override
+ public Object removeProperty(final Object key) {
+ return this.parameters.remove(key);
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
index e133b74303..87168a805b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
@@ -41,6 +41,7 @@ import org.apache.tinkerpop.gremlin.structure.util.Attachable;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -206,11 +207,16 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
@Override
public Map<Object, Object> getProperties() {
- throw new UnsupportedOperationException("TODO:");
+ return Collections.unmodifiableMap(parameters.getRaw());
}
@Override
- public void addProperty(Object key, Object value) {
+ public void addProperty(final Object key, final Object value) {
configure(key, value);
}
+
+ @Override
+ public Object removeProperty(Object key) {
+ return parameters.remove(key);
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
index 2b579f9474..dab1d71581 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
@@ -40,6 +40,7 @@ import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -175,11 +176,16 @@ public class AddVertexStartStep extends AbstractStep<Vertex, Vertex>
@Override
public Map<Object, Object> getProperties() {
- throw new UnsupportedOperationException("TODO:");
+ return Collections.unmodifiableMap(parameters.getRaw());
}
@Override
- public void addProperty(Object key, Object value) {
+ public void addProperty(final Object key, final Object value) {
configure(key, value);
}
+
+ @Override
+ public Object removeProperty(final Object key) {
+ return parameters.remove(key);
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index 07873344e0..c936980350 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -36,6 +36,7 @@ import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -161,11 +162,16 @@ public class AddVertexStep<S> extends ScalarMapStep<S, Vertex>
@Override
public Map<Object, Object> getProperties() {
- throw new UnsupportedOperationException("TODO:");
+ return Collections.unmodifiableMap(parameters.getRaw());
}
@Override
- public void addProperty(Object key, Object value) {
+ public void addProperty(final Object key, final Object value) {
configure(key, value);
}
+
+ @Override
+ public Object removeProperty(final Object key) {
+ return parameters.remove(key);
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
index badd84003c..83af105e46 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
@@ -244,4 +244,9 @@ public class AddPropertyStep<S extends Element> extends SideEffectStep<S>
public Object getValue() {
throw new UnsupportedOperationException("TODO:");
}
+
+ @Override
+ public Object removeProperty(Object key) {
+ return parameters.remove(key);
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/AddElementContract.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/AddElementContract.java
index 05a7791ac6..59fc09f434 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/AddElementContract.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/AddElementContract.java
@@ -23,4 +23,5 @@ import java.util.Map;
public interface AddElementContract<K, V> extends StepContract {
public Map<K, V> getProperties();
public void addProperty(K key, V value);
+ public V removeProperty(K key);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddEdgeContract.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddEdgeContract.java
index a70a8d44c9..4374b8a5fa 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddEdgeContract.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddEdgeContract.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.stepContract;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -27,7 +28,7 @@ public class DefaultAddEdgeContract<L, Vertex, K, Value> implements AddEdgeContr
private Vertex to;
private Map<K, Value> properties = new HashMap<>();
- public DefaultAddEdgeContract(L label) {
+ public DefaultAddEdgeContract(final L label) {
this.label = label;
}
@@ -46,20 +47,25 @@ public class DefaultAddEdgeContract<L, Vertex, K, Value> implements AddEdgeContr
return null;
}
- public void addFrom(Vertex from) {
+ public void addFrom(final Vertex from) {
this.from = from;
}
- public void addTo(Vertex to) {
+ public void addTo(final Vertex to) {
this.to = to;
}
@Override
public Map<K, Value> getProperties() {
- return properties;
+ return Collections.unmodifiableMap(properties);
}
- public void addProperty(K key, Value value){
+ public void addProperty(final K key, final Value value){
properties.put(key, value);
}
+
+ @Override
+ public Value removeProperty(final K key) {
+ return properties.remove(key);
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddPropertyContract.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddPropertyContract.java
index 92d041414d..75075df3fd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddPropertyContract.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddPropertyContract.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.stepContract;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -39,9 +40,14 @@ public class DefaultAddPropertyContract<K, V> implements AddPropertyContract<K,
metaProperties.put(key, value);
}
+ @Override
+ public V removeProperty(K key) {
+ return metaProperties.remove(key);
+ }
+
@Override
public Map<K, V> getProperties() {
- throw new UnsupportedOperationException();
+ return Collections.unmodifiableMap(metaProperties);
}
@Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddVertexContract.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddVertexContract.java
index 9275417109..7ce0187139 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddVertexContract.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/stepContract/DefaultAddVertexContract.java
@@ -29,6 +29,7 @@ public class DefaultAddVertexContract<L, K, V> implements AddVertexContract<L, K
this.label = label;
}
+ @Override
public void addProperty(K key, V value){
properties.put(key, value);
}
@@ -42,4 +43,9 @@ public class DefaultAddVertexContract<L, K, V> implements AddVertexContract<L, K
public Map<K, V> getProperties() {
return properties;
}
+
+ @Override
+ public V removeProperty(final K key) {
+ return properties.remove(key);
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategy.java
index 47fe6670b4..0f26198b87 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategy.java
@@ -77,7 +77,6 @@ public final class ConnectiveStrategy extends AbstractTraversalStrategy<Traversa
}
private static void processConjunctionMarker(final Class<? extends ConnectiveStep> markerClass, final Traversal.Admin<?, ?> traversal) {
- final GValueManager manager = traversal.getGValueManager();
final List<Step> steps = traversal.getSteps();
for (int i = 0; i < steps.size(); i++) {
final Step step = steps.get(i);
@@ -89,7 +88,7 @@ public final class ConnectiveStrategy extends AbstractTraversalStrategy<Traversa
for (int j = i - 1; j >= 0; i--, j--) {
final Step previousStep = steps.get(j);
if (legalCurrentStep(previousStep)) {
- // step is moving from one traversal to another. GValue state is unchanged.
+ // step is moving from one traversal to another. GValue state is unchanged so retain it
connectiveTraversal.addStep(0, previousStep);
traversal.removeStep(previousStep);
} else break;
@@ -106,7 +105,7 @@ public final class ConnectiveStrategy extends AbstractTraversalStrategy<Traversa
} else {
connectiveTraversal.addStep(nextStep);
}
- // step is moving from one traversal to another. GValue state is unchanged.
+ // step is moving from one traversal to another. GValue state is unchanged so retain it
traversal.removeStep(nextStep);
} else break;
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
index 72f2eee9a6..8ecb646f00 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
import org.apache.commons.configuration2.Configuration;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.AbstractLambdaTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -44,6 +45,9 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertyMapStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.AddPropertyContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.AddVertexContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.DefaultAddPropertyContract;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
@@ -194,7 +198,7 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal
traversal.removeStep(step);
} else {
throw new IllegalStateException(String.format("%s is not accounting for a particular %s %s",
- PartitionStrategy.class.getSimpleName(), PropertyType.class.toString(), step.getReturnType()));
+ PartitionStrategy.class.getSimpleName(), PropertyType.class, step.getReturnType()));
}
});
@@ -221,7 +225,7 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal
TraversalHelper.insertAfterStep(new LambdaMapStep<>(traversal, new MapPropertiesConverter()), mapPropertiesFilterStep, traversal);
} else {
throw new IllegalStateException(String.format("%s is not accounting for a particular %s %s",
- PartitionStrategy.class.getSimpleName(), PropertyType.class.toString(), step.getReturnType()));
+ PartitionStrategy.class.getSimpleName(), PropertyType.class, step.getReturnType()));
}
});
}
@@ -246,6 +250,8 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal
if (step instanceof AddVertexStartStep || step instanceof AddVertexStep) {
final Parameters parameters = ((Parameterizing) step).getParameters();
final Map<Object, List<Object>> params = parameters.getRaw();
+ final AddVertexContract contract = traversal.getGValueManager().getStepContract(step);
+
params.forEach((k, v) -> {
// need to filter out T based keys
@@ -257,13 +263,25 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal
addPropertyStep.configure(partitionKey, writePartition);
addPropertyStepsToAppend.add(addPropertyStep);
- // need to remove the parameter from the AddVertex/StartStep because it's now being added
- // via the AddPropertyStep
+ // need to remove the parameter from the AddVertex/StartStep and move any GValue to a
+ // different contract related because it's now being added via the AddPropertyStep
parameters.remove(k);
- });
- Collections.reverse(addPropertyStepsToAppend);
- addPropertyStepsToAppend.forEach(s -> TraversalHelper.insertAfterStep(s, step, traversal));
+ if (contract != null) {
+ final Object possibleGValue = contract.removeProperty(k);
+ if (possibleGValue instanceof GValue) {
+ final AddPropertyContract addPropertyContract = new DefaultAddPropertyContract(k, possibleGValue);
+ traversal.getGValueManager().register(addPropertyStep, addPropertyContract);
+ }
+
+ // remove an empty contract for the addV because we've moved all the state to
+ // the property() steps
+ if (contract.getProperties().isEmpty())
+ traversal.getGValueManager().remove(step);
+ }
+
+ TraversalHelper.insertAfterStep(addPropertyStep, step, traversal);
+ });
}
});
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
index d2713218c5..c9d0edaccf 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
@@ -22,10 +22,12 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.Compare;
import org.apache.tinkerpop.gremlin.process.traversal.Contains;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.LambdaHolder;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.AndStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep;
@@ -37,6 +39,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalSte
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TraversalFilterStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.DefaultEdgeLabelContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.StepContract;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
@@ -92,15 +96,15 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
final Iterator<FilterStep> filterStepIterator = TraversalHelper.getStepsOfAssignableClass(FilterStep.class, traversal).iterator();
while (!changed && filterStepIterator.hasNext()) {
final FilterStep<?> step = filterStepIterator.next();
- changed = step instanceof HasStep && InlineFilterStrategy.processHasStep((HasStep) step, traversal) ||
- step instanceof TraversalFilterStep && InlineFilterStrategy.processTraversalFilterStep((TraversalFilterStep) step, traversal) ||
- step instanceof OrStep && InlineFilterStrategy.processOrStep((OrStep) step, traversal) ||
- step instanceof AndStep && InlineFilterStrategy.processAndStep((AndStep) step, traversal);
+ changed = step instanceof HasStep && processHasStep((HasStep) step, traversal) ||
+ step instanceof TraversalFilterStep && processTraversalFilterStep((TraversalFilterStep) step, traversal) ||
+ step instanceof OrStep && processOrStep((OrStep) step, traversal) ||
+ step instanceof AndStep && processAndStep((AndStep) step, traversal);
}
if (!changed && traversal.isRoot()) {
final Iterator<MatchStep> matchStepIterator = TraversalHelper.getStepsOfClass(MatchStep.class, traversal).iterator();
while (!changed && matchStepIterator.hasNext()) {
- if (InlineFilterStrategy.processMatchStep(matchStepIterator.next(), traversal))
+ if (processMatchStep(matchStepIterator.next(), traversal))
changed = true;
}
}
@@ -111,20 +115,26 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
///////////////////////////
private static boolean processHasStep(final HasStep<?> step, final Traversal.Admin<?, ?> traversal) {
+ final GValueManager manager = traversal.getGValueManager();
if (step.getPreviousStep() instanceof HasStep) {
final HasStep<?> previousStep = (HasStep<?>) step.getPreviousStep();
final List<HasContainer> hasContainers = new ArrayList<>(step.getHasContainers());
for (final HasContainer hasContainer : hasContainers) {
previousStep.addHasContainer(hasContainer);
}
+
+ // merged has() steps so GValue state from later has() steps must be merged into the previous has()
+ manager.copyRegistryState((Step) step, previousStep);
+
TraversalHelper.copyLabels(step, previousStep, false);
- traversal.removeStep(step);
+ TraversalHelper.removeStep(step, traversal);
return true;
} else if (step.getPreviousStep() instanceof VertexStep
&& ((VertexStep) step.getPreviousStep()).returnsEdge()
&& 0 == ((VertexStep) step.getPreviousStep()).getEdgeLabels().length) {
final VertexStep<Edge> previousStep = (VertexStep<Edge>) step.getPreviousStep();
final List<String> edgeLabels = new ArrayList<>();
+ final List<GValue> edgeLabelGValues = new ArrayList<>();
for (final HasContainer hasContainer : new ArrayList<>(step.getHasContainers())) {
if (hasContainer.getKey().equals(T.label.getAccessor())) {
if (hasContainer.getBiPredicate() == Compare.eq &&
@@ -132,19 +142,28 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
edgeLabels.isEmpty()) {
edgeLabels.add((String) hasContainer.getValue());
step.removeHasContainer(hasContainer);
+ if (hasContainer.getPredicate().isParameterized()) {
+ edgeLabelGValues.addAll(hasContainer.getPredicate().getGValueRegistry().getGValues());
+ }
} else if (hasContainer.getBiPredicate() == Contains.within &&
hasContainer.getValue() instanceof Collection &&
((Collection) hasContainer.getValue()).containsAll(edgeLabels)) {
edgeLabels.addAll((Collection<String>) hasContainer.getValue());
step.removeHasContainer(hasContainer);
+ if (hasContainer.getPredicate().isParameterized()) {
+ edgeLabelGValues.addAll(hasContainer.getPredicate().getGValueRegistry().getGValues());
+ }
} else if (hasContainer.getPredicate() instanceof OrP && edgeLabels.isEmpty()) {
boolean removeContainer = true;
final List<P<?>> orps = ((OrP) hasContainer.getPredicate()).getPredicates();
final List<String> newEdges = new ArrayList<>();
for (int i = 0; i < orps.size(); i++) {
- if (orps.get(i).getBiPredicate() == Compare.eq && orps.get(i).getValue() instanceof String)
+ if (orps.get(i).getBiPredicate() == Compare.eq && orps.get(i).getValue() instanceof String) {
newEdges.add((String) orps.get(i).getValue());
- else {
+ if (hasContainer.getPredicate().isParameterized()) {
+ edgeLabelGValues.addAll(hasContainer.getPredicate().getGValueRegistry().getGValues());
+ }
+ } else {
removeContainer = false;
break;
}
@@ -162,7 +181,13 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
TraversalHelper.copyLabels(previousStep, newVertexStep, false);
if (step.getHasContainers().isEmpty()) {
TraversalHelper.copyLabels(step, newVertexStep, false);
- traversal.removeStep(step);
+ TraversalHelper.removeStep(step, traversal);
+ }
+
+ if (!edgeLabelGValues.isEmpty()) {
+ final GValue<String>[] edgeLabelGValuesArray = edgeLabelGValues.stream().toArray(GValue[]::new);
+ final StepContract contract = new DefaultEdgeLabelContract<>(edgeLabelGValuesArray);
+ manager.register(newVertexStep, contract);
}
return true;
}
@@ -171,7 +196,7 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
return false;
}
- private static final boolean processTraversalFilterStep(final TraversalFilterStep<?> step, final Traversal.Admin<?, ?> traversal) {
+ private static boolean processTraversalFilterStep(final TraversalFilterStep<?> step, final Traversal.Admin<?, ?> traversal) {
final Traversal.Admin<?, ?> childTraversal = step.getLocalChildren().get(0);
if (TraversalHelper.hasAllStepsOfClass(childTraversal, FilterStep.class) &&
!TraversalHelper.hasStepOfClass(childTraversal,
@@ -182,19 +207,26 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
final Step<?, ?> finalStep = childTraversal.getEndStep();
TraversalHelper.insertTraversal((Step) step, childTraversal, traversal);
TraversalHelper.copyLabels(step, finalStep, false);
- traversal.removeStep(step);
+ TraversalHelper.removeStep(step, traversal);
return true;
}
return false;
}
- private static final boolean processOrStep(final OrStep<?> step, final Traversal.Admin<?, ?> traversal) {
+ /**
+ * Processes an {@link OrStep} within a traversal and attempts to optimize it by replacing it with
+ * simpler constructs if possible. The method evaluates the local child traversals of the provided
+ * {@code OrStep} and determines if the {@code OrStep} can be simplified into a single step such as
+ * a {@link HasStep}.
+ */
+ private static boolean processOrStep(final OrStep<?> step, final Traversal.Admin<?, ?> traversal) {
boolean process = true;
String key = null;
P predicate = null;
final List<String> labels = new ArrayList<>();
+ final List<Step> stepsToRemoveFromManager = new ArrayList<>();
for (final Traversal.Admin<?, ?> childTraversal : step.getLocalChildren()) {
- InlineFilterStrategy.instance().apply(childTraversal); // todo: this may be a bad idea, but I can't seem to find a test case to break it
+ InlineFilterStrategy.instance().apply(childTraversal);
for (final Step<?, ?> childStep : childTraversal.getSteps()) {
if (childStep instanceof HasStep) {
P p = null;
@@ -213,6 +245,7 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
predicate = null == predicate ? p : predicate.or(p);
}
labels.addAll(childStep.getLabels());
+ stepsToRemoveFromManager.add(childStep);
} else {
process = false;
break;
@@ -224,6 +257,8 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
if (process) {
final HasStep hasStep = new HasStep<>(traversal, new HasContainer(key, predicate));
TraversalHelper.replaceStep(step, hasStep, traversal);
+ stepsToRemoveFromManager.forEach(s -> traversal.getGValueManager().remove(s));
+ traversal.getGValueManager().register(hasStep, hasStep);
TraversalHelper.copyLabels(step, hasStep, false);
for (final String label : labels) {
hasStep.addLabel(label);
@@ -233,7 +268,7 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
return false;
}
- private static final boolean processAndStep(final AndStep<?> step, final Traversal.Admin<?, ?> traversal) {
+ private static boolean processAndStep(final AndStep<?> step, final Traversal.Admin<?, ?> traversal) {
boolean process = true;
for (final Traversal.Admin<?, ?> childTraversal : step.getLocalChildren()) {
if (!TraversalHelper.hasAllStepsOfClass(childTraversal, FilterStep.class) ||
@@ -257,13 +292,13 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
}
if (null != finalStep) TraversalHelper.copyLabels(step, finalStep, false);
- traversal.removeStep(step);
+ TraversalHelper.removeStep(step, traversal);
return true;
}
return false;
}
- private static final boolean processMatchStep(final MatchStep<?, ?> step, final Traversal.Admin<?, ?> traversal) {
+ private static boolean processMatchStep(final MatchStep<?, ?> step, final Traversal.Admin<?, ?> traversal) {
if (step.getPreviousStep() instanceof EmptyStep)
return false;
boolean changed = false;
@@ -286,7 +321,7 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
}
}
if (step.getGlobalChildren().isEmpty())
- traversal.removeStep(step);
+ TraversalHelper.removeStep(step, traversal);
return changed;
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/OrderLimitStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/OrderLimitStrategy.java
index ee0c1a3945..31a64ac8d0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/OrderLimitStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/OrderLimitStrategy.java
@@ -95,8 +95,13 @@ public final class OrderLimitStrategy extends AbstractTraversalStrategy<Traversa
else
currentStep = currentStep.getNextStep();
}
- if (null != range)
+
+ if (null != range) {
order.setLimit(range.getHighRange());
+
+ // remove GValueManager state because the step contract value has been used to set up order()
+ traversal.getGValueManager().remove(range);
+ }
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategy.java
index 70101b2f70..766a087df1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategy.java
@@ -20,6 +20,7 @@
package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.VertexProgramStep;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -98,6 +99,8 @@ public final class PathProcessorStrategy extends AbstractTraversalStrategy<Trave
return;
}
+ final GValueManager manager = traversal.getGValueManager();
+
// process where(as("a").out()...) => select("a").where(out()...)
final List<WhereTraversalStep> whereTraversalSteps = TraversalHelper.getStepsOfClass(WhereTraversalStep.class, traversal);
for (final WhereTraversalStep<?> whereTraversalStep : whereTraversalSteps) {
@@ -110,8 +113,7 @@ public final class PathProcessorStrategy extends AbstractTraversalStrategy<Trave
final int index = TraversalHelper.stepIndex(whereTraversalStep, traversal);
if (whereTraversalStep.getPreviousStep() instanceof SelectStep) {
done = false;
- traversal.removeStep(index);
- traversal.addStep(index - 1, whereTraversalStep);
+ TraversalHelper.moveStep(whereTraversalStep, index - 1, traversal);
}
}
final WhereTraversalStep.WhereStartStep<?> whereStartStep = (WhereTraversalStep.WhereStartStep<?>) localChild.getStartStep();
@@ -128,9 +130,9 @@ public final class PathProcessorStrategy extends AbstractTraversalStrategy<Trave
whereStartStep.removeScopeKey();
// process where(as("a").out()) => as('xyz').select("a").filter(out()).select('xyz')
if (!(localChild.getEndStep() instanceof WhereTraversalStep.WhereEndStep)) {
- localChild.removeStep(localChild.getStartStep());
+ TraversalHelper.removeStep(localChild.getStartStep(), localChild);
traversal.addStep(index + 1, new TraversalFilterStep<>(traversal, localChild));
- traversal.removeStep(whereTraversalStep);
+ TraversalHelper.removeStep(whereTraversalStep, traversal.asAdmin());
}
}
}
@@ -154,15 +156,25 @@ public final class PathProcessorStrategy extends AbstractTraversalStrategy<Trave
final String[] keys = new String[byTraversals.size()];
int counter = 0;
for (final Map.Entry<String, Traversal.Admin<Object, Object>> entry : byTraversals.entrySet()) {
+ final Traversal.Admin<Object, Object> byTraversal = entry.getValue();
+ final Traversal.Admin<Object, Object> clonedByTraversal = byTraversal.clone();
final SelectOneStep selectOneStep = new SelectOneStep(traversal, selectStep.getPop(), entry.getKey());
- final TraversalMapStep<?, ?> mapStep = new TraversalMapStep<>(traversal, entry.getValue().clone());
+ final TraversalMapStep<?, ?> mapStep = new TraversalMapStep<>(traversal, clonedByTraversal);
mapStep.addLabel(entry.getKey());
traversal.addStep(index + 1, mapStep);
traversal.addStep(index + 1, selectOneStep);
keys[counter++] = entry.getKey();
+
+ final List<Step> byChildSteps = TraversalHelper.getStepsOfAssignableClassRecursively(Step.class, byTraversal);
+ for (int ix = 0; ix < byChildSteps.size(); ix++) {
+ manager.copyRegistryState(byChildSteps.get(ix), clonedByTraversal.getSteps().get(ix));
+ manager.remove(byChildSteps.get(ix));
+ clonedByTraversal.getGValueManager().remove(byChildSteps.get(ix));
+ }
+
}
traversal.addStep(index + 1 + (byTraversals.size() * 2), new SelectStep(traversal, Pop.last, keys));
- traversal.removeStep(index);
+ TraversalHelper.removeStep(selectStep, traversal);
}
}
@@ -174,13 +186,25 @@ public final class PathProcessorStrategy extends AbstractTraversalStrategy<Trave
if (!traversal.getStrategies().getStrategy(ProductiveByStrategy.class).isPresent()) {
final List<SelectOneStep> selectOneSteps = TraversalHelper.getStepsOfClass(SelectOneStep.class, traversal);
for (final SelectOneStep<?, ?> selectOneStep : selectOneSteps) {
- if (selectOneStep.getPop() != Pop.all && selectOneStep.getPop() != Pop.mixed && // TODO: necessary?
+ if (selectOneStep.getPop() != Pop.all && selectOneStep.getPop() != Pop.mixed &&
selectOneStep.getMaxRequirement().compareTo(PathProcessor.ElementRequirement.ID) > 0 &&
labelCount(selectOneStep.getScopeKeys().iterator().next(), TraversalHelper.getRootTraversal(traversal)) <= 1) {
final int index = TraversalHelper.stepIndex(selectOneStep, traversal);
final Traversal.Admin<?, ?> localChild = selectOneStep.getLocalChildren().get(0);
+
+ final Traversal.Admin<?, ?> localChildClone = localChild.clone();
+ final List<Step> localChildSteps = TraversalHelper.getStepsOfAssignableClassRecursively(Step.class, localChild);
+ for (int ix = 0; ix < localChildSteps.size(); ix++) {
+ manager.copyRegistryState(localChildSteps.get(ix), localChildClone.getSteps().get(ix));
+
+ // todo: look at cases where we clone() traversal more carefully. so much to think about in these cases
+ manager.remove(localChildSteps.get(ix));
+ localChildClone.getGValueManager().remove(localChildSteps.get(ix));
+ }
+
selectOneStep.removeLocalChild(localChild);
- final TraversalMapStep<?, ?> mapStep = new TraversalMapStep<>(traversal, localChild.clone());
+
+ final TraversalMapStep<?, ?> mapStep = new TraversalMapStep<>(traversal, localChildClone);
traversal.addStep(index + 1, mapStep);
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/RepeatUnrollStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/RepeatUnrollStrategy.java
index 18481cd2c6..0a4d0c285c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/RepeatUnrollStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/RepeatUnrollStrategy.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -35,6 +36,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -67,6 +69,8 @@ public final class RepeatUnrollStrategy extends AbstractTraversalStrategy<Traver
if (TraversalHelper.onGraphComputer(traversal))
return;
+ final GValueManager manager = traversal.getGValueManager();
+
final boolean lazyBarrierStrategyInstalled = TraversalHelper.getRootTraversal(traversal).
getStrategies().getStrategy(LazyBarrierStrategy.class).isPresent();
@@ -79,13 +83,27 @@ public final class RepeatUnrollStrategy extends AbstractTraversalStrategy<Traver
!TraversalHelper.hasStepOfAssignableClassRecursively(INVALIDATING_STEPS, repeatStep.getRepeatTraversal())) {
final Traversal.Admin<?, ?> repeatTraversal = repeatStep.getGlobalChildren().get(0);
- repeatTraversal.removeStep(repeatTraversal.getSteps().size() - 1); // removes the RepeatEndStep
+
+ // removes the RepeatEndStep - no need to worry about GValue state as that step can't carry any
+ repeatTraversal.removeStep(repeatTraversal.getSteps().size() - 1);
final int repeatLength = repeatTraversal.getSteps().size();
int insertIndex = i;
final int loops = (int) ((LoopTraversal) repeatStep.getUntilTraversal()).getMaxLoops();
for (int j = 0; j < loops; j++) {
- TraversalHelper.insertTraversal(insertIndex, repeatTraversal.clone(), traversal);
+
+ // todo: bad? like, is it ok that StepContracts are shared across steps??
+ // cloned the repeat traversal so copy the StepContract to the cloned steps.
+ final Traversal.Admin<?, ?> repeatClone = repeatTraversal.clone();
+ final List<Step> repeatSteps = repeatTraversal.getSteps();
+ for (int ix = 0; ix < repeatSteps.size(); ix++) {
+ manager.copyRegistryState(repeatSteps.get(ix), repeatClone.getSteps().get(ix));
+
+ // todo: PathProcessorStrategy showed some state can hang about in the manager - really need to investigate clone more
+ repeatClone.getGValueManager().remove(repeatSteps.get(ix));
+ }
+
+ TraversalHelper.insertTraversal(insertIndex, repeatClone, traversal);
insertIndex = insertIndex + repeatLength;
// the addition of barriers is determined by the existence of LazyBarrierStrategy
@@ -105,6 +123,10 @@ public final class RepeatUnrollStrategy extends AbstractTraversalStrategy<Traver
// remove the RepeatStep
traversal.removeStep(i);
+
+ // drop the steps that have GValue state from the manager. since we're killing the entire traversal
+ // we must remove the child steps too
+ TraversalHelper.getStepsOfAssignableClassRecursively(Step.class, repeatTraversal).forEach(manager::remove);
}
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java
index aefada9d8b..6f9952ee41 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java
@@ -175,7 +175,7 @@ public final class TraversalHelper {
}
/**
- * Replace a step with a new step.
+ * Replace a step with a new step. When a step is replaced, it is also removed from the {@link GValueManager}.
*
* @param removeStep the step to remove
* @param insertStep the step to insert
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/GValueManagerVerifier.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/GValueManagerVerifier.java
index 8c23bc94f7..bbcb9b88c1 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/GValueManagerVerifier.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/GValueManagerVerifier.java
@@ -24,14 +24,15 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.EdgeLabelContract;
import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.RangeContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.StepContract;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import org.apache.tinkerpop.gremlin.util.CollectionUtil;
-import java.util.HashSet;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -40,19 +41,44 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
/**
* Provides utilities to verify the state and behavior of {@code GValueManager} during and after traversal strategy
* application. It offers a builder pattern to configure and perform verification checks for traversals.
+ * Multiple strategies can be applied in the order they are provided.
*/
public class GValueManagerVerifier {
/**
- * Creates a verification builder for the given traversal and strategy
+ * Creates a verification builder for the given traversal and strategies
*/
public static <S, E> VerificationBuilder<S, E> verify(final Traversal.Admin<S, E> traversal, final TraversalStrategy strategy) {
- return new VerificationBuilder<>(traversal, strategy);
+ return verify(traversal, strategy, Collections.emptySet());
+ }
+
+ /**
+ * Creates a verification builder for the given traversal and strategies
+ */
+ public static <S, E> VerificationBuilder<S, E> verify(final Traversal.Admin<S, E> traversal, final TraversalStrategy strategy,
+ final Collection<TraversalStrategy> additionalStrategies) {
+ // Create an array with FilterRankingStrategy as the first strategy
+ TraversalStrategy[] strategies;
+
+ if (additionalStrategies.isEmpty()) {
+ // If no additional strategies, just use the one provided
+ strategies = new TraversalStrategy[] { strategy };
+ } else {
+ // If there are additional strategies, combine them with one provided
+ strategies = new TraversalStrategy[additionalStrategies.size() + 1];
+ strategies[0] = strategy;
+
+ int i = 1;
+ for (TraversalStrategy ts : additionalStrategies) {
+ strategies[i++] = ts;
+ }
+ }
+
+ return new VerificationBuilder<>(traversal, strategies);
}
/**
@@ -60,11 +86,11 @@ public class GValueManagerVerifier {
*/
public static class VerificationBuilder<S, E> {
private final Traversal.Admin<S, E> traversal;
- private final TraversalStrategy strategy;
+ private final TraversalStrategy[] strategies;
- private VerificationBuilder(final Traversal.Admin<S, E> traversal, final TraversalStrategy strategy) {
+ private VerificationBuilder(final Traversal.Admin<S, E> traversal, final TraversalStrategy... strategies) {
this.traversal = traversal;
- this.strategy = strategy;
+ this.strategies = strategies;
}
public BeforeVerifier<S, E> beforeApplying() {
@@ -72,7 +98,9 @@ public class GValueManagerVerifier {
}
/**
- * Applies the strategy and returns the verifier
+ * Applies the strategies and returns the verifier while ensuring that the step state is consistent where
+ * every step in the manager is in the traversal and every step that is a parameterized {@link StepContract}
+ * is in the manager.
*/
public AfterVerifier<S, E> afterApplying() {
// Capture pre-strategy state
@@ -81,14 +109,35 @@ public class GValueManagerVerifier {
final Set<String> preVariables = manager.variableNames();
final Set<GValue> preGValues = manager.gValues();
- // Apply strategy
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(strategy);
- traversal.setStrategies(strategies);
+ // Apply strategies
+ final TraversalStrategies traversalStrategies = new DefaultTraversalStrategies();
+ for (TraversalStrategy strategy : strategies) {
+ traversalStrategies.addStrategies(strategy);
+ }
+ traversal.setStrategies(traversalStrategies);
traversal.applyStrategies();
+
+ verifyStateIsConsistent();
return new AfterVerifier<>(traversal, preVariables, preStepVariables, preGValues);
}
+
+ /**
+ * Verifies that the manager is not referencing any steps that aren't in the traversal after strategy
+ * application.
+ */
+ private void verifyStateIsConsistent() {
+ // Get all steps in the traversal including nested ones
+ final List<Step> allSteps = TraversalHelper.getStepsOfAssignableClassRecursively(Step.class, traversal);
+ final GValueManager manager = traversal.getGValueManager();
+
+ // Check that manager only references valid steps
+ final Set<Step> stepsInManager = manager.getSteps();
+ for (Step step : stepsInManager) {
+ assertThat("GValueManager references step not in traversal: " + step,
+ allSteps.stream().anyMatch(s -> s == step), is(true));
+ }
+ }
}
/**
@@ -158,32 +207,6 @@ public class GValueManagerVerifier {
assertEquals("All variables should be preserved", preVariables, currentVariables);
return this;
}
-
- /**
- * Verifies that all variables are removed
- */
- public AfterVerifier<S, E> variablesAreRemoved() {
- final Set<String> currentVariables = manager.variableNames();
- assertThat("All variables should be removed", currentVariables.isEmpty(), is(true));
- return this;
- }
-
- /**
- * Verifies that variables have been transferred between steps
- */
- public AfterVerifier<S, E> variablesHaveBeenTransferredFrom(final Step originalStep) {
- if (!preStepGValues.containsKey(originalStep)) {
- fail("Original step was not parameterized before strategy application");
- }
-
- final Set<String> originalVariables = preStepVariables.get(originalStep);
- final Set<String> currentVariables = traversal.getGValueManager().variableNames();
-
- assertThat("Variables should be transferred",
- currentVariables.containsAll(originalVariables), is(true));
-
- return this;
- }
}
/**
@@ -199,15 +222,18 @@ public class GValueManagerVerifier {
}
/**
- * Returns this instance cast to the implementing class type
+ * Returns this instance cast to the implementing class type.
*/
protected abstract T self();
/**
- * Verifies that GValueManager is empty
+ * Verifies that GValueManager is empty, specifically meaning that there are no variables in the manager. There
+ * might yet be unnamed {@link GValue} instances but since we don't bind to those and they are usually incident
+ * to something like {@code V(1, x=2, 3)} where one variable implies that all arguments must be converted to
+ * {@link GValue} you can have scenarios where there is a mix.
*/
public T managerIsEmpty() {
- assertThat("GValueManager should be empty", manager.isEmpty(), is(true));
+ assertThat(String.format("GValueManager should be empty but contains [%s]", manager.gValues()), manager.hasVariables(), is(true));
return self();
}
@@ -273,14 +299,14 @@ public class GValueManagerVerifier {
}
/**
- * Verifies that specific variables exist
+ * Verifies that specific variables exist.
*/
public T hasVariables(final String... variables) {
return hasVariables(CollectionUtil.asSet(variables));
}
/**
- * Verifies that specific variables exist
+ * Verifies that specific variables exist.
*/
public T hasVariables(final Set<String> variables) {
// Get variables from the current traversal and all child traversals
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategyTest.java
index 7ebe1e46c2..2e6184460e 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategyTest.java
@@ -169,9 +169,9 @@ public class ConnectiveStrategyTest {
// where steps are being moved from the top level traversal down to child traversals.
GValueManagerVerifier.verify(traversal, ConnectiveStrategy.instance()).
beforeApplying().
- hasVariables(expectedVariables).
+ hasVariables(expectedVariables).
afterApplying().
- hasVariables(expectedVariables);
+ hasVariables(expectedVariables);
}
}
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTest.java
index bacf05bc32..c06f4c7ad9 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTest.java
@@ -24,12 +24,15 @@ import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -171,22 +174,6 @@ public class PartitionStrategyTest {
}
});
}
-
- public static GraphTraversal create() {
- return create(null);
- }
-
- public static GraphTraversal create(final Class<? extends Element> clazz) {
- final Graph mockedGraph = mock(Graph.class);
- final Graph.Features features = mock(Graph.Features.class);
- final Graph.Features.VertexFeatures vertexFeatures = mock(Graph.Features.VertexFeatures.class);
- when(mockedGraph.features()).thenReturn(features);
- when(features.vertex()).thenReturn(vertexFeatures);
- when(vertexFeatures.getCardinality(any())).thenReturn(VertexProperty.Cardinality.single);
- final DefaultGraphTraversal t = new DefaultGraphTraversal<>(mockedGraph);
- if (clazz != null) t.asAdmin().addStep(new GraphStep<>(t.asAdmin(), clazz, true));
- return t;
- }
}
public static class RewriteTest {
@@ -219,4 +206,117 @@ public class PartitionStrategyTest {
assertEquals("p", strategy.getPartitionKey());
}
}
+
+ /**
+ * Tests that GValueManager is being used correctly in PartitionStrategy
+ * to ensure that GValue state is properly maintained when steps are modified.
+ */
+ @RunWith(Parameterized.class)
+ public static class GValueTest {
+
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin<?, ?> traversal;
+
+ @Parameterized.Parameter(value = 1)
+ public PartitionStrategy strategy;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ // Create a default PartitionStrategy
+ final PartitionStrategy defaultStrategy = PartitionStrategy.build()
+ .partitionKey("p").writePartition("a").readPartitions("a").create();
+
+ return Arrays.asList(new Object[][]{
+ // Basic vertex steps with GValue edge labels
+ {
+ create().V().hasLabel("person").out(GValue.of("x", "knows")).asAdmin(),
+ defaultStrategy
+ },
+ {
+ create().V().hasLabel("person").both(GValue.of("x", "created")).asAdmin(),
+ defaultStrategy
+ },
+ {
+ create().V().hasLabel("person").in(GValue.of("x", "created")).asAdmin(),
+ defaultStrategy
+ },
+ // Multiple GValue edge labels
+ {
+ create().V().hasLabel("person").out(GValue.of("x", "knows"), GValue.of("y", "created")).asAdmin(),
+ defaultStrategy
+ },
+ // Edge steps with GValue
+ {
+ create().V().hasLabel("person").outE(GValue.of("x", "knows")).inV().asAdmin(),
+ defaultStrategy
+ },
+ {
+ create().V().hasLabel("person").inE(GValue.of("x", "created")).outV().asAdmin(),
+ defaultStrategy
+ },
+ {
+ create().V().hasLabel("person").bothE(GValue.of("x", "created")).otherV().asAdmin(),
+ defaultStrategy
+ },
+ // Mutating steps with GValue
+ {
+ create().V().hasLabel("person").addE(GValue.of("x", "knows")).to("person").asAdmin(),
+ defaultStrategy
+ },
+ {
+ create().addV("person").property("name", GValue.of("x", "john")).asAdmin(),
+ defaultStrategy
+ },
+ {
+ create().addV("person").property("name", GValue.of("y", "john")).asAdmin(),
+ defaultStrategy
+ },
+ {
+ create().addV("person").property("name", GValue.of("y", "john")).asAdmin(),
+ PartitionStrategy.build().partitionKey("p").
+ writePartition("a").readPartitions("a").includeMetaProperties(true).create()
+ },
+ {
+ create().addV("person").property("name", GValue.of("y", "john")).property("age", GValue.of("z", 10)).asAdmin(),
+ PartitionStrategy.build().partitionKey("p").
+ writePartition("a").readPartitions("a").includeMetaProperties(true).create()
+ },
+ // Filter steps with GValue
+ {
+ create().V().hasLabel(GValue.of("x", "person")).asAdmin(),
+ defaultStrategy
+ },
+ // Complex traversals with GValue
+ {
+ create().V().hasLabel("person").out(GValue.of("x", "knows")).out(GValue.of("y", "created")).asAdmin(),
+ defaultStrategy
+ }
+ });
+ }
+
+ @Test
+ public void shouldMaintainGValueState() {
+ // Verify that after applying the strategy, the GValue state is properly maintained
+ GValueManagerVerifier.verify(traversal, strategy)
+ .afterApplying()
+ .variablesArePreserved();
+ }
+ }
+
+ public static GraphTraversal create() {
+ return create(null);
+ }
+
+ public static GraphTraversal create(final Class<? extends Element> clazz) {
+ final Graph mockedGraph = mock(Graph.class);
+ final Graph.Features features = mock(Graph.Features.class);
+ final Graph.Features.VertexFeatures vertexFeatures = mock(Graph.Features.VertexFeatures.class);
+ when(mockedGraph.features()).thenReturn(features);
+ when(features.vertex()).thenReturn(vertexFeatures);
+ when(vertexFeatures.getCardinality(any())).thenReturn(VertexProperty.Cardinality.single);
+ when(vertexFeatures.supportsMetaProperties()).thenReturn(true);
+ final DefaultGraphTraversal t = new DefaultGraphTraversal<>(mockedGraph);
+ if (clazz != null) t.asAdmin().addStep(new GraphStep<>(t.asAdmin(), clazz, true));
+ return t;
+ }
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategyTest.java
index 991c91c357..02ef89b8a5 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategyTest.java
@@ -60,13 +60,6 @@ public class AdjacentToIncidentStrategyTest {
@Parameterized.Parameter(value = 1)
public Traversal optimized;
- void applyAdjacentToIncidentStrategy(final Traversal traversal) {
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(AdjacentToIncidentStrategy.instance());
- traversal.asAdmin().setStrategies(strategies);
- traversal.asAdmin().applyStrategies();
- }
-
@Parameterized.Parameters(name = "{0}")
public static Iterable<Object[]> generateTestParameters() {
return Arrays.asList(new Traversal[][]{
@@ -94,9 +87,10 @@ public class AdjacentToIncidentStrategyTest {
@Test
public void doTest() {
final String repr = translator.translate(original.getBytecode()).getScript();
- applyAdjacentToIncidentStrategy(original);
+ GValueManagerVerifier.verify(original.asAdmin(), AdjacentToIncidentStrategy.instance())
+ .afterApplying()
+ .managerIsEmpty();
assertEquals(repr, optimized, original);
- assertThat(optimized.asAdmin().getGValueManager().isEmpty(), is(true));
}
}
@@ -163,12 +157,17 @@ public class AdjacentToIncidentStrategyTest {
@Test
public void shouldCopyGValueParameters() {
- GValueManagerVerifier.verify(traversal, AdjacentToIncidentStrategy.instance())
- .afterApplying()
- .hasVariables(expectedNames)
- .edgeLabelContractIsValid(traversal.getStartStep(), expectedLabelCount,
- expectedNames,
- expectedValues);
+ // get reference to the start step before we apply strategies as its going to get replaced and we need
+ // it to verify the before state.
+ final Step step = traversal.asAdmin().getSteps().get(0);
+ GValueManagerVerifier.verify(traversal, AdjacentToIncidentStrategy.instance()).
+ beforeApplying().
+ hasVariables(expectedNames).
+ edgeLabelContractIsValid(step, expectedLabelCount, expectedNames, expectedValues).
+ afterApplying().
+ hasVariables(expectedNames).
+ variablesArePreserved().
+ edgeLabelContractIsValid(traversal.getStartStep(), expectedLabelCount, expectedNames, expectedValues);
}
}
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategyTest.java
index 53f87ff294..f97b3e7f6b 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/ByModulatorOptimizationStrategyTest.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
import org.apache.tinkerpop.gremlin.structure.T;
@@ -37,6 +38,8 @@ import java.util.List;
import static org.apache.tinkerpop.gremlin.process.traversal.Operator.assign;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.values;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
/**
@@ -177,17 +180,12 @@ public class ByModulatorOptimizationStrategyTest {
return originalAndOptimized;
}
- private void applyByModulatorOptimizationStrategy(final Traversal traversal) {
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(ByModulatorOptimizationStrategy.instance());
- traversal.asAdmin().setStrategies(strategies);
- traversal.asAdmin().applyStrategies();
- }
-
@Test
public void doTest() {
final String repr = translator.translate(original.getBytecode()).getScript();
- applyByModulatorOptimizationStrategy(original);
+ GValueManagerVerifier.verify(original.asAdmin(), ByModulatorOptimizationStrategy.instance())
+ .afterApplying()
+ .managerIsEmpty();
assertEquals(repr, optimized, original);
}
}
\ No newline at end of file
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/CountStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/CountStrategyTest.java
index bcb816d7f6..cc299bd177 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/CountStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/CountStrategyTest.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
@@ -72,111 +73,106 @@ public class CountStrategyTest {
@Parameterized.Parameters(name = "{0}")
public static Iterable<Object[]> generateTestParameters() {
-
- return Arrays.asList(new Traversal[][]{
- {__.count().is(0), __.limit(1).count().is(0)},
- {__.count().is(1), __.limit(2).count().is(1)},
- {__.count().is(-1), __.limit(0).count().is(-1)}, // -1 for RangeGlobalStep means no upper limit
- {__.count().is(-2), __.limit(0).count().is(-2)},
- {__.count().is(-2023), __.limit(0).count().is(-2023)},
- {__.out().count().is(0), __.out().limit(1).count().is(0)},
- {__.outE().count().is(lt(1)), __.outE().limit(1).count().is(lt(1))},
- {__.both().count().is(lte(0)), __.both().limit(1).count().is(lte(0))},
- {__.map(__.out().count().is(0)), __.map(__.out().limit(1).count().is(0))},
- {__.map(__.outE().count().is(lt(1))), __.map(__.outE().limit(1).count().is(lt(1)))},
- {__.map(__.both().count().is(lte(0))), __.map(__.both().limit(1).count().is(lte(0)))},
- {__.filter(__.out().count().is(0)), __.not(__.out())},
- {__.filter(__.outE().count().is(lt(1))), __.not(__.outE())},
- {__.filter(__.both().count().is(lte(0))), __.not(__.both())},
- {__.filter(__.out().out().count().is(0)), __.not(__.out().out())},
- {__.store("x").count().is(0).as("a"), __.store("x").limit(1).count().is(0).as("a")},
- {__.out().count().as("a").is(0), __.out().limit(1).count().as("a").is(0)},
- {__.out().count().is(neq(4)), __.out().limit(5).count().is(neq(4))},
- {__.out().count().is(lte(3)), __.out().limit(4).count().is(lte(3))},
- {__.out().count().is(lt(3)), __.out().limit(3).count().is(lt(3))},
- {__.out().count().is(gt(2)), __.out().limit(3).count().is(gt(2))},
- {__.out().count().is(gte(2)), __.out().limit(2).count().is(gte(2))},
- {__.out().count().is(inside(2, 4)), __.out().limit(4).count().is(inside(2, 4))},
- {__.out().count().is(outside(2, 4)), __.out().limit(5).count().is(outside(2, 4))},
- {__.out().count().is(within(2, 6, 4)), __.out().limit(7).count().is(within(2, 6, 4))},
- {__.out().count().is(without(2, 6, 4)), __.out().limit(6).count().is(without(2, 6, 4))},
- {__.map(__.count().is(0)), __.map(__.limit(1).count().is(0))},
- {__.flatMap(__.count().is(0)), __.flatMap(__.limit(1).count().is(0))},
- {__.flatMap(__.count().is(0)).as("a"), __.flatMap(__.count().is(0)).as("a")},
- {__.filter(__.count().is(0)).as("a"), __.not(__.identity()).as("a")},
- {__.filter(__.count().is(0)), __.not(__.identity())},
- {__.sideEffect(__.count().is(0)), __.sideEffect(__.not(__.identity()))},
- {__.branch(__.count().is(0)), __.branch(__.limit(1).count().is(0))},
- {__.count().is(0).store("x"), __.limit(1).count().is(0).store("x")},
- {__.repeat(__.out()).until(__.outE().count().is(0)), __.repeat(__.out()).until(__.not(__.outE()))},
- {__.repeat(__.out()).until(__.out().out().values("name").count().is(0)), __.repeat(__.out()).until(__.out().out().not(__.values("name")))},
- {__.repeat(__.out()).until(__.out().out().properties("age").has("x").count().is(0)), __.repeat(__.out()).until(__.out().out().not(__.properties("age").has("x")))},
- {__.repeat(__.out()).emit(__.outE().count().is(0)), __.repeat(__.out()).emit(__.not(__.outE()))},
- {__.where(__.outE().hasLabel("created").count().is(0)), __.not(__.outE().hasLabel("created"))},
- {__.where(__.out().outE().hasLabel("created").count().is(0)), __.not(__.out().outE().hasLabel("created"))},
- {__.where(__.out().outE().hasLabel("created").count().is(0).store("x")), __.where(__.out().outE().hasLabel("created").limit(1).count().is(0).store("x"))},
- {__.where(__.or(__.out("none").out().count().is(0), __.has("none"))), __.where(__.or(__.not(__.out("none").out()), __.has("none"))) },
- {__.where(__.or(__.out("none").out().count().is(0), __.has("none").count().is(0))), __.where(__.or(__.not(__.out("none").out()), __.not(__.has("none")))) },
- {__.where(__.or(__.out("none").out(), __.has("none").count().is(0))), __.where(__.or(__.out("none").out(), __.not(__.has("none")))) },
- {__.filter(__.out().count().is(gt(-1))), __.filter(__.out().limit(0).count().is(gt(-1)))},
- {__.filter(__.out().count().is(gte(-1))), __.filter(__.out().limit(0).count().is(gte(-1)))},
- {__.filter(__.out().count().is(gte(0))), __.filter(__.out().limit(0).count().is(gte(0)))},
- {__.filter(__.out().count().is(lt(-1))), __.filter(__.out().limit(0).count().is(lt(-1)))},
- {__.filter(__.out().count().is(lt(0))), __.filter(__.out().limit(0).count().is(lt(0)))},
- {__.filter(__.out().count().is(lt(2))), __.filter(__.out().limit(2).count().is(lt(2)))},
- {__.filter(__.out().count().is(lte(-1))), __.filter(__.out().limit(0).count().is(lte(-1)))},
- {__.filter(__.out().count().is(lte(1))), __.filter(__.out().limit(2).count().is(lte(1)))},
- {__.filter(__.out().count().is(eq(-1))), __.filter(__.out().limit(0).count().is(eq(-1)))},
- {__.filter(__.out().count().is(neq(-1))), __.filter(__.out().limit(0).count().is(neq(-1)))},
- {__.filter(__.out().count().is(lte(1))), __.filter(__.out().limit(2).count().is(lte(1)))},
- {__.filter(__.out().count().is(lt(-3).and(gt(0)))), __.filter(__.out().limit(1).count().is(lt(-3).and(gt(0))))},
- {__.filter(__.out().count().is(lt(1).and(gt(-5)))), __.filter(__.out().limit(1).count().is(lt(1).and(gt(-5))))},
- {__.filter(__.out().count().is(gt(-5).and(lt(1)))), __.filter(__.out().limit(1).count().is(gt(-5).and(lt(1))))},
- {__.filter(__.out().count().is(gt(-5).and(gt(7)))), __.filter(__.out().limit(8).count().is(gt(-5).and(gt(7))))},
- {__.filter(__.out().count().is(lt(1).or(gt(-5)))), __.filter(__.out().limit(1).count().is(lt(1).or(gt(-5))))},
- {__.filter(__.out().count().is(lt(1).or(gt(3)))), __.filter(__.out().limit(4).count().is(lt(1).or(gt(3))))},
- {__.filter(__.out().count().is(gt(3).or(lt(-51)))), __.filter(__.out().limit(4).count().is(gt(3).or(lt(-51))))},
- {__.filter(__.out().count().is(gt(-5).or(lt(1)))), __.filter(__.out().limit(1).count().is(gt(-5).or(lt(1))))},
- {__.filter(__.out().count().is(lt(10).and(gt(1)))), __.filter(__.out().limit(10).count().is(lt(10).and(gt(1))))},
- {__.filter(__.out().count().is(gt(1).and(lt(10)))), __.filter(__.out().limit(10).count().is(gt(1).and(lt(10))))},
- {__.filter(__.out().count().is(lt(10).or(gt(1)))), __.filter(__.out().limit(10).count().is(lt(10).or(gt(1))))},
- {__.filter(__.out().count().is(gt(1).or(lt(10)))), __.filter(__.out().limit(10).count().is(gt(1).or(lt(10))))},
- {__.filter(__.out().count().is(lt(5).and(gt(10)))), __.filter(__.out().limit(11).count().is(lt(5).and(gt(10))))},
- {__.filter(__.out().count().is(gt(10).and(lt(5)))), __.filter(__.out().limit(11).count().is(gt(10).and(lt(5))))},
- {__.filter(__.out().count().is(lt(-5).and(lt(1)))), __.filter(__.out().limit(1).count().is(lt(-5).and(lt(1))))},
- {__.filter(__.out().count().is(lt(-5).and(lt(-1)))), __.filter(__.out().limit(0).count().is(lt(-5).and(lt(-1))))},
- {__.filter(__.out().count().is(lt(-5).or(lt(-1)))), __.filter(__.out().limit(0).count().is(lt(-5).or(lt(-1))))},
- {__.where(__.in("knows").count().is(inside(-1,1).and(lt(-1)))), __.where(__.in("knows").limit(1).count().is(inside(-1,1).and(lt(-1))))}, // TINKERPOP-2893
- {__.where(__.inV().count().is(outside(-1,-2))), __.where(__.inV().limit(0).count().is(outside(-1,-2)))}, // TINKERPOP-2892
- {__.filter(__.bothE().count().is(gt(0))), __.filter(__.bothE())},
- {__.filter(__.bothE().count().is(gte(1))), __.filter(__.bothE())},
- {__.filter(__.bothE().count().is(gt(1))), __.filter(__.bothE().limit(2).count().is(gt(1)))},
- {__.filter(__.bothE().count().is(gte(2))), __.filter(__.bothE().limit(2).count().is(gte(2)))},
- {__.and(__.out().count().is(0), __.in().count().is(0)), __.and(__.not(__.out()), __.not(__.in()))},
- {__.and(__.out().count().is(0), __.in().count().is(1)), __.and(__.not(__.out()), __.in().limit(2).count().is(1))},
- {__.and(__.out().count().is(1), __.in().count().is(0)), __.and(__.out().limit(2).count().is(1), __.not(__.in()))},
- {__.and(__.out().out().count().is(0), __.in().count().is(0)), __.and(__.not(__.out().out()), __.not(__.in()))},
- {__.or(__.out().count().is(0), __.in().count().is(0)), __.or(__.not(__.out()), __.not(__.in()))},
- {__.path().filter(__.count().is(gte(0.5))).limit(5), __.path().identity().limit(5)}, // unfortunately we can't just remove the filter step
- {__.path().filter(__.unfold().count().is(gte(0.5))), __.path().filter(__.unfold())},
- {__.path().filter(__.unfold().count().is(gte(1.5))), __.path().filter(__.unfold().limit(2).count().is(gte(1.5)))},
- });
- }
-
- void applyCountStrategy(final Traversal traversal) {
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(CountStrategy.instance());
- traversal.asAdmin().setStrategies(strategies);
- traversal.asAdmin().applyStrategies();
+ return Arrays.asList(new Traversal[][]{
+ {__.count().is(0), __.limit(1).count().is(0)},
+ {__.count().is(1), __.limit(2).count().is(1)},
+ {__.count().is(-1), __.limit(0).count().is(-1)}, // -1 for RangeGlobalStep means no upper limit
+ {__.count().is(-2), __.limit(0).count().is(-2)},
+ {__.count().is(-2023), __.limit(0).count().is(-2023)},
+ {__.count().is(P.between(0, 10)), __.limit(10).count().is(P.between(0, 10))},
+ {__.count().is(P.between(5, 10)), __.limit(10).count().is(P.between(5, 10))},
+ {__.count().is(P.gt(10).or(lt(5))), __.limit(11).count().is(P.gt(10).or(lt(5)))},
+ {__.out().count().is(0), __.out().limit(1).count().is(0)},
+ {__.outE().count().is(lt(1)), __.outE().limit(1).count().is(lt(1))},
+ {__.both().count().is(lte(0)), __.both().limit(1).count().is(lte(0))},
+ {__.map(__.out().count().is(0)), __.map(__.out().limit(1).count().is(0))},
+ {__.map(__.outE().count().is(lt(1))), __.map(__.outE().limit(1).count().is(lt(1)))},
+ {__.map(__.both().count().is(lte(0))), __.map(__.both().limit(1).count().is(lte(0)))},
+ {__.filter(__.out().count().is(0)), __.not(__.out())},
+ {__.filter(__.outE().count().is(lt(1))), __.not(__.outE())},
+ {__.filter(__.both().count().is(lte(0))), __.not(__.both())},
+ {__.filter(__.out().out().count().is(0)), __.not(__.out().out())},
+ {__.store("x").count().is(0).as("a"), __.store("x").limit(1).count().is(0).as("a")},
+ {__.out().count().as("a").is(0), __.out().limit(1).count().as("a").is(0)},
+ {__.out().count().is(neq(4)), __.out().limit(5).count().is(neq(4))},
+ {__.out().count().is(lte(3)), __.out().limit(4).count().is(lte(3))},
+ {__.out().count().is(lt(3)), __.out().limit(3).count().is(lt(3))},
+ {__.out().count().is(gt(2)), __.out().limit(3).count().is(gt(2))},
+ {__.out().count().is(gte(2)), __.out().limit(2).count().is(gte(2))},
+ {__.out().count().is(inside(2, 4)), __.out().limit(4).count().is(inside(2, 4))},
+ {__.out().count().is(outside(2, 4)), __.out().limit(5).count().is(outside(2, 4))},
+ {__.out().count().is(within(2, 6, 4)), __.out().limit(7).count().is(within(2, 6, 4))},
+ {__.out().count().is(without(2, 6, 4)), __.out().limit(6).count().is(without(2, 6, 4))},
+ {__.map(__.count().is(0)), __.map(__.limit(1).count().is(0))},
+ {__.flatMap(__.count().is(0)), __.flatMap(__.limit(1).count().is(0))},
+ {__.flatMap(__.count().is(0)).as("a"), __.flatMap(__.count().is(0)).as("a")}, // no change
+ {__.filter(__.count().is(0)).as("a"), __.not(__.identity()).as("a")},
+ {__.filter(__.count().is(0)), __.not(__.identity())},
+ {__.sideEffect(__.count().is(0)), __.sideEffect(__.not(__.identity()))},
+ {__.branch(__.count().is(0)), __.branch(__.limit(1).count().is(0))},
+ {__.count().is(0).store("x"), __.limit(1).count().is(0).store("x")},
+ {__.repeat(__.out()).until(__.outE().count().is(0)), __.repeat(__.out()).until(__.not(__.outE()))},
+ {__.repeat(__.out()).until(__.out().out().values("name").count().is(0)), __.repeat(__.out()).until(__.out().out().not(__.values("name")))},
+ {__.repeat(__.out()).until(__.out().out().properties("age").has("x").count().is(0)), __.repeat(__.out()).until(__.out().out().not(__.properties("age").has("x")))},
+ {__.repeat(__.out()).emit(__.outE().count().is(0)), __.repeat(__.out()).emit(__.not(__.outE()))},
+ {__.where(__.outE().hasLabel("created").count().is(0)), __.not(__.outE().hasLabel("created"))},
+ {__.where(__.out().outE().hasLabel("created").count().is(0)), __.not(__.out().outE().hasLabel("created"))},
+ {__.where(__.out().outE().hasLabel("created").count().is(0).store("x")), __.where(__.out().outE().hasLabel("created").limit(1).count().is(0).store("x"))},
+ {__.where(__.or(__.out("none").out().count().is(0), __.has("none"))), __.where(__.or(__.not(__.out("none").out()), __.has("none"))) },
+ {__.where(__.or(__.out("none").out().count().is(0), __.has("none").count().is(0))), __.where(__.or(__.not(__.out("none").out()), __.not(__.has("none")))) },
+ {__.where(__.or(__.out("none").out(), __.has("none").count().is(0))), __.where(__.or(__.out("none").out(), __.not(__.has("none")))) },
+ {__.filter(__.out().count().is(gt(-1))), __.filter(__.out().limit(0).count().is(gt(-1)))},
+ {__.filter(__.out().count().is(gte(-1))), __.filter(__.out().limit(0).count().is(gte(-1)))},
+ {__.filter(__.out().count().is(gte(0))), __.filter(__.out().limit(0).count().is(gte(0)))},
+ {__.filter(__.out().count().is(lt(-1))), __.filter(__.out().limit(0).count().is(lt(-1)))},
+ {__.filter(__.out().count().is(lt(0))), __.filter(__.out().limit(0).count().is(lt(0)))},
+ {__.filter(__.out().count().is(lt(2))), __.filter(__.out().limit(2).count().is(lt(2)))},
+ {__.filter(__.out().count().is(lte(-1))), __.filter(__.out().limit(0).count().is(lte(-1)))},
+ {__.filter(__.out().count().is(lte(1))), __.filter(__.out().limit(2).count().is(lte(1)))},
+ {__.filter(__.out().count().is(eq(-1))), __.filter(__.out().limit(0).count().is(eq(-1)))},
+ {__.filter(__.out().count().is(neq(-1))), __.filter(__.out().limit(0).count().is(neq(-1)))},
+ {__.filter(__.out().count().is(lte(1))), __.filter(__.out().limit(2).count().is(lte(1)))},
+ {__.filter(__.out().count().is(lt(-3).and(gt(0)))), __.filter(__.out().limit(1).count().is(lt(-3).and(gt(0))))},
+ {__.filter(__.out().count().is(lt(1).and(gt(-5)))), __.filter(__.out().limit(1).count().is(lt(1).and(gt(-5))))},
+ {__.filter(__.out().count().is(gt(-5).and(lt(1)))), __.filter(__.out().limit(1).count().is(gt(-5).and(lt(1))))},
+ {__.filter(__.out().count().is(gt(-5).and(gt(7)))), __.filter(__.out().limit(8).count().is(gt(-5).and(gt(7))))},
+ {__.filter(__.out().count().is(lt(1).or(gt(-5)))), __.filter(__.out().limit(1).count().is(lt(1).or(gt(-5))))},
+ {__.filter(__.out().count().is(lt(1).or(gt(3)))), __.filter(__.out().limit(4).count().is(lt(1).or(gt(3))))},
+ {__.filter(__.out().count().is(gt(3).or(lt(-51)))), __.filter(__.out().limit(4).count().is(gt(3).or(lt(-51))))},
+ {__.filter(__.out().count().is(gt(-5).or(lt(1)))), __.filter(__.out().limit(1).count().is(gt(-5).or(lt(1))))},
+ {__.filter(__.out().count().is(lt(10).and(gt(1)))), __.filter(__.out().limit(10).count().is(lt(10).and(gt(1))))},
+ {__.filter(__.out().count().is(gt(1).and(lt(10)))), __.filter(__.out().limit(10).count().is(gt(1).and(lt(10))))},
+ {__.filter(__.out().count().is(lt(10).or(gt(1)))), __.filter(__.out().limit(10).count().is(lt(10).or(gt(1))))},
+ {__.filter(__.out().count().is(gt(1).or(lt(10)))), __.filter(__.out().limit(10).count().is(gt(1).or(lt(10))))},
+ {__.filter(__.out().count().is(lt(5).and(gt(10)))), __.filter(__.out().limit(11).count().is(lt(5).and(gt(10))))},
+ {__.filter(__.out().count().is(gt(10).and(lt(5)))), __.filter(__.out().limit(11).count().is(gt(10).and(lt(5))))},
+ {__.filter(__.out().count().is(lt(-5).and(lt(1)))), __.filter(__.out().limit(1).count().is(lt(-5).and(lt(1))))},
+ {__.filter(__.out().count().is(lt(-5).and(lt(-1)))), __.filter(__.out().limit(0).count().is(lt(-5).and(lt(-1))))},
+ {__.filter(__.out().count().is(lt(-5).or(lt(-1)))), __.filter(__.out().limit(0).count().is(lt(-5).or(lt(-1))))},
+ {__.where(__.in("knows").count().is(inside(-1,1).and(lt(-1)))), __.where(__.in("knows").limit(1).count().is(inside(-1,1).and(lt(-1))))}, // TINKERPOP-2893
+ {__.where(__.inV().count().is(outside(-1,-2))), __.where(__.inV().limit(0).count().is(outside(-1,-2)))}, // TINKERPOP-2892
+ {__.filter(__.bothE().count().is(gt(0))), __.filter(__.bothE())},
+ {__.filter(__.bothE().count().is(gte(1))), __.filter(__.bothE())},
+ {__.filter(__.bothE().count().is(gt(1))), __.filter(__.bothE().limit(2).count().is(gt(1)))},
+ {__.filter(__.bothE().count().is(gte(2))), __.filter(__.bothE().limit(2).count().is(gte(2)))},
+ {__.and(__.out().count().is(0), __.in().count().is(0)), __.and(__.not(__.out()), __.not(__.in()))},
+ {__.and(__.out().count().is(0), __.in().count().is(1)), __.and(__.not(__.out()), __.in().limit(2).count().is(1))},
+ {__.and(__.out().count().is(1), __.in().count().is(0)), __.and(__.out().limit(2).count().is(1), __.not(__.in()))},
+ {__.and(__.out().out().count().is(0), __.in().count().is(0)), __.and(__.not(__.out().out()), __.not(__.in()))},
+ {__.or(__.out().count().is(0), __.in().count().is(0)), __.or(__.not(__.out()), __.not(__.in()))},
+ {__.path().filter(__.count().is(gte(0.5))).limit(5), __.path().identity().limit(5)}, // unfortunately we can't just remove the filter step
+ {__.path().filter(__.unfold().count().is(gte(0.5))), __.path().filter(__.unfold())},
+ {__.path().filter(__.unfold().count().is(gte(1.5))), __.path().filter(__.unfold().limit(2).count().is(gte(1.5)))},
+ });
}
@Test
public void doTest() {
final String repr = translator.translate(original.getBytecode()).getScript();
- applyCountStrategy(original);
+ GValueManagerVerifier.verify(original.asAdmin(), CountStrategy.instance())
+ .afterApplying()
+ .managerIsEmpty();
assertEquals(repr, optimized, original);
-
- assertThat(optimized.asAdmin().getGValueManager().isEmpty(), is(true));
}
}
@@ -195,6 +191,7 @@ public class CountStrategyTest {
return Arrays.asList(new Object[][]{
{ __.count().is(GValue.of("x", 0)).asAdmin() },
{ __.count().is(GValue.of("x", 1)).asAdmin() },
+ { __.count().is(P.between(GValue.of("x", 0), GValue.of("y", 10)))},
{ __.out().count().is(gt(GValue.ofInteger("x", 1))).asAdmin() },
{ __.out().count().is(lt(GValue.ofInteger("x", 1))).asAdmin() },
{ __.out().count().is(gte(GValue.ofInteger("x", 1))).asAdmin() },
@@ -236,7 +233,7 @@ public class CountStrategyTest {
{__.count().is(GValue.of("x", 0)).store("x")},
{__.repeat(__.out()).until(__.outE().count().is(GValue.of("x", 0)))},
{__.repeat(__.out()).until(__.out().out().values("name").count().is(GValue.of("x", 0)))},
- {__.repeat(__.out()).until(__.out().out().properties("age").has("x").count().is(GValue.of("x", 0)))}, /// //////////
+ {__.repeat(__.out()).until(__.out().out().properties("age").has("x").count().is(GValue.of("x", 0)))},
{__.repeat(__.out()).emit(__.outE().count().is(GValue.of("x", 0)))},
{__.where(__.outE().hasLabel("created").count().is(GValue.of("x", 0)))},
{__.where(__.out().outE().hasLabel("created").count().is(GValue.of("x", 0)))},
@@ -286,6 +283,13 @@ public class CountStrategyTest {
{__.path().filter(__.count().is(gte(GValue.ofDouble("x", 0.5)))).limit(5)},
{__.path().filter(__.unfold().count().is(gte(GValue.ofDouble("x", 0.5))))},
{__.path().filter(__.unfold().count().is(gte(GValue.ofDouble("x", 1.5))))},
+ {__.project("a").by(__.count().is(GValue.of("x", 0)))},
+ {__.select("a").by(__.count().is(GValue.of("x", 0)))},
+ {__.group().by(__.count().is(GValue.of("x", 0)))},
+ {__.choose(__.count().is(GValue.of("x", 0)), __.out(), __.in())},
+ {__.count().as("c").is(GValue.of("x", 0))},
+ {__.union(__.count().is(GValue.of("x", 0)), __.out().count())},
+ {__.coalesce(__.count().is(GValue.of("x", 0)), __.out().count())},
});
}
@@ -314,6 +318,9 @@ public class CountStrategyTest {
public static Iterable<Object[]> generateTestParameters() {
return Arrays.asList(new Object[][]{
{__.flatMap(__.count().is(GValue.of("x", 0))).as("a")},
+ {__.limit(5).count().is(GValue.of("x", 0))},
+ {__.range(1, 5).count().is(GValue.of("x", 0))},
+ {__.count().is(P.within(GValue.of("x", "string"), GValue.of("y", "another")))},
});
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/EarlyLimitStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/EarlyLimitStrategyTest.java
index 21a62723c9..f4415b42eb 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/EarlyLimitStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/EarlyLimitStrategyTest.java
@@ -20,7 +20,6 @@
package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
-import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
@@ -29,7 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.RangeContract;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
@@ -46,6 +45,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
@@ -124,7 +124,7 @@ public class EarlyLimitStrategyTest {
this.original.asAdmin().applyStrategies();
assertEquals(repr, this.optimized, this.original);
- assertThat(optimized.asAdmin().getGValueManager().isEmpty(), is(true));
+ assertThat(original.asAdmin().getGValueManager().hasVariables(), is(true));
}
}
@@ -136,7 +136,7 @@ public class EarlyLimitStrategyTest {
public static class GValuePropagatedTest {
@Parameterized.Parameter(value = 0)
- public Traversal.Admin<?, ?> original;
+ public Traversal.Admin<?, ?> traversal;
@Parameterized.Parameter(value = 1)
public int expectedLowRange;
@@ -197,47 +197,19 @@ public class EarlyLimitStrategyTest {
@Test
public void shouldMaintainGValueManagerState() {
- final Optional<RangeGlobalStep> maybeOriginalRangeGlobalStep = TraversalHelper.getFirstStepOfAssignableClass(RangeGlobalStep.class, original);
+ final Optional<RangeGlobalStep> maybeOriginalRangeGlobalStep = TraversalHelper.getFirstStepOfAssignableClass(RangeGlobalStep.class, traversal);
assertThat(maybeOriginalRangeGlobalStep.isPresent(), is(true));
final RangeGlobalStep originalRangeGlobalStep = maybeOriginalRangeGlobalStep.get();
- // Apply the EarlyLimitStrategy
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(EarlyLimitStrategy.instance());
- original.setStrategies(strategies);
-
- final GValueManager gValueManager = original.getGValueManager();
- original.applyStrategies();
-
- // the original step should not have any context in the GValueManager
- assertNull(gValueManager.getStepContract(originalRangeGlobalStep));
-
- // Find the RangeGlobalStep in the optimized traversal
- final Optional<RangeGlobalStep> maybeOptimizedRangeGlobalStep = TraversalHelper.getFirstStepOfAssignableClass(RangeGlobalStep.class, original);
- assertThat(maybeOptimizedRangeGlobalStep.isPresent(), is(true));
- final RangeGlobalStep optimizedRangeGlobalStep = maybeOptimizedRangeGlobalStep.get();
-
- // The optimized step should exist
- assertNotNull(optimizedRangeGlobalStep);
-
- // better not be the same step coz we cloned stuff
- assertNotSame(originalRangeGlobalStep, optimizedRangeGlobalStep);
-
- // optimized step should have the same GValue parameters
- assertThat(gValueManager.isParameterized(optimizedRangeGlobalStep), is(true));
- final RangeContract<GValue<Long>> optimizedContract = gValueManager.getStepContract(optimizedRangeGlobalStep);
- assertNotNull(optimizedContract);
-
- // Verify the range values
- assertEquals(expectedLowRange, optimizedContract.getLowRange().get().longValue());
- assertEquals(expectedHighRange, optimizedContract.getHighRange().get().longValue());
-
- // Verify the parameter names
- assertEquals(expectedNames[0], optimizedContract.getLowRange().getName());
- assertEquals(expectedNames[1], optimizedContract.getHighRange().getName());
-
- assertEquals(CollectionUtil.asSet(Arrays.stream(expectedNames).filter(Objects::nonNull).toArray(String[]::new)),
- gValueManager.variableNames());
+ // Use GValueManagerVerifier to verify the state before and after applying the strategy
+ GValueManagerVerifier.verify(traversal, EarlyLimitStrategy.instance()).
+ beforeApplying().
+ rangeContractIsValid(originalRangeGlobalStep, expectedLowRange, expectedHighRange, expectedNames[0], expectedNames[1]).
+ afterApplying().
+ rangeContractIsValid(
+ TraversalHelper.getFirstStepOfAssignableClass(RangeGlobalStep.class, traversal).get(),
+ expectedLowRange, expectedHighRange, expectedNames[0], expectedNames[1]).
+ hasVariables(CollectionUtil.asSet(Arrays.stream(expectedNames).filter(Objects::nonNull).toArray(String[]::new)));
}
}
@@ -249,7 +221,7 @@ public class EarlyLimitStrategyTest {
public static class GValueLeftAloneTest {
@Parameterized.Parameter(value = 0)
- public Traversal.Admin<?, ?> original;
+ public Traversal.Admin<?, ?> traversal;
@Parameterized.Parameter(value = 1)
public int expectedLowRange;
@@ -289,43 +261,17 @@ public class EarlyLimitStrategyTest {
@Test
public void shouldMaintainGValueManagerState() {
- final Optional<RangeGlobalStep> maybeOriginalRangeGlobalStep = TraversalHelper.getFirstStepOfAssignableClass(RangeGlobalStep.class, original);
+ final Optional<RangeGlobalStep> maybeOriginalRangeGlobalStep = TraversalHelper.getFirstStepOfAssignableClass(RangeGlobalStep.class, traversal);
assertThat(maybeOriginalRangeGlobalStep.isPresent(), is(true));
final RangeGlobalStep originalRangeGlobalStep = maybeOriginalRangeGlobalStep.get();
- // Apply the EarlyLimitStrategy
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(EarlyLimitStrategy.instance());
- original.setStrategies(strategies);
-
- final GValueManager gValueManager = original.getGValueManager();
- original.applyStrategies();
-
- // the original step should be in the GValueManager
- assertNotNull(gValueManager.getStepContract(originalRangeGlobalStep));
-
- // Find the RangeGlobalStep in the optimized traversal
- final Optional<RangeGlobalStep> maybeOptimizedRangeGlobalStep = TraversalHelper.getFirstStepOfAssignableClass(RangeGlobalStep.class, original);
- assertThat(maybeOptimizedRangeGlobalStep.isPresent(), is(true));
- final RangeGlobalStep optimizedRangeGlobalStep = maybeOptimizedRangeGlobalStep.get();
-
- // The optimized step should exist, be the same instance as the original and have the same GValue parameters
- assertNotNull(optimizedRangeGlobalStep);
- assertSame(originalRangeGlobalStep, optimizedRangeGlobalStep);
- assertThat(gValueManager.isParameterized(optimizedRangeGlobalStep), is(true));
- final RangeContract<GValue<Long>> optimizedContract = gValueManager.getStepContract(optimizedRangeGlobalStep);
- assertNotNull(optimizedContract);
-
- // Verify the range values
- assertEquals(expectedLowRange, optimizedContract.getLowRange().get().longValue());
- assertEquals(expectedHighRange, optimizedContract.getHighRange().get().longValue());
-
- // Verify the parameter names
- assertEquals(expectedNames[0], optimizedContract.getLowRange().getName());
- assertEquals(expectedNames[1], optimizedContract.getHighRange().getName());
-
- assertEquals(CollectionUtil.asSet(Arrays.stream(expectedNames).filter(Objects::nonNull).toArray(String[]::new)),
- gValueManager.variableNames());
+ // Use GValueManagerVerifier to verify the state before and after applying the strategy
+ GValueManagerVerifier.verify(traversal, EarlyLimitStrategy.instance()).
+ beforeApplying().
+ rangeContractIsValid(originalRangeGlobalStep, expectedLowRange, expectedHighRange, expectedNames[0], expectedNames[1]).
+ afterApplying().
+ rangeContractIsValid(TraversalHelper.getFirstStepOfAssignableClass(RangeGlobalStep.class, traversal).get(),expectedLowRange, expectedHighRange, expectedNames[0], expectedNames[1]).
+ hasVariables(CollectionUtil.asSet(Arrays.stream(expectedNames).filter(Objects::nonNull).toArray(String[]::new)));
}
}
@@ -337,7 +283,10 @@ public class EarlyLimitStrategyTest {
public static class GValueExpectClearStateTest {
@Parameterized.Parameter(value = 0)
- public Traversal.Admin<?, ?> original;
+ public Traversal.Admin<?, ?> traversal;
+
+ @Parameterized.Parameter(value = 1)
+ public Set<String> expectedVariables;
@Parameterized.Parameters(name = "{0}")
public static Iterable<Object[]> generateTestParameters() {
@@ -345,53 +294,40 @@ public class EarlyLimitStrategyTest {
{
__.out().range(GValue.of("x", 5L), GValue.of("y", -1L)).
map(__.identity()).
- range(GValue.of("z", 10L), GValue.of("w", 60L)).asAdmin()
+ range(GValue.of("z", 10L), GValue.of("w", 60L)).asAdmin(),
+ CollectionUtil.asSet("x", "y", "z", "w")
},
{
__.out().limit(GValue.of("a", 15L)).
map(__.identity()).
- limit(GValue.of("b", 10L)).asAdmin()
+ limit(GValue.of("b", 10L)).asAdmin(),
+ CollectionUtil.asSet("a", "b")
},
{
__.out().skip(GValue.of("c", 5L)).
map(__.identity()).
- limit(GValue.of("d", 20L)).asAdmin()
+ limit(GValue.of("d", 20L)).asAdmin(),
+ CollectionUtil.asSet("c", "d")
},
{
__.out().limit(GValue.of("e", 30L)).
map(__.identity()).
- skip(GValue.of("f", 10L)).asAdmin()
+ skip(GValue.of("f", 10L)).asAdmin(),
+ CollectionUtil.asSet("e", "f")
}
});
}
@Test
public void shouldMaintainGValueManagerState() {
- final List<RangeGlobalStep> originalRangeSteps = TraversalHelper.getStepsOfAssignableClass(RangeGlobalStep.class, original);
-
- // Apply the EarlyLimitStrategy
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(EarlyLimitStrategy.instance());
- original.setStrategies(strategies);
-
- final GValueManager gValueManager = original.getGValueManager();
- original.applyStrategies();
-
- // the original range steps should not have any context in the GValueManager because they've merged and
- // that context is lost
- for (RangeGlobalStep step : originalRangeSteps) {
- assertNull(gValueManager.getStepContract(step));
- assertThat(gValueManager.isParameterized(step), is(false));
- }
-
- // Find the merged RangeGlobalStep in the optimized traversal
- final List<RangeGlobalStep> optimizedRangeSteps = TraversalHelper.getStepsOfAssignableClass(RangeGlobalStep.class, original);
- assertEquals(1, optimizedRangeSteps.size());
- final RangeGlobalStep optimizedRangeGlobalStep = optimizedRangeSteps.get(0);
-
- // The optimized step should exist but the GValue stuff should be gone
- assertNotNull(optimizedRangeGlobalStep);
- assertThat(gValueManager.isParameterized(optimizedRangeGlobalStep), is(false));
+ final List<RangeGlobalStep> originalRangeSteps = TraversalHelper.getStepsOfAssignableClass(RangeGlobalStep.class, traversal);
+
+ // Use GValueManagerVerifier to verify the state before and after applying the strategy
+ GValueManagerVerifier.verify(traversal, EarlyLimitStrategy.instance()).
+ beforeApplying().
+ hasVariables(expectedVariables).
+ afterApplying().
+ managerIsEmpty();
}
}
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/FilterRankingStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/FilterRankingStrategyTest.java
index 26ac4dc433..6329122414 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/FilterRankingStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/FilterRankingStrategyTest.java
@@ -18,23 +18,32 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.util.CollectionUtil;
import org.apache.tinkerpop.gremlin.util.function.Lambda;
import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.function.Predicate;
import static org.apache.tinkerpop.gremlin.process.traversal.P.eq;
@@ -48,188 +57,258 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.or;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.outE;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.properties;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-@RunWith(Parameterized.class)
+@RunWith(Enclosed.class)
public class FilterRankingStrategyTest {
private static final Translator.ScriptTranslator translator = GroovyTranslator.of("__");
- public static Iterable<Object[]> data() {
- return generateTestParameters();
- }
+ @RunWith(Parameterized.class)
+ public static class StandardTest {
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin original;
+
+ @Parameterized.Parameter(value = 1)
+ public Traversal optimized;
+
+ @Parameterized.Parameter(value = 2)
+ public Collection<TraversalStrategy> otherStrategies;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ final Predicate testP = t -> true;
+
+ return Arrays.asList(new Object[][]{
+ {__.has("name", "marko").as("a").out().as("b").has("age", 32).where("a", neq("b")).as("c").out(),
+ __.has("name", "marko").as("a").out().has("age", 32).as("b").where("a", neq("b")).as("c").out(),
+ Collections.emptyList()},
+
+ {__.V().as("n").where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person"))).select("n").by("name"),
+ __.V().as("n").where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person"))).select("n").by("name"),
+ Collections.emptyList()},
- @Parameterized.Parameter(value = 0)
- public Traversal.Admin original;
+ {__.V().as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person")))))).select("n").by("name"),
+ __.V().as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person")))))).select("n").by("name"),
+ Collections.emptyList()},
- @Parameterized.Parameter(value = 1)
- public Traversal optimized;
+ {__.V().as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person")))))).has("test", "val").select("n").by("name"),
+ __.V().has("test", "val").as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person")))))).select("n").by("name"),
+ Collections.emptyList()},
- @Parameterized.Parameter(value = 2)
- public Collection<TraversalStrategy> otherStrategies;
+ {__.V().as("n").out().where(__.or(__.hasLabel("out-dest-label1").select("n").hasLabel("software"),__.hasLabel("out-dest-label2").select("n").hasLabel("person"))).select("n").by("name"),
+ __.V().as("n").out().where(__.or(__.hasLabel("out-dest-label1").select("n").hasLabel("software"),__.hasLabel("out-dest-label2").select("n").hasLabel("person"))).select("n").by("name"),
+ Collections.emptyList()},
- @Test
- public void doTest() {
- final String repr = translator.translate(original.getBytecode()).getScript();
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(FilterRankingStrategy.instance());
- for (final TraversalStrategy strategy : this.otherStrategies) {
- strategies.addStrategies(strategy);
+ // some lambda tests, where reordering over lambdas should not happen
+ {__.order().map(Lambda.function("it.get().out()")).dedup(),
+ __.order().map(Lambda.function("it.get().out()")).dedup(),
+ Collections.emptyList()},
+
+ {__.order().filter(__.map(Lambda.function("it.get().out()"))).dedup(),
+ __.order().filter(__.map(Lambda.function("it.get().out()"))).dedup(),
+ Collections.emptyList()},
+
+ {__.dedup().order(),
+ __.dedup().order(),
+ Collections.emptyList()},
+ {__.has("name", "marko").as("a").out().as("b").has("age", 32).where("a", neq("b")).as("c").out(),
+ __.has("name", "marko").as("a").out().has("age", 32).as("b").where("a", neq("b")).as("c").out(),
+ Collections.emptyList()},
+ {__.has("name", "marko").as("a").out().has("age", 32).as("b").where("a", neq("b")),
+ __.has("name", "marko").as("a").out().has("age", 32).as("b").where("a", neq("b")),
+ Collections.emptyList()},
+ {__.has("name", "marko").has("age", 32).dedup().has("name", "bob").as("a"),
+ __.has("name", "marko").has("age", 32).has("name", "bob").dedup().as("a"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.has("name", "marko").dedup().as("a").has("age", 32).has("name", "bob").as("b"),
+ __.has("name", "marko").has("age", 32).has("name", "bob").dedup().as("b", "a"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.where("b", eq("c")).as("a").dedup("a").has("name", "marko"),
+ __.has("name", "marko").where("b", eq("c")).as("a").dedup("a"),
+ Collections.emptyList()},
+ {__.where("b", eq("c")).has("name", "bob").as("a").dedup("a").has("name", "marko"),
+ __.has("name", "bob").has("name", "marko").where("b", eq("c")).as("a").dedup("a"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.has("name", "marko").as("a").out().has("name", "bob").dedup().as("b").where(__.as("a").out().as("b")),
+ __.has("name", "marko").as("a").out().has("name", "bob").dedup().as("b").where(__.as("a").out().as("b")),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.has("name", "marko").as("a").out().has("name", "bob").as("b").dedup().where(__.as("a").out().as("b")),
+ __.has("name", "marko").as("a").out().has("name", "bob").dedup().as("b").where(__.as("a").out().as("b")),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.has("name", "marko").as("a").out().has("name", "bob").dedup().as("c").where(__.as("a").out().as("b")),
+ __.has("name", "marko").as("a").out().has("name", "bob").where(__.as("a").out().as("b")).dedup().as("c"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.order().dedup(),
+ __.dedup().order(),
+ Collections.emptyList()},
+ {__.order().filter(testP).dedup(),
+ __.order().filter(testP).dedup(),
+ Collections.emptyList()},
+ {__.order().as("a").dedup(),
+ __.dedup().order().as("a"),
+ Collections.emptyList()},
+ {__.order().as("a").dedup("a"),
+ __.order().as("a").dedup("a"),
+ Collections.emptyList()},
+ {__.order().as("a").dedup("a").has("name", "marko"),
+ __.has("name", "marko").as("a").dedup("a").order(),
+ Collections.emptyList()},
+ {__.order().as("a").dedup("a").has("name", "marko").out(),
+ __.has("name", "marko").as("a").dedup("a").order().out(),
+ Collections.emptyList()},
+ {__.order().as("a").dedup("a").has("name", "marko").where("a", eq("b")).out(),
+ __.has("name", "marko").as("a").where("a", eq("b")).dedup("a").order().out(),
+ Collections.emptyList()},
+ {__.identity().order().dedup(),
+ __.dedup().order(),
+ Collections.singletonList(IdentityRemovalStrategy.instance())},
+ {__.order().identity().dedup(),
+ __.dedup().order(),
+ Collections.singletonList(IdentityRemovalStrategy.instance())},
+ {__.order().out().dedup(),
+ __.order().out().dedup(),
+ Collections.emptyList()},
+ {has("value", 0).filter(out()).dedup(),
+ has("value", 0).filter(out()).dedup(),
+ Collections.emptyList()},
+ {__.dedup().has("value", 0).or(not(has("age")), has("age", 10)).has("value", 1),
+ __.has("value", 0).has("value", 1).or(not(has("age")), has("age", 10)).dedup(),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.dedup().filter(out()).has("value", 0),
+ has("value", 0).filter(out()).dedup(),
+ Collections.emptyList()},
+ {filter(out()).dedup().has("value", 0),
+ has("value", 0).filter(out()).dedup(),
+ Collections.emptyList()},
+ {__.as("a").out().has("age").where(P.eq("a")),
+ __.as("a").out().where(P.eq("a")).has("age"),
+ Collections.emptyList()},
+ {__.as("a").out().has("age").where(P.eq("a")).by("age"),
+ __.as("a").out().has("age").where(P.eq("a")).by("age"),
+ Collections.emptyList()},
+ {__.as("a").out().and(has("age"), has("name")).where(P.eq("a")).by("age"),
+ __.as("a").out().and(has("age"), has("name")).where(P.eq("a")).by("age"),
+ Collections.emptyList()},
+ {__.as("a").out().and(has("age"), has("name")).where(P.eq("a")),
+ __.as("a").out().where(P.eq("a")).and(has("age"), has("name")),
+ Collections.emptyList()},
+ {__.as("a").out().and(has("age"), has("name")).where(P.eq("a")).by("age"),
+ __.as("a").out().has("age").has("name").where(P.eq("a")).by("age"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.as("a").out().and(has("age"), has("name")).where(P.eq("a")),
+ __.as("a").out().where(P.eq("a")).has("age").has("name"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.as("a").out().and(has("age"), has("name")).filter(__.where(P.eq("a"))),
+ __.as("a").out().where(P.eq("a")).has("age").has("name"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {__.as("a").out().and(has("age"), has("name")).filter(__.where(P.eq("a")).by("age")),
+ __.as("a").out().has("age").has("name").where(P.eq("a")).by("age"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {has("value", 0).filter(out()).dedup(),
+ has("value", 0).filter(out()).dedup(),
+ Collections.emptyList()},
+ {has("value", 0).or(has("name"), has("age")).has("value", 1).dedup(),
+ has("value", 0).has("value", 1).or(has("name"), has("age")).dedup(),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {has("value", 0).or(out(), in()).as(Graph.Hidden.hide("x")).has("value", 1).dedup(),
+ has("value", 0).has("value", 1).or(outE(), inE()).dedup(),
+ TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {has("value", 0).and(has("age"), has("name", "marko")).is(10),
+ __.is(10).has("value", 0).has("age").has("name", "marko"),
+ Collections.singletonList(InlineFilterStrategy.instance())},
+ {has("value", 0).filter(or(not(has("age")), has("age", 1))).has("value", 1).dedup(),
+ has("value", 0).has("value", 1).or(not(filter(properties("age"))), has("age", 1)).dedup(),
+ TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ });
}
- this.original.asAdmin().setStrategies(strategies);
- this.original.asAdmin().applyStrategies();
- assertEquals(repr, this.optimized, this.original);
- }
- @Parameterized.Parameters(name = "{0}")
- public static Iterable<Object[]> generateTestParameters() {
- final Predicate testP = t -> true;
-
- return Arrays.asList(new Object[][]{
- {__.has("name", "marko").as("a").out().as("b").has("age", 32).where("a", neq("b")).as("c").out(),
- __.has("name", "marko").as("a").out().has("age", 32).as("b").where("a", neq("b")).as("c").out(),
- Collections.emptyList()},
-
- {__.V().as("n").where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person"))).select("n").by("name"),
- __.V().as("n").where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person"))).select("n").by("name"),
- Collections.emptyList()},
-
- {__.V().as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person")))))).select("n").by("name"),
- __.V().as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person")))))).select("n").by("name"),
- Collections.emptyList()},
-
- {__.V().as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person")))))).has("test", "val").select("n").by("name"),
- __.V().has("test", "val").as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel("software"),__.select("n").hasLabel("person")))))).select("n").by("name"),
- Collections.emptyList()},
-
- {__.V().as("n").out().where(__.or(__.hasLabel("out-dest-label1").select("n").hasLabel("software"),__.hasLabel("out-dest-label2").select("n").hasLabel("person"))).select("n").by("name"),
- __.V().as("n").out().where(__.or(__.hasLabel("out-dest-label1").select("n").hasLabel("software"),__.hasLabel("out-dest-label2").select("n").hasLabel("person"))).select("n").by("name"),
- Collections.emptyList()},
-
- // some lambda tests, where reordering over lambdas should not happen
- {__.order().map(Lambda.function("it.get().out()")).dedup(),
- __.order().map(Lambda.function("it.get().out()")).dedup(),
- Collections.emptyList()},
-
- {__.order().filter(__.map(Lambda.function("it.get().out()"))).dedup(),
- __.order().filter(__.map(Lambda.function("it.get().out()"))).dedup(),
- Collections.emptyList()},
-
- {__.dedup().order(),
- __.dedup().order(),
- Collections.emptyList()},
- {__.has("name", "marko").as("a").out().as("b").has("age", 32).where("a", neq("b")).as("c").out(),
- __.has("name", "marko").as("a").out().has("age", 32).as("b").where("a", neq("b")).as("c").out(),
- Collections.emptyList()},
- {__.has("name", "marko").as("a").out().has("age", 32).as("b").where("a", neq("b")),
- __.has("name", "marko").as("a").out().has("age", 32).as("b").where("a", neq("b")),
- Collections.emptyList()},
- {__.has("name", "marko").has("age", 32).dedup().has("name", "bob").as("a"),
- __.has("name", "marko").has("age", 32).has("name", "bob").dedup().as("a"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.has("name", "marko").dedup().as("a").has("age", 32).has("name", "bob").as("b"),
- __.has("name", "marko").has("age", 32).has("name", "bob").dedup().as("b", "a"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.where("b", eq("c")).as("a").dedup("a").has("name", "marko"),
- __.has("name", "marko").where("b", eq("c")).as("a").dedup("a"),
- Collections.emptyList()},
- {__.where("b", eq("c")).has("name", "bob").as("a").dedup("a").has("name", "marko"),
- __.has("name", "bob").has("name", "marko").where("b", eq("c")).as("a").dedup("a"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.has("name", "marko").as("a").out().has("name", "bob").dedup().as("b").where(__.as("a").out().as("b")),
- __.has("name", "marko").as("a").out().has("name", "bob").dedup().as("b").where(__.as("a").out().as("b")),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.has("name", "marko").as("a").out().has("name", "bob").as("b").dedup().where(__.as("a").out().as("b")),
- __.has("name", "marko").as("a").out().has("name", "bob").dedup().as("b").where(__.as("a").out().as("b")),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.has("name", "marko").as("a").out().has("name", "bob").dedup().as("c").where(__.as("a").out().as("b")),
- __.has("name", "marko").as("a").out().has("name", "bob").where(__.as("a").out().as("b")).dedup().as("c"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.order().dedup(),
- __.dedup().order(),
- Collections.emptyList()},
- {__.order().filter(testP).dedup(),
- __.order().filter(testP).dedup(),
- Collections.emptyList()},
- {__.order().as("a").dedup(),
- __.dedup().order().as("a"),
- Collections.emptyList()},
- {__.order().as("a").dedup("a"),
- __.order().as("a").dedup("a"),
- Collections.emptyList()},
- {__.order().as("a").dedup("a").has("name", "marko"),
- __.has("name", "marko").as("a").dedup("a").order(),
- Collections.emptyList()},
- {__.order().as("a").dedup("a").has("name", "marko").out(),
- __.has("name", "marko").as("a").dedup("a").order().out(),
- Collections.emptyList()},
- {__.order().as("a").dedup("a").has("name", "marko").where("a", eq("b")).out(),
- __.has("name", "marko").as("a").where("a", eq("b")).dedup("a").order().out(),
- Collections.emptyList()},
- {__.identity().order().dedup(),
- __.dedup().order(),
- Collections.singletonList(IdentityRemovalStrategy.instance())},
- {__.order().identity().dedup(),
- __.dedup().order(),
- Collections.singletonList(IdentityRemovalStrategy.instance())},
- {__.order().out().dedup(),
- __.order().out().dedup(),
- Collections.emptyList()},
- {has("value", 0).filter(out()).dedup(),
- has("value", 0).filter(out()).dedup(),
- Collections.emptyList()},
- {__.dedup().has("value", 0).or(not(has("age")), has("age", 10)).has("value", 1),
- __.has("value", 0).has("value", 1).or(not(has("age")), has("age", 10)).dedup(),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.dedup().filter(out()).has("value", 0),
- has("value", 0).filter(out()).dedup(),
- Collections.emptyList()},
- {filter(out()).dedup().has("value", 0),
- has("value", 0).filter(out()).dedup(),
- Collections.emptyList()},
- {__.as("a").out().has("age").where(P.eq("a")),
- __.as("a").out().where(P.eq("a")).has("age"),
- Collections.emptyList()},
- {__.as("a").out().has("age").where(P.eq("a")).by("age"),
- __.as("a").out().has("age").where(P.eq("a")).by("age"),
- Collections.emptyList()},
- {__.as("a").out().and(has("age"), has("name")).where(P.eq("a")).by("age"),
- __.as("a").out().and(has("age"), has("name")).where(P.eq("a")).by("age"),
- Collections.emptyList()},
- {__.as("a").out().and(has("age"), has("name")).where(P.eq("a")),
- __.as("a").out().where(P.eq("a")).and(has("age"), has("name")),
- Collections.emptyList()},
- {__.as("a").out().and(has("age"), has("name")).where(P.eq("a")).by("age"),
- __.as("a").out().has("age").has("name").where(P.eq("a")).by("age"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.as("a").out().and(has("age"), has("name")).where(P.eq("a")),
- __.as("a").out().where(P.eq("a")).has("age").has("name"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.as("a").out().and(has("age"), has("name")).filter(__.where(P.eq("a"))),
- __.as("a").out().where(P.eq("a")).has("age").has("name"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {__.as("a").out().and(has("age"), has("name")).filter(__.where(P.eq("a")).by("age")),
- __.as("a").out().has("age").has("name").where(P.eq("a")).by("age"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {has("value", 0).filter(out()).dedup(),
- has("value", 0).filter(out()).dedup(),
- Collections.emptyList()},
- {has("value", 0).or(has("name"), has("age")).has("value", 1).dedup(),
- has("value", 0).has("value", 1).or(has("name"), has("age")).dedup(),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {has("value", 0).or(out(), in()).as(Graph.Hidden.hide("x")).has("value", 1).dedup(),
- has("value", 0).has("value", 1).or(outE(), inE()).dedup(),
- TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {has("value", 0).and(has("age"), has("name", "marko")).is(10),
- __.is(10).has("value", 0).has("age").has("name", "marko"),
- Collections.singletonList(InlineFilterStrategy.instance())},
- {has("value", 0).filter(or(not(has("age")), has("age", 1))).has("value", 1).dedup(),
- has("value", 0).has("value", 1).or(not(filter(properties("age"))), has("age", 1)).dedup(),
- TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- });
+ @Test
+ public void doTest() {
+ final String repr = translator.translate(original.getBytecode()).getScript();
+ GValueManagerVerifier.verify(original.asAdmin(), FilterRankingStrategy.instance(), otherStrategies)
+ .afterApplying()
+ .managerIsEmpty();
+ assertEquals(repr, this.optimized, this.original);
+ }
}
-}
+ /**
+ * Tests that {@link GValueManager} properly preserves variables when using GValue instances
+ * in traversals processed by FilterRankingStrategy.
+ */
+ @RunWith(Parameterized.class)
+ public static class GValuePreservedTest {
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin<?, ?> traversal;
+ @Parameterized.Parameter(value = 1)
+ public Set<String> expectedVariables;
+
+ @Parameterized.Parameter(value = 2)
+ public Collection<TraversalStrategy> additionalStrategies;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ return Arrays.asList(new Object[][]{
+ // Examples without additional strategies
+ {__.has("name", GValue.of("x", "marko")).as("a").out().as("b").has("age", GValue.ofInteger("y", 32)).where("a", neq("b")).as("c").out(),
+ CollectionUtil.asSet("x", "y"),
+ Collections.emptySet()},
+ {__.V().as("n").where(__.or(__.select("n").hasLabel(GValue.of("x", "software")),__.select("n").hasLabel(GValue.of("y", "person")))).select("n").by("name"),
+ CollectionUtil.asSet("x", "y"),
+ Collections.emptyList()},
+ {__.V().as("n").where(__.where(__.where(__.where(__.or(__.select("n").hasLabel(GValue.of("x", "software")),__.select("n").hasLabel(GValue.of("y", "person"))))))).has("test", GValue.of("z", "val")).select("n").by("name"),
+ CollectionUtil.asSet("x", "y", "z"),
+ Collections.emptyList()},
+ {__.has("value", GValue.ofInteger("x", 0)).filter(out()).dedup(),
+ CollectionUtil.asSet("x"),
+ Collections.emptyList()},
+
+ // Examples with InlineFilterStrategy
+ {__.has("name", GValue.of("x", "marko")).has("age", GValue.ofInteger("y", 32)).dedup().has("name", GValue.of("z", "bob")).as("a"),
+ CollectionUtil.asSet("x", "y", "z"),
+ CollectionUtil.asSet(InlineFilterStrategy.instance())},
+ {__.dedup().has("value", GValue.ofInteger("x", 0)).or(not(has("age")), has("age", GValue.ofInteger("y", 10))).has("value", GValue.ofInteger("z", 1)),
+ CollectionUtil.asSet("x", "y", "z"),
+ CollectionUtil.asSet(InlineFilterStrategy.instance())},
+ {__.has("value", GValue.ofInteger("x", 0)).and(has("age"), has("name", GValue.of("y", "marko"))).is(GValue.ofInteger("z", 10)),
+ CollectionUtil.asSet("x", "y", "z"),
+ CollectionUtil.asSet(InlineFilterStrategy.instance())},
+ {__.has("name", GValue.of("x", "marko")).as("a").out().and(has("age"), has("name", GValue.of("y", "marko"))).filter(__.where(P.eq("a")).by("age")),
+ CollectionUtil.asSet("x", "y"),
+ CollectionUtil.asSet(InlineFilterStrategy.instance())},
+
+ // Examples with IdentityRemovalStrategy
+ {__.identity().order().dedup().has("name", GValue.of("x", "marko")),
+ CollectionUtil.asSet("x"),
+ CollectionUtil.asSet(IdentityRemovalStrategy.instance())},
+ {__.order().identity().dedup().has("value", GValue.ofInteger("x", 0)),
+ CollectionUtil.asSet("x"),
+ CollectionUtil.asSet(IdentityRemovalStrategy.instance())},
+
+ // Examples with TraversalStrategies.GlobalCache
+ {has("value", GValue.ofInteger("x", 0)).or(out(), in()).as(Graph.Hidden.hide("x")).has("value", GValue.ofInteger("y", 1)).dedup(),
+ CollectionUtil.asSet("x", "y"),
+ TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {has("value", GValue.ofInteger("x", 0)).filter(or(not(has("age")), has("age", GValue.ofInteger("y", 1)))).has("value", GValue.ofInteger("z", 1)).dedup(),
+ CollectionUtil.asSet("x", "y", "z"),
+ TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ });
+ }
+
+ @Test
+ public void shouldPreserveVariables() {
+ // Verify that variables are preserved after applying all strategies
+ GValueManagerVerifier.verify(traversal, FilterRankingStrategy.instance(), additionalStrategies).
+ beforeApplying().
+ afterApplying().
+ variablesArePreserved().
+ hasVariables(expectedVariables);
+ }
+ }
+}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IdentityRemovalStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IdentityRemovalStrategyTest.java
index c0ead528bb..d18d97e8c8 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IdentityRemovalStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IdentityRemovalStrategyTest.java
@@ -20,10 +20,9 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
-import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -72,15 +71,9 @@ public class IdentityRemovalStrategyTest {
@Test
public void doTest() {
final String repr = translator.translate(original.getBytecode()).getScript();
- applyIdentityRemovalStrategy(original);
+ GValueManagerVerifier.verify(original.asAdmin(), IdentityRemovalStrategy.instance())
+ .afterApplying()
+ .managerIsEmpty();
assertEquals(repr, optimized, original);
}
-
- private void applyIdentityRemovalStrategy(final Traversal traversal) {
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(IdentityRemovalStrategy.instance());
- traversal.asAdmin().setStrategies(strategies);
- traversal.asAdmin().applyStrategies();
-
- }
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java
index 5bc1a44965..3655dc3ecb 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategyTest.java
@@ -105,7 +105,7 @@ public class IncidentToAdjacentStrategyTest {
public static class GValueTest {
@Parameterized.Parameter(value = 0)
- public Traversal.Admin<?, ?> original;
+ public Traversal.Admin<?, ?> traversal;
@Parameterized.Parameter(value = 1)
public int expectedLabelCount;
@@ -160,12 +160,17 @@ public class IncidentToAdjacentStrategyTest {
@Test
public void shouldCopyGValueParameters() {
- GValueManagerVerifier.verify(original, IncidentToAdjacentStrategy.instance())
- .afterApplying()
- .hasVariables(expectedNames)
- .edgeLabelContractIsValid(original.getStartStep(), expectedLabelCount,
- expectedNames,
- expectedValues);
+ // get reference to the start step before we apply strategies as its going to get replaced and we need
+ // it to verify the before state.
+ final Step step = traversal.asAdmin().getSteps().get(0);
+ GValueManagerVerifier.verify(traversal, AdjacentToIncidentStrategy.instance()).
+ beforeApplying().
+ hasVariables(expectedNames).
+ edgeLabelContractIsValid(step, expectedLabelCount, expectedNames, expectedValues).
+ afterApplying().
+ hasVariables(expectedNames).
+ variablesArePreserved().
+ edgeLabelContractIsValid(traversal.getStartStep(), expectedLabelCount, expectedNames, expectedValues);
}
}
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategyTest.java
index 7596f49dc8..a43ae2d2ca 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategyTest.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -26,8 +27,10 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
@@ -35,6 +38,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.EmptyTraversal;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -65,105 +69,185 @@ import static org.junit.Assert.assertEquals;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-@RunWith(Parameterized.class)
+@RunWith(Enclosed.class)
public class InlineFilterStrategyTest {
private static final Translator.ScriptTranslator translator = GroovyTranslator.of("__");
- @Parameterized.Parameter(value = 0)
- public Traversal.Admin original;
+ @RunWith(Parameterized.class)
+ public static class StandardTest {
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin original;
- @Parameterized.Parameter(value = 1)
- public Traversal optimized;
+ @Parameterized.Parameter(value = 1)
+ public Traversal optimized;
- @Parameterized.Parameter(value = 2)
- public List<TraversalStrategy> extras;
+ @Parameterized.Parameter(value = 2)
+ public List<TraversalStrategy> extras;
- @Test
- public void doTest() {
- final String repr = translator.translate(original.getBytecode()).getScript();
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(InlineFilterStrategy.instance());
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
- extras.forEach(strategies::addStrategies);
+ return Arrays.asList(new Object[][]{
+ {has("age", 10).as("a").has("name", "marko").as("b").coin(0.5).as("c"), addHas(__.start(), "age", eq(10), "name", eq("marko")).as("a", "b").coin(0.5).as("c"), Collections.emptyList()},
+ //
+ {filter(out("knows")), filter(out("knows")), Collections.emptyList()},
+ {filter(has("age", gt(10))).as("a"), has("age", gt(10)).as("a"), Collections.emptyList()},
+ {filter(has("age", gt(10)).as("b")).as("a"), has("age", gt(10)).as("b", "a"), Collections.emptyList()},
+ {filter(has("age", gt(10)).as("a")), has("age", gt(10)).as("a"), Collections.emptyList()},
+ {filter(and(has("age", gt(10)).as("a"), has("name", "marko"))), addHas(__.start(), "age", gt(10), "name", eq("marko")).as("a"), Collections.emptyList()},
+ {filter(out("created").and().out("knows").or().in("knows")), or(and(out("created"), out("knows")), __.in("knows")), Collections.singletonList(ConnectiveStrategy.instance())},
+ {filter(out("created").and().out("knows").or().in("knows")).and(hasLabel("person")), or(and(out("created"), out("knows")), __.in("knows")).hasLabel("person"), Collections.singletonList(ConnectiveStrategy.instance())},
+ //
+ {or(has("name", "marko"), has("age", 32)), or(has("name", "marko"), has("age", 32)), Collections.emptyList()},
+ {or(has("name", "marko"), has("name", "bob")), has("name", eq("marko").or(eq("bob"))), Collections.emptyList()},
+ {or(has("name", "marko"), has("name")), or(has("name", "marko"), has("name")), Collections.emptyList()},
+ {or(has("age", 10), and(has("age", gt(20)), has("age", lt(100)))), has("age", eq(10).or(gt(20).and(lt(100)))), Collections.emptyList()},
+ {or(has("name", "marko"), filter(has("name", "bob"))), has("name", eq("marko").or(eq("bob"))), Collections.emptyList()},
+ {or(has("name", "marko"), filter(or(filter(has("name", "bob")), has("name", "stephen")))), has("name", eq("marko").or(eq("bob").or(eq("stephen")))), Collections.emptyList()},
+ {or(has("name", "marko").as("a"), filter(or(filter(has("name", "bob")).as("b"), has("name", "stephen").as("c")))), has("name", eq("marko").or(eq("bob").or(eq("stephen")))).as("a", "b", "c"), Collections.emptyList()},
+ {or(and(has("age",gt(20)), has("age",lt(30))), and(has("age",gt(35)), has("age",lt(40)))), has("age", gt(20).and(lt(30)).or(gt(35).and(lt(40)))), Collections.emptyList()},
+ //
+ {has("name", "marko").and().has("name", "marko").and().has("name", "marko"), has("name", "marko").has("name", "marko").has("name", "marko"), Collections.emptyList()},
+ {filter(has("name", "marko")).and().filter(has("name", "marko")).and().filter(has("name", "marko")), has("name", "marko").has("name", "marko").has("name", "marko"), Collections.emptyList()},
+ {V().has("name", "marko").and().has("name", "marko").and().has("name", "marko"), V().has("name", "marko").has("name", "marko").has("name", "marko"), Collections.emptyList()},
+ {V().filter(has("name", "marko")).and().filter(has("name", "marko")).and().filter(has("name", "marko")), V().has("name", "marko").has("name", "marko").has("name", "marko"), Collections.emptyList()},
+ {has("name", "marko").and().has("name", "marko").and().has("name", "marko"), has("name", "marko").has("name", "marko").has("name", "marko"), Collections.singletonList(ConnectiveStrategy.instance())},
+ {V().filter(has("name", "marko")).and().filter(has("name", "marko")).and().filter(has("name", "marko")), and(V().has("name", "marko"), has("name", "marko"), has("name", "marko")), Collections.singletonList(ConnectiveStrategy.instance())},
+ {V().has("name", "marko").and().has("name", "marko").and().has("name", "marko"), and(V().has("name", "marko"), has("name", "marko"), has("name", "marko")), Collections.singletonList(ConnectiveStrategy.instance())},
+ {EmptyGraph.instance().traversal().V().filter(has("name", "marko")).and().filter(has("name", "marko")).and().filter(has("name", "marko")), EmptyGraph.instance().traversal().V().has("name", "marko").has("name", "marko").has("name", "marko"), Collections.singletonList(ConnectiveStrategy.instance())},
+ {EmptyGraph.instance().traversal().V().has("name", "marko").and().has("name", "marko").and().has("name", "marko"), EmptyGraph.instance().traversal().V().has("name", "marko").has("name", "marko").has("name", "marko"), Collections.singletonList(ConnectiveStrategy.instance())},
+ //
+ {and(has("age", gt(10)), filter(has("age", 22))), addHas(__.start(), "age", gt(10), "age", eq(22)), Collections.emptyList()},
+ {and(has("age", gt(10)).as("a"), filter(has("age", 22).as("b")).as("c")).as("d"), addHas(__.start(), "age", gt(10), "age", eq(22)).as("a", "b", "c", "d"), Collections.emptyList()},
+ {and(has("age", gt(10)).as("a"), and(filter(has("age", 22).as("b")).as("c"), has("name", "marko").as("d"))), addHas(__.start(), "age", gt(10), "age", eq(22), "name", eq("marko")).as("a", "b", "c", "d"), Collections.emptyList()},
+ {and(has("age", gt(10)).as("a"), and(has("name", "stephen").as("b"), has("name", "marko").as("c")).as("d")).as("e"), addHas(__.start(), "age", gt(10), "name", eq("stephen"), "name", eq("marko")).as("a", "b", "c", "d", "e"), Collections.emptyList()},
+ {and(has("age", gt(10)), and(out("knows"), has("name", "marko"))), has("age", gt(10)).and(out("knows"), has("name", "marko")), Collections.emptyList()},
+ {and(has("age", gt(20)), or(has("age", lt(10)), has("age", gt(100)))), addHas(__.start(), "age", gt(20), "age", lt(10).or(gt(100))), Collections.emptyList()},
+ {and(has("age", gt(20)).as("a"), or(has("age", lt(10)), has("age", gt(100)).as("b"))), addHas(__.start(), "age", gt(20), "age", lt(10).or(gt(100))).as("a", "b"), Collections.emptyList()},
+ {and(has("age", gt(20)).as("a"), or(has("age", lt(10)).as("c"), has("age", gt(100)).as("b"))), addHas(__.start(), "age", gt(20), "age", lt(10).or(gt(100))).as("a", "b", "c"), Collections.emptyList()},
+ //
+ {V().match(as("a").has("age", 10), as("a").filter(has("name")).as("b")), V().has("age", 10).as("a").match(as("a").has("name").as("b")), Collections.emptyList()},
+ {match(as("a").has("age", 10), as("a").filter(has("name")).as("b")), match(as("a").has("age", 10), as("a").has("name").as("b")), Collections.emptyList()},
+ {match(as("a").has("age", 10).both().as("b"), as("b").out().as("c")), match(as("a").has("age", 10).both().as("b"), as("b").out().as("c")), Collections.emptyList()},
+ {__.map(match(as("a").has("age", 10), as("a").filter(has("name")).as("b"))), __.map(match(as("a").has("age", 10), as("a").has("name").as("b"))), Collections.emptyList()},
+ {V().match(as("a").has("age", 10)), V().has("age", 10).as("a"), Collections.emptyList()},
+ {V().match(as("a").has("age", 10).has("name", "marko").as("b")), V().has("age", 10).has("name", "marko").as("a", "b"), Collections.emptyList()},
+ {V().match(as("a").has("age", 10).has("name", "marko").as("b"), as("a").out("knows").as("c")), V().has("age", 10).has("name", "marko").as("a", "b").match(as("a").out("knows").as("c")), Collections.emptyList()},
+ {V().match(as("a").out("knows").as("c"), as("a").has("age", 10).has("name", "marko").as("b")), V().has("age", 10).has("name", "marko").as("a", "b").match(as("a").out("knows").as("c")), Collections.emptyList()},
+ {V().match(as("a").out("knows").as("c"), as("a").has("age", 10).has("name", "marko").as("b"), as("a").has("name", "bob")), V().has("age", 10).has("name", "marko").has("name", "bob").as("a", "b").match(as("a").out("knows").as("c")), Collections.emptyList()},
+ {V().match(as("a").has("age", 10).as("b"), as("a").filter(has("name")).as("b")), V().has("age", 10).as("a", "b").match(as("a").has("name").as("b")), Collections.emptyList()},
+ //
+ {filter(dedup()), filter(dedup()), Collections.emptyList()},
+ {filter(filter(drop())), filter(drop()), Collections.emptyList()},
+ {and(has("name"), limit(10).has("age")), and(has("name"), limit(10).has("age")), Collections.emptyList()},
+ {filter(tail(10).as("a")), filter(tail(10).as("a")), Collections.emptyList()},
+ //
+ {outE().hasLabel("knows").inV(), outE("knows").inV(), Collections.emptyList()},
+ {outE().hasLabel("knows").hasLabel("created").inV(), outE("knows").hasLabel("created").inV(), Collections.emptyList()},
+ {outE().or(hasLabel("knows"), hasLabel("created")).inV(), outE("knows", "created").inV(), Collections.emptyList()},
+ {outE().or(hasLabel("knows").as("a"), hasLabel("created").as("b")).as("c").inV(), outE("knows", "created").as("a", "b", "c").inV(), Collections.emptyList()},
+ {outE().hasLabel(P.eq("knows").or(P.gt("created"))).has("weight", gt(1.0)).inV(), addHas(outE(), T.label.getAccessor(), P.eq("knows").or(P.gt("created")), "weight", gt(1.0)).inV(), Collections.emptyList()},
+ {outE().hasLabel(P.eq("knows").or(P.eq("created"))).has("weight", gt(1.0)).inV(), outE("knows", "created").has("weight", gt(1.0)).inV(), Collections.emptyList()},
+ {outE().hasLabel(P.within("knows", "created")).inV(), outE("knows", "created").inV(), Collections.emptyList()},
+ });
+ }
- this.original.asAdmin().setStrategies(strategies);
- this.original.asAdmin().applyStrategies();
- assertEquals(repr, this.optimized, this.original);
- }
+ @Test
+ public void doTest() {
+ final String repr = translator.translate(original.getBytecode()).getScript();
+
+ GValueManagerVerifier.verify(original.asAdmin(), InlineFilterStrategy.instance(), extras)
+ .afterApplying()
+ .managerIsEmpty();
- @Parameterized.Parameters(name = "{0}")
- public static Iterable<Object[]> generateTestParameters() {
-
- return Arrays.asList(new Object[][]{
- {has("age", 10).as("a").has("name", "marko").as("b").coin(0.5).as("c"), addHas(__.start(), "age", eq(10), "name", eq("marko")).as("a", "b").coin(0.5).as("c"), Collections.emptyList()},
- //
- {filter(out("knows")), filter(out("knows")), Collections.emptyList()},
- {filter(has("age", gt(10))).as("a"), has("age", gt(10)).as("a"), Collections.emptyList()},
- {filter(has("age", gt(10)).as("b")).as("a"), has("age", gt(10)).as("b", "a"), Collections.emptyList()},
- {filter(has("age", gt(10)).as("a")), has("age", gt(10)).as("a"), Collections.emptyList()},
- {filter(and(has("age", gt(10)).as("a"), has("name", "marko"))), addHas(__.start(), "age", gt(10), "name", eq("marko")).as("a"), Collections.emptyList()},
- {filter(out("created").and().out("knows").or().in("knows")), or(and(out("created"), out("knows")), __.in("knows")), Collections.singletonList(ConnectiveStrategy.instance())},
- {filter(out("created").and().out("knows").or().in("knows")).and(hasLabel("person")), or(and(out("created"), out("knows")), __.in("knows")).hasLabel("person"), Collections.singletonList(ConnectiveStrategy.instance())},
- //
- {or(has("name", "marko"), has("age", 32)), or(has("name", "marko"), has("age", 32)), Collections.emptyList()},
- {or(has("name", "marko"), has("name", "bob")), has("name", eq("marko").or(eq("bob"))), Collections.emptyList()},
- {or(has("name", "marko"), has("name")), or(has("name", "marko"), has("name")), Collections.emptyList()},
- {or(has("age", 10), and(has("age", gt(20)), has("age", lt(100)))), has("age", eq(10).or(gt(20).and(lt(100)))), Collections.emptyList()},
- {or(has("name", "marko"), filter(has("name", "bob"))), has("name", eq("marko").or(eq("bob"))), Collections.emptyList()},
- {or(has("name", "marko"), filter(or(filter(has("name", "bob")), has("name", "stephen")))), has("name", eq("marko").or(eq("bob").or(eq("stephen")))), Collections.emptyList()},
- {or(has("name", "marko").as("a"), filter(or(filter(has("name", "bob")).as("b"), has("name", "stephen").as("c")))), has("name", eq("marko").or(eq("bob").or(eq("stephen")))).as("a", "b", "c"), Collections.emptyList()},
- {or(and(has("age",gt(20)), has("age",lt(30))), and(has("age",gt(35)), has("age",lt(40)))), has("age", gt(20).and(lt(30)).or(gt(35).and(lt(40)))), Collections.emptyList()},
- //
- {has("name", "marko").and().has("name", "marko").and().has("name", "marko"), has("name", "marko").has("name", "marko").has("name", "marko"), Collections.emptyList()},
- {filter(has("name", "marko")).and().filter(has("name", "marko")).and().filter(has("name", "marko")), has("name", "marko").has("name", "marko").has("name", "marko"), Collections.emptyList()},
- {V().has("name", "marko").and().has("name", "marko").and().has("name", "marko"), V().has("name", "marko").has("name", "marko").has("name", "marko"), Collections.emptyList()},
- {V().filter(has("name", "marko")).and().filter(has("name", "marko")).and().filter(has("name", "marko")), V().has("name", "marko").has("name", "marko").has("name", "marko"), Collections.emptyList()},
- {has("name", "marko").and().has("name", "marko").and().has("name", "marko"), has("name", "marko").has("name", "marko").has("name", "marko"), Collections.singletonList(ConnectiveStrategy.instance())},
- {V().filter(has("name", "marko")).and().filter(has("name", "marko")).and().filter(has("name", "marko")), and(V().has("name", "marko"), has("name", "marko"), has("name", "marko")), Collections.singletonList(ConnectiveStrategy.instance())},
- {V().has("name", "marko").and().has("name", "marko").and().has("name", "marko"), and(V().has("name", "marko"), has("name", "marko"), has("name", "marko")), Collections.singletonList(ConnectiveStrategy.instance())},
- {EmptyGraph.instance().traversal().V().filter(has("name", "marko")).and().filter(has("name", "marko")).and().filter(has("name", "marko")), EmptyGraph.instance().traversal().V().has("name", "marko").has("name", "marko").has("name", "marko"), Collections.singletonList(ConnectiveStrategy.instance())},
- {EmptyGraph.instance().traversal().V().has("name", "marko").and().has("name", "marko").and().has("name", "marko"), EmptyGraph.instance().traversal().V().has("name", "marko").has("name", "marko").has("name", "marko"), Collections.singletonList(ConnectiveStrategy.instance())},
- //
- {and(has("age", gt(10)), filter(has("age", 22))), addHas(__.start(), "age", gt(10), "age", eq(22)), Collections.emptyList()},
- {and(has("age", gt(10)).as("a"), filter(has("age", 22).as("b")).as("c")).as("d"), addHas(__.start(), "age", gt(10), "age", eq(22)).as("a", "b", "c", "d"), Collections.emptyList()},
- {and(has("age", gt(10)).as("a"), and(filter(has("age", 22).as("b")).as("c"), has("name", "marko").as("d"))), addHas(__.start(), "age", gt(10), "age", eq(22), "name", eq("marko")).as("a", "b", "c", "d"), Collections.emptyList()},
- {and(has("age", gt(10)).as("a"), and(has("name", "stephen").as("b"), has("name", "marko").as("c")).as("d")).as("e"), addHas(__.start(), "age", gt(10), "name", eq("stephen"), "name", eq("marko")).as("a", "b", "c", "d", "e"), Collections.emptyList()},
- {and(has("age", gt(10)), and(out("knows"), has("name", "marko"))), has("age", gt(10)).and(out("knows"), has("name", "marko")), Collections.emptyList()},
- {and(has("age", gt(20)), or(has("age", lt(10)), has("age", gt(100)))), addHas(__.start(), "age", gt(20), "age", lt(10).or(gt(100))), Collections.emptyList()},
- {and(has("age", gt(20)).as("a"), or(has("age", lt(10)), has("age", gt(100)).as("b"))), addHas(__.start(), "age", gt(20), "age", lt(10).or(gt(100))).as("a", "b"), Collections.emptyList()},
- {and(has("age", gt(20)).as("a"), or(has("age", lt(10)).as("c"), has("age", gt(100)).as("b"))), addHas(__.start(), "age", gt(20), "age", lt(10).or(gt(100))).as("a", "b", "c"), Collections.emptyList()},
- //
- {V().match(as("a").has("age", 10), as("a").filter(has("name")).as("b")), V().has("age", 10).as("a").match(as("a").has("name").as("b")), Collections.emptyList()},
- {match(as("a").has("age", 10), as("a").filter(has("name")).as("b")), match(as("a").has("age", 10), as("a").has("name").as("b")), Collections.emptyList()},
- {match(as("a").has("age", 10).both().as("b"), as("b").out().as("c")), match(as("a").has("age", 10).both().as("b"), as("b").out().as("c")), Collections.emptyList()},
- {__.map(match(as("a").has("age", 10), as("a").filter(has("name")).as("b"))), __.map(match(as("a").has("age", 10), as("a").has("name").as("b"))), Collections.emptyList()},
- {V().match(as("a").has("age", 10)), V().has("age", 10).as("a"), Collections.emptyList()},
- {V().match(as("a").has("age", 10).has("name", "marko").as("b")), V().has("age", 10).has("name", "marko").as("a", "b"), Collections.emptyList()},
- {V().match(as("a").has("age", 10).has("name", "marko").as("b"), as("a").out("knows").as("c")), V().has("age", 10).has("name", "marko").as("a", "b").match(as("a").out("knows").as("c")), Collections.emptyList()},
- {V().match(as("a").out("knows").as("c"), as("a").has("age", 10).has("name", "marko").as("b")), V().has("age", 10).has("name", "marko").as("a", "b").match(as("a").out("knows").as("c")), Collections.emptyList()},
- {V().match(as("a").out("knows").as("c"), as("a").has("age", 10).has("name", "marko").as("b"), as("a").has("name", "bob")), V().has("age", 10).has("name", "marko").has("name", "bob").as("a", "b").match(as("a").out("knows").as("c")), Collections.emptyList()},
- {V().match(as("a").has("age", 10).as("b"), as("a").filter(has("name")).as("b")), V().has("age", 10).as("a", "b").match(as("a").has("name").as("b")), Collections.emptyList()},
- //
- {filter(dedup()), filter(dedup()), Collections.emptyList()},
- {filter(filter(drop())), filter(drop()), Collections.emptyList()},
- {and(has("name"), limit(10).has("age")), and(has("name"), limit(10).has("age")), Collections.emptyList()},
- {filter(tail(10).as("a")), filter(tail(10).as("a")), Collections.emptyList()},
- //
- {outE().hasLabel("knows").inV(), outE("knows").inV(), Collections.emptyList()},
- {outE().hasLabel("knows").hasLabel("created").inV(), outE("knows").hasLabel("created").inV(), Collections.emptyList()},
- {outE().or(hasLabel("knows"), hasLabel("created")).inV(), outE("knows", "created").inV(), Collections.emptyList()},
- {outE().or(hasLabel("knows").as("a"), hasLabel("created").as("b")).as("c").inV(), outE("knows", "created").as("a", "b", "c").inV(), Collections.emptyList()},
- {outE().hasLabel(P.eq("knows").or(P.gt("created"))).has("weight", gt(1.0)).inV(), addHas(outE(), T.label.getAccessor(), P.eq("knows").or(P.gt("created")), "weight", gt(1.0)).inV(), Collections.emptyList()},
- {outE().hasLabel(P.eq("knows").or(P.eq("created"))).has("weight", gt(1.0)).inV(), outE("knows", "created").has("weight", gt(1.0)).inV(), Collections.emptyList()},
- {outE().hasLabel(P.within("knows", "created")).inV(), outE("knows", "created").inV(), Collections.emptyList()},
- });
+ assertEquals(repr, this.optimized, this.original);
+ }
+
+ private static GraphTraversal.Admin<?, ?> addHas(final GraphTraversal<?, ?> traversal, final Object... hasKeyValues) {
+ final HasStep<?> hasStep = new HasStep<>((Traversal.Admin) traversal);
+ for (int i = 0; i < hasKeyValues.length; i = i + 2) {
+ hasStep.addHasContainer(new HasContainer((String) hasKeyValues[i], (P) hasKeyValues[i + 1]));
+ }
+ return traversal.asAdmin().addStep(hasStep);
+ }
}
- private static GraphTraversal.Admin<?, ?> addHas(final GraphTraversal<?, ?> traversal, final Object... hasKeyValues) {
- final HasStep<?> hasStep = new HasStep<>((Traversal.Admin) traversal);
- for (int i = 0; i < hasKeyValues.length; i = i + 2) {
- hasStep.addHasContainer(new HasContainer((String) hasKeyValues[i], (P) hasKeyValues[i + 1]));
+ /**
+ * Tests that {@link GValueManager} is properly maintaining state when using InlineFilterStrategy.
+ */
+ @RunWith(Parameterized.class)
+ public static class GValueManagerVerifierTest {
+
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin<?, ?> traversal;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ return Arrays.asList(new Object[][]{
+ // Test filter with has step using GValue
+ { filter(has("age", GValue.of("x", 10))).asAdmin() },
+ { filter(has("age", gt(GValue.ofInteger("x", 20)))).asAdmin() },
+ { filter(has("age", lt(GValue.ofInteger("x", 30)))).asAdmin() },
+
+ // Test or conditions with GValue
+ { or(has("name", GValue.of("x", "marko")), has("age", GValue.of("y", 32))).asAdmin() },
+ { or(has("name", GValue.of("x", "marko")), has("name", GValue.of("y", "bob"))).asAdmin() },
+
+ // Test and conditions with GValue
+ { and(has("age", gt(GValue.ofInteger("x", 10))), filter(has("age", GValue.of("y", 22)))).asAdmin() },
+ { and(has("age", gt(GValue.ofInteger("x", 10))).as("a"), filter(has("age", GValue.of("y", 22)).as("b"))).asAdmin() },
+
+ // Test complex conditions with GValue
+ { or(and(has("age", gt(GValue.ofInteger("x", 20))), has("age", lt(GValue.ofInteger("y", 30)))),
+ and(has("age", gt(GValue.ofInteger("z", 35))), has("age", lt(GValue.ofInteger("w", 40))))).asAdmin() },
+
+ // Test edge label with GValue
+ { outE().hasLabel(GValue.of("x", "knows")).inV().asAdmin() },
+ { outE().hasLabel(GValue.of("x", "knows"), GValue.of("y", "created")).inV().asAdmin() },
+
+ // Additional test cases for expanded coverage
+
+ // Test with P.within predicate using GValue
+ { filter(has("age", P.within(GValue.ofInteger("x", 20), GValue.ofInteger("y", 30), GValue.ofInteger("z", 40)))).asAdmin() },
+
+ // Test with P.inside predicate using GValue
+ { filter(has("age", inside(GValue.ofInteger("x", 20), GValue.ofInteger("y", 30)))).asAdmin() },
+
+ // Test with multiple has steps in filter
+ { filter(has("age", GValue.of("x", 25)).has("name", GValue.of("y", "marko"))).asAdmin() },
+
+ // Test with nested filters
+ { filter(filter(has("age", GValue.of("x", 25)))).asAdmin() },
+
+ // Test with more complex nested structure
+ { filter(or(has("name", GValue.of("x", "marko")),
+ filter(and(has("age", gt(GValue.ofInteger("y", 30))),
+ has("age", lt(GValue.ofInteger("z", 40))))))).asAdmin() },
+
+ // Test with different GValue types
+ { filter(has("weight", GValue.ofDouble("x", 0.5))).asAdmin() },
+ { filter(has("active", GValue.ofBoolean("x", true))).asAdmin() },
+
+ // Test with multiple variables in a single predicate
+ { filter(has("age", inside(GValue.ofInteger("min", 20), GValue.ofInteger("max", 30)))).asAdmin() },
+
+ // Test with hasLabel and multiple GValues
+ { filter(hasLabel(GValue.of("x", "person"))).asAdmin() },
+ { filter(hasLabel(GValue.of("x", "person"), GValue.of("y", "software"))).asAdmin() },
+
+ // Test with traversal filter step containing GValue
+ { filter(__.filter(has("age", GValue.of("x", 25)))).asAdmin() },
+ });
+ }
+
+ @Test
+ public void shouldMaintainGValueManagerState() {
+ // Use GValueManagerVerifier to verify the state before and after applying the strategy
+ GValueManagerVerifier.verify(traversal, InlineFilterStrategy.instance()).
+ beforeApplying().
+ stepsOfClassAreParameterized(true, HasStep.class).
+ afterApplying().
+ variablesArePreserved();
}
- return traversal.asAdmin().addStep(hasStep);
}
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/OrderLimitStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/OrderLimitStrategyTest.java
index 6fb8d31d5e..f0a975b173 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/OrderLimitStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/OrderLimitStrategyTest.java
@@ -23,54 +23,104 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.Traversa
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.OrderGlobalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.util.EmptyTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import static org.junit.Assert.assertEquals;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-@RunWith(Parameterized.class)
+@RunWith(Enclosed.class)
public class OrderLimitStrategyTest {
private static final Translator.ScriptTranslator translator = GroovyTranslator.of("__");
- @Parameterized.Parameter(value = 0)
- public Traversal.Admin traversal;
+ @RunWith(Parameterized.class)
+ public static class StandardTest {
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin traversal;
- @Parameterized.Parameter(value = 1)
- public long limit;
+ @Parameterized.Parameter(value = 1)
+ public long limit;
- void applyOrderLimitStrategyStrategy(final Traversal traversal) {
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(OrderLimitStrategy.instance());
- traversal.asAdmin().setStrategies(strategies);
- traversal.asAdmin().applyStrategies();
- }
- @Test
- public void doTest() {
- final String repr = translator.translate(traversal.getBytecode()).getScript();
- traversal.asAdmin().setParent(new TraversalVertexProgramStep(EmptyTraversal.instance(), EmptyTraversal.instance())); // trick it
- applyOrderLimitStrategyStrategy(traversal);
- assertEquals(repr, limit, TraversalHelper.getFirstStepOfAssignableClass(OrderGlobalStep.class, traversal.asAdmin()).get().getLimit());
+ @Test
+ public void doTest() {
+ final String repr = translator.translate(traversal.getBytecode()).getScript();
+ traversal.asAdmin().setParent(new TraversalVertexProgramStep(EmptyTraversal.instance(), EmptyTraversal.instance())); // trick it
+ GValueManagerVerifier.verify(traversal, OrderLimitStrategy.instance()).
+ afterApplying().variablesArePreserved();
+ assertEquals(repr, limit, TraversalHelper.getFirstStepOfAssignableClass(OrderGlobalStep.class, traversal.asAdmin()).get().getLimit());
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ return Arrays.asList(new Object[][]{
+ {__.order().limit(1), 1l},
+ {__.out().order().range(7, 15), 15l},
+ {__.order().select("a").limit(7), 7l},
+ {__.order().out().limit(10), Long.MAX_VALUE}});
+ }
}
- @Parameterized.Parameters(name = "{0}")
- public static Iterable<Object[]> generateTestParameters() {
- return Arrays.asList(new Object[][]{
- {__.order().limit(1), 1l},
- {__.out().order().range(7, 15), 15l},
- {__.order().select("a").limit(7), 7l},
- {__.order().out().limit(10), Long.MAX_VALUE}});
+ /**
+ * Tests that GValueManager correctly preserves GValue parameters when OrderLimitStrategy is applied.
+ */
+ @RunWith(Parameterized.class)
+ public static class GValueTest {
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin<?, ?> traversal;
+
+ @Parameterized.Parameter(value = 1)
+ public String[] expectedNames;
+
+ @Parameterized.Parameter(value = 2)
+ public Collection<TraversalStrategy> additionalStrategies;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ return Arrays.asList(new Object[][]{
+ {
+ __.order().limit(GValue.ofLong("x", 5L)).asAdmin(),
+ new String[]{"x"},
+ Collections.emptyList(),
+ },
+ {
+ __.out().order().range(GValue.ofLong("low", 3L), GValue.ofLong("high", 10L)).asAdmin(),
+ new String[]{"low", "high"},
+ Collections.emptyList(),
+ },
+ {
+ __.order().select("a").limit(GValue.ofLong("x", 7L)).asAdmin(),
+ new String[]{"x"},
+ Collections.emptyList(),
+ }
+ });
+ }
+
+ @Test
+ public void shouldPreserveGValueParameters() {
+ traversal.asAdmin().setParent(new TraversalVertexProgramStep(EmptyTraversal.instance(), EmptyTraversal.instance())); // trick it
+ GValueManagerVerifier.verify(traversal, OrderLimitStrategy.instance()).
+ beforeApplying().
+ hasVariables(expectedNames).
+ afterApplying().
+ managerIsEmpty();
+ }
}
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategyTest.java
index 47cd52f313..6318ea2d2c 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategyTest.java
@@ -20,6 +20,7 @@
package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.TraversalVertexProgramStep;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
@@ -31,11 +32,14 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.ValueTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -46,81 +50,168 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.coalesce;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-@RunWith(Parameterized.class)
+@RunWith(Enclosed.class)
public class PathProcessorStrategyTest {
private static final Translator.ScriptTranslator translator = GroovyTranslator.of("__");
private static final Collection<TraversalStrategy> withProductiveByStrategy =
Stream.concat(Stream.of(ProductiveByStrategy.instance()),
TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList().stream()).collect(Collectors.toList());
- @Parameterized.Parameter(value = 0)
- public Traversal.Admin original;
+ @RunWith(Parameterized.class)
+ public static class StandardTest {
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin original;
- @Parameterized.Parameter(value = 1)
- public Traversal optimized;
+ @Parameterized.Parameter(value = 1)
+ public Traversal optimized;
- @Parameterized.Parameter(value = 2)
- public Collection<TraversalStrategy> otherStrategies;
+ @Parameterized.Parameter(value = 2)
+ public Collection<TraversalStrategy> otherStrategies;
- @Test
- public void doTest() {
- final String repr = translator.translate(original.getBytecode()).getScript();
- final Traversal.Admin<?, ?> rootTraversal = new DefaultGraphTraversal<>();
- final TraversalParent parent = new TraversalVertexProgramStep(rootTraversal, this.original.asAdmin());
- rootTraversal.addStep(parent.asStep());
- this.original.asAdmin().setParent(parent); // trick it into OLAP
- final TraversalStrategies strategies = new DefaultTraversalStrategies();
- strategies.addStrategies(PathProcessorStrategy.instance());
- for (final TraversalStrategy strategy : this.otherStrategies) {
- strategies.addStrategies(strategy);
+ @Test
+ public void doTest() {
+ final String repr = translator.translate(original.getBytecode()).getScript();
+ final Traversal.Admin<?, ?> rootTraversal = new DefaultGraphTraversal<>();
+ final TraversalParent parent = new TraversalVertexProgramStep(rootTraversal, this.original.asAdmin());
+ rootTraversal.addStep(parent.asStep());
+ this.original.asAdmin().setParent(parent); // trick it into OLAP
+ final TraversalStrategies strategies = new DefaultTraversalStrategies();
+ strategies.addStrategies(PathProcessorStrategy.instance());
+ for (final TraversalStrategy strategy : this.otherStrategies) {
+ strategies.addStrategies(strategy);
+ }
+ this.original.asAdmin().setStrategies(strategies);
+ this.original.asAdmin().applyStrategies();
+ assertEquals(repr, this.optimized, this.original);
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ return Arrays.asList(new Object[][]{
+ // select("a")
+ {__.select("a"), __.select("a"), Collections.emptyList()},
+ {__.select("a").by(), __.select("a").by(), Collections.emptyList()},
+ {__.select("a").by(__.outE().count()), __.select("a").map(__.outE().count()), Collections.emptyList()},
+ {__.select("a").by("name"), __.select("a").map(new ValueTraversal<>("name")), Collections.emptyList()},
+ {__.select("a").out(), __.select("a").out(), Collections.emptyList()},
+ {__.select(Pop.all, "a").by(__.values("name")), __.select(Pop.all, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
+ {__.select(Pop.last, "a").by(__.values("name")), __.select(Pop.last, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
+ {__.select(Pop.first, "a").by(__.values("name")), __.select(Pop.first, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
+ {__.select(Pop.all, "a").by(__.values("name")), __.select(Pop.all, "a").by("name"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.select(Pop.last, "a").by(__.values("name")), __.select(Pop.last, "a").map(__.values("name")), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.select(Pop.first, "a").by(__.values("name")), __.select(Pop.first, "a").map(__.values("name")), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ // select("a","b")
+ {__.select("a", "b"), __.select("a", "b"), Collections.emptyList()},
+ {__.select("a", "b").by(), __.select("a", "b").by(), Collections.emptyList()},
+ {__.select("a", "b", "c").by(), __.select("a", "b", "c").by(), Collections.emptyList()},
+ {__.select("a", "b").by().by("age"), __.select("b").map(new ValueTraversal<>("age")).as("b").select("a").map(new IdentityTraversal<>()).as("a").select(Pop.last, "a", "b"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.select("a", "b").by("name").by("age"), __.select("b").map(new ValueTraversal<>("age")).as("b").select("a").map(new ValueTraversal<>("name")).as("a").select(Pop.last, "a", "b"), Collections.emptyList()},
+ {__.select("a", "b", "c").by("name").by(__.outE().count()), __.select("c").map(new ValueTraversal<>("name")).as("c").select("b").map(__.outE().count()).as("b").select("a").map(new ValueTraversal<>("name")).as("a").select(Pop.last, "a", "b", "c"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.select(Pop.first, "a", "b").by("name").by("age"), __.select(Pop.first, "b").map(new ValueTraversal<>("age")).as("b").select(Pop.first, "a").map(new ValueTraversal<>("name")).as("a").select(Pop.last, "a", "b"), Collections.emptyList()},
+ {__.select(Pop.last, "a", "b").by("name").by("age"), __.select(Pop.last, "b").map(new ValueTraversal<>("age")).as("b").select(Pop.last, "a").map(new ValueTraversal<>("name")).as("a").select(Pop.last, "a", "b"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.select(Pop.all, "a", "b").by("name").by("age"), __.select(Pop.all, "a", "b").by("name").by("age"), Collections.emptyList()},
+ {__.select(Pop.mixed, "a", "b").by("name").by("age"), __.select(Pop.mixed, "a", "b").by("name").by("age"), Collections.emptyList()},
+ // where(as("a")...)
+ {__.where(__.out("knows")), __.where(__.outE("knows")), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.where(__.as("a").out("knows")), __.identity().as("xyz").select(Pop.last, "a").filter(__.out("knows")).select(Pop.last, "xyz"), Collections.emptyList()},
+ {__.where(__.as("a").has("age", P.gt(10))), __.identity().as("xyz").select(Pop.last, "a").has("age", P.gt(10)).select(Pop.last, "xyz"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.select("b").where(__.as("a").has("age", P.gt(10))), __.select("b").as("xyz").select(Pop.last, "a").has("age", P.gt(10)).select(Pop.last, "xyz"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.where(__.as("a").out("knows").as("b")), __.identity().as("xyz").select(Pop.last, "a").where(__.out("knows").as("b")).select(Pop.last, "xyz"), Collections.emptyList()},
+ {__.where("a", P.eq("b")), __.where("a", P.eq("b")), Collections.emptyList()},
+ {__.as("a").out().where(__.as("a").has("age", P.gt(10))).in(), __.as("a").out().as("xyz").select(Pop.last, "a").has("age", P.gt(10)).select(Pop.last, "xyz").in(), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.as("a").out().where(__.as("a").has("age", P.gt(10))).in().path(), __.as("a").out().where(__.as("a").has("age", P.gt(10))).in().path(), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+ {__.select("a", "b").where(__.as("c").out("knows")), __.identity().as("xyz").select(Pop.last, "c").where(__.out("knows")).select("xyz").select(Pop.last, "a", "b"), Collections.emptyList()},
+ });
}
- this.original.asAdmin().setStrategies(strategies);
- this.original.asAdmin().applyStrategies();
- assertEquals(repr, this.optimized, this.original);
}
+ /**
+ * Tests that GValueManager.copyParams is being called correctly in PathProcessorStrategy
+ * such that the newly added steps have the same parameters that previous ones had.
+ */
+ @RunWith(Parameterized.class)
+ public static class GValueTest {
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin<?, ?> traversal;
+
+ @Parameterized.Parameter(value = 1)
+ public String[] expectedNames;
- @Parameterized.Parameters(name = "{0}")
- public static Iterable<Object[]> generateTestParameters() {
+ @Parameterized.Parameter(value = 2)
+ public Collection<TraversalStrategy> additionalStrategies;
- return Arrays.asList(new Object[][]{
- // select("a")
- {__.select("a"), __.select("a"), Collections.emptyList()},
- {__.select("a").by(), __.select("a").by(), Collections.emptyList()},
- {__.select("a").by(__.outE().count()), __.select("a").map(__.outE().count()), Collections.emptyList()},
- {__.select("a").by("name"), __.select("a").map(new ValueTraversal<>("name")), Collections.emptyList()},
- {__.select("a").out(), __.select("a").out(), Collections.emptyList()},
- {__.select(Pop.all, "a").by(__.values("name")), __.select(Pop.all, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
- {__.select(Pop.last, "a").by(__.values("name")), __.select(Pop.last, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
- {__.select(Pop.first, "a").by(__.values("name")), __.select(Pop.first, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
- {__.select(Pop.all, "a").by(__.values("name")), __.select(Pop.all, "a").by("name"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.select(Pop.last, "a").by(__.values("name")), __.select(Pop.last, "a").map(__.values("name")), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.select(Pop.first, "a").by(__.values("name")), __.select(Pop.first, "a").map(__.values("name")), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- // select("a","b")
- {__.select("a", "b"), __.select("a", "b"), Collections.emptyList()},
- {__.select("a", "b").by(), __.select("a", "b").by(), Collections.emptyList()},
- {__.select("a", "b", "c").by(), __.select("a", "b", "c").by(), Collections.emptyList()},
- {__.select("a", "b").by().by("age"), __.select("b").map(new ValueTraversal<>("age")).as("b").select("a").map(new IdentityTraversal<>()).as("a").select(Pop.last, "a", "b"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.select("a", "b").by("name").by("age"), __.select("b").map(new ValueTraversal<>("age")).as("b").select("a").map(new ValueTraversal<>("name")).as("a").select(Pop.last, "a", "b"), Collections.emptyList()},
- {__.select("a", "b", "c").by("name").by(__.outE().count()), __.select("c").map(new ValueTraversal<>("name")).as("c").select("b").map(__.outE().count()).as("b").select("a").map(new ValueTraversal<>("name")).as("a").select(Pop.last, "a", "b", "c"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.select(Pop.first, "a", "b").by("name").by("age"), __.select(Pop.first, "b").map(new ValueTraversal<>("age")).as("b").select(Pop.first, "a").map(new ValueTraversal<>("name")).as("a").select(Pop.last, "a", "b"), Collections.emptyList()},
- {__.select(Pop.last, "a", "b").by("name").by("age"), __.select(Pop.last, "b").map(new ValueTraversal<>("age")).as("b").select(Pop.last, "a").map(new ValueTraversal<>("name")).as("a").select(Pop.last, "a", "b"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.select(Pop.all, "a", "b").by("name").by("age"), __.select(Pop.all, "a", "b").by("name").by("age"), Collections.emptyList()},
- {__.select(Pop.mixed, "a", "b").by("name").by("age"), __.select(Pop.mixed, "a", "b").by("name").by("age"), Collections.emptyList()},
- // where(as("a")...)
- {__.where(__.out("knows")), __.where(__.outE("knows")), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.where(__.as("a").out("knows")), __.identity().as("xyz").select(Pop.last, "a").filter(__.out("knows")).select(Pop.last, "xyz"), Collections.emptyList()},
- {__.where(__.as("a").has("age", P.gt(10))), __.identity().as("xyz").select(Pop.last, "a").has("age", P.gt(10)).select(Pop.last, "xyz"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.select("b").where(__.as("a").has("age", P.gt(10))), __.select("b").as("xyz").select(Pop.last, "a").has("age", P.gt(10)).select(Pop.last, "xyz"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.where(__.as("a").out("knows").as("b")), __.identity().as("xyz").select(Pop.last, "a").where(__.out("knows").as("b")).select(Pop.last, "xyz"), Collections.emptyList()},
- {__.where("a", P.eq("b")), __.where("a", P.eq("b")), Collections.emptyList()},
- {__.as("a").out().where(__.as("a").has("age", P.gt(10))).in(), __.as("a").out().as("xyz").select(Pop.last, "a").has("age", P.gt(10)).select(Pop.last, "xyz").in(), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- {__.as("a").out().where(__.as("a").has("age", P.gt(10))).in().path(), __.as("a").out().where(__.as("a").has("age", P.gt(10))).in().path(), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
- });
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ return Arrays.asList(new Object[][]{
+ {
+ __.select("a").by(__.out(GValue.of("x", "knows"))).asAdmin(),
+ new String[]{"x"},
+ Collections.emptyList(),
+ },
+ {
+ __.select("a", "b").by(__.out(GValue.of("y", "knows"))).by(__.in(GValue.of("z", "created"))).asAdmin(),
+ new String[]{"y", "z"},
+ Collections.emptyList(),
+ },
+ {
+ __.select(Pop.last, "a").by(__.out(GValue.of("w", "knows"))).asAdmin(),
+ new String[]{"w"},
+ Collections.emptyList(),
+ },
+ {
+ __.where(__.outE(GValue.of("x","knows"))).asAdmin(),
+ new String[]{"x"},
+ Collections.emptyList(),
+ },
+ {
+ __.where(__.as("a").outE(GValue.of("x","knows"))).asAdmin(),
+ new String[]{"x"},
+ Collections.emptyList(),
+ },
+ {
+ __.where(__.as("a").has("age", P.gt(GValue.ofInteger("x", 10)))).asAdmin(),
+ new String[]{"x"},
+ Collections.emptyList(),
+ },
+ {
+ __.where(__.as("a").has("age", P.gt(GValue.ofInteger("x", 10)))).asAdmin(),
+ new String[]{"x"},
+ TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList(),
+ },
+ {
+ __.select("a", "b").where(__.as("c").out(GValue.of("x", "knows"))).asAdmin(),
+ new String[]{"x"},
+ Collections.emptyList(),
+ },
+ {
+ __.select("a", "b").where(__.as("c").out(GValue.of("x", "knows"), GValue.of("y", "created"))).asAdmin(),
+ new String[]{"x", "y"},
+ Collections.emptyList(),
+ },
+ {
+ __.select("a", "b").where(__.as("c").out(GValue.of("x", "knows"), GValue.of("y", "created"))).asAdmin(),
+ new String[]{"x", "y"},
+ TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList(),
+ }
+ });
+ }
+
+ @Test
+ public void shouldPreserveGValueParameters() {
+ GValueManagerVerifier.verify(traversal, PathProcessorStrategy.instance())
+ .beforeApplying()
+ .hasVariables(expectedNames)
+ .afterApplying()
+ .hasVariables(expectedNames)
+ .variablesArePreserved();
+ }
}
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/RepeatUnrollStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/RepeatUnrollStrategyTest.java
index 9cd20c467d..bf4b960723 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/RepeatUnrollStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/RepeatUnrollStrategyTest.java
@@ -19,18 +19,23 @@
package org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
+import org.apache.tinkerpop.gremlin.process.traversal.GValueManager;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.NoOpBarrierStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.GValueManagerVerifier;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -39,98 +44,142 @@ import java.util.Collection;
import java.util.Collections;
import java.util.function.Predicate;
+import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.path;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-@RunWith(Parameterized.class)
+@RunWith(Enclosed.class)
public class RepeatUnrollStrategyTest {
private static final Translator.ScriptTranslator translator = GroovyTranslator.of("__");
- @Parameterized.Parameter(value = 0)
- public Traversal.Admin<?,?> original;
+ @RunWith(Parameterized.class)
+ public static class StandardTest {
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin<?,?> original;
- @Parameterized.Parameter(value = 1)
- public Traversal<?,?> optimized;
+ @Parameterized.Parameter(value = 1)
+ public Traversal<?,?> optimized;
- @Parameterized.Parameter(value = 2)
- public Collection<TraversalStrategy<?>> otherStrategies;
+ @Parameterized.Parameter(value = 2)
+ public Collection<TraversalStrategy<?>> otherStrategies;
- @Test
- public void doTest() {
- final String repr = translator.translate(original.getBytecode()).getScript();
- final TraversalStrategies strategiesWithLazy = new DefaultTraversalStrategies();
- strategiesWithLazy.addStrategies(RepeatUnrollStrategy.instance());
- Traversal.Admin<?, ?> clonedOriginal = this.original.clone();
+ @Test
+ public void doTest() {
+ final String repr = translator.translate(original.getBytecode()).getScript();
+ final TraversalStrategies strategiesWithLazy = new DefaultTraversalStrategies();
+ strategiesWithLazy.addStrategies(RepeatUnrollStrategy.instance());
+ Traversal.Admin<?, ?> clonedOriginal = this.original.clone();
+
+ // adding LazyBarrierStrategy as RepeatUnrollStrategy adds barriers and the existence of this strategy
+ // triggers those additions. if they are not there they will not be present and most of these assertions
+ // assume this strategy present
+ strategiesWithLazy.addStrategies(LazyBarrierStrategy.instance());
+ for (final TraversalStrategy<?> strategy : this.otherStrategies) {
+ strategiesWithLazy.addStrategies(strategy);
+ }
+ clonedOriginal.setStrategies(strategiesWithLazy);
+ clonedOriginal.applyStrategies();
+ assertEquals("With LazyBarrierStrategy: " + repr, this.optimized, clonedOriginal);
+
+ final TraversalStrategies strategiesWithoutLazy = new DefaultTraversalStrategies();
+ strategiesWithoutLazy.addStrategies(RepeatUnrollStrategy.instance());
+ for (final TraversalStrategy<?> strategy : this.otherStrategies) {
+ strategiesWithoutLazy.addStrategies(strategy);
+ }
+ clonedOriginal = this.original.clone();
+ clonedOriginal.setStrategies(strategiesWithoutLazy);
+ clonedOriginal.applyStrategies();
+ final Traversal.Admin<?, ?> optimizedWithoutBarriers = this.optimized.asAdmin().clone();
+
+ // remove all the barriers as LazyBarrierStrategy is not present and therefore RepeatUnrollStrategy should
+ // not add any
+ TraversalHelper.getStepsOfAssignableClassRecursively(NoOpBarrierStep.class, optimizedWithoutBarriers).forEach(s -> {
+ TraversalHelper.copyLabels(s, s.getPreviousStep(), true);
+ s.getTraversal().removeStep(s);
+ });
+ assertEquals("Without LazyBarrierStrategy: " + repr, optimizedWithoutBarriers, clonedOriginal);
- // adding LazyBarrierStrategy as RepeatUnrollStrategy adds barriers and the existence of this strategy
- // triggers those additions. if they are not there they will not be present and most of these assertions
- // assume this strategy present
- strategiesWithLazy.addStrategies(LazyBarrierStrategy.instance());
- for (final TraversalStrategy<?> strategy : this.otherStrategies) {
- strategiesWithLazy.addStrategies(strategy);
- }
- clonedOriginal.setStrategies(strategiesWithLazy);
- clonedOriginal.applyStrategies();
- assertEquals("With LazyBarrierStrategy: " + repr, this.optimized, clonedOriginal);
-
- final TraversalStrategies strategiesWithoutLazy = new DefaultTraversalStrategies();
- strategiesWithoutLazy.addStrategies(RepeatUnrollStrategy.instance());
- for (final TraversalStrategy<?> strategy : this.otherStrategies) {
- strategiesWithoutLazy.addStrategies(strategy);
}
- clonedOriginal = this.original.clone();
- clonedOriginal.setStrategies(strategiesWithoutLazy);
- clonedOriginal.applyStrategies();
- final Traversal.Admin<?, ?> optimizedWithoutBarriers = this.optimized.asAdmin().clone();
-
- // remove all the barriers as LazyBarrierStrategy is not present and therefore RepeatUnrollStrategy should
- // not add any
- TraversalHelper.getStepsOfAssignableClassRecursively(NoOpBarrierStep.class, optimizedWithoutBarriers).forEach(s -> {
- TraversalHelper.copyLabels(s, s.getPreviousStep(), true);
- s.getTraversal().removeStep(s);
- });
- assertEquals("Without LazyBarrierStrategy: " + repr, optimizedWithoutBarriers, clonedOriginal);
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ // tests added here should be written to assume that LazyBarrierStrategy is present. if a barrier() is
+ // manually added as the "original" then include an exclusion in doTest()
+ final int repeatBarrierSize = RepeatUnrollStrategy.MAX_BARRIER_SIZE;
+ final Predicate<Traverser<Vertex>> predicate = t -> t.loops() > 5;
+ return Arrays.asList(new Object[][]{
+ {__.repeat(out()).times(0), __.repeat(out()).times(0), Collections.emptyList()},
+ {__.<Vertex>times(0).repeat(out()), __.<Vertex>times(0).repeat(out()), Collections.emptyList()},
+ {__.identity(), __.identity(), Collections.emptyList()},
+ {out().as("a").in().repeat(__.outE("created").bothV()).times(2).in(), out().as("a").in().outE("created").bothV().barrier(repeatBarrierSize).outE("created").bothV().barrier(repeatBarrierSize).in(), Collections.emptyList()},
+ {out().repeat(__.outE("created").bothV()).times(1).in(), out().outE("created").bothV().barrier(repeatBarrierSize).in(), Collections.emptyList()},
+ {__.repeat(__.outE("created").bothV()).times(1).in(), __.outE("created").bothV().barrier(repeatBarrierSize).in(), Collections.emptyList()},
+ {__.repeat(out()).times(2).as("x").repeat(in().as("b")).times(3), out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize).as("x").in().as("b").barrier(repeatBarrierSize).in().as("b").barrier(repeatBarrierSize).in().as("b").barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.repeat(__.outE("created").inV()).times(2), __.outE("created").inV().barrier(repeatBarrierSize).outE("created").inV().barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.repeat(out()).times(3), out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.repeat(__.local(__.select("a").out("knows"))).times(2), __.local(__.select("a").out("knows")).barrier(repeatBarrierSize).local(__.select("a").out("knows")).barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.<Vertex>times(2).repeat(out()), out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.<Vertex>out().times(2).repeat(out().as("a")).as("x"), out().out().as("a").barrier(repeatBarrierSize).out().as("a").barrier(repeatBarrierSize).as("x"), Collections.emptyList()},
+ {__.repeat(out()).emit().times(2), __.repeat(out()).emit().times(2), Collections.emptyList()},
+ {__.repeat(out()).until(predicate), __.repeat(out()).until(predicate), Collections.emptyList()},
+ {__.repeat(out()).until(predicate).repeat(out()).times(2), __.repeat(out()).until(predicate).out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.repeat(__.union(__.both(), __.identity())).times(2).out(), __.union(__.both(), __.identity()).barrier(repeatBarrierSize).union(__.both(), __.identity()).barrier(repeatBarrierSize).out(), Collections.emptyList()},
+ {in().repeat(out("knows")).times(3).as("a").count().is(0), in().out("knows").barrier(repeatBarrierSize).out("knows").barrier(repeatBarrierSize).out("knows").as("a").count().is(0), Collections.emptyList()},
+ //
+ {__.repeat(__.outE().inV()).times(2), __.outE().inV().barrier(repeatBarrierSize).outE().inV().barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.repeat(__.outE().filter(path()).inV()).times(2), __.outE().filter(path()).inV().barrier(repeatBarrierSize).outE().filter(path()).inV().barrier(repeatBarrierSize), Collections.singletonList(IncidentToAdjacentStrategy.instance())},
+ {__.repeat(__.outE().inV()).times(2), __.out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize), Collections.singletonList(IncidentToAdjacentStrategy.instance())},
+ // Nested Loop tests
+ {__.repeat(out().repeat(out()).times(0)).times(1), __.out().repeat(out()).times(0).barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.repeat(out().repeat(out()).times(1)).times(1), __.out().out().barrier(repeatBarrierSize), Collections.emptyList()},
+ {__.repeat(out()).until(__.repeat(out()).until(predicate)), __.repeat(out()).until(__.repeat(out()).until(predicate)), Collections.emptyList()},
+ {__.repeat(__.repeat(out()).times(2)).until(predicate), __.repeat(__.out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize)).until(predicate), Collections.emptyList()},
+ {__.repeat(__.repeat(out("created")).until(__.has("name", "ripple"))), __.repeat(__.repeat(out("created")).until(__.has("name", "ripple"))), Collections.emptyList()},
+ });
+ }
}
- @Parameterized.Parameters(name = "{0}")
- public static Iterable<Object[]> generateTestParameters() {
- // tests added here should be written to assume that LazyBarrierStrategy is present. if a barrier() is
- // manually added as the "original" then include an exclusion in doTest()
- final int repeatBarrierSize = RepeatUnrollStrategy.MAX_BARRIER_SIZE;
- final Predicate<Traverser<Vertex>> predicate = t -> t.loops() > 5;
- return Arrays.asList(new Object[][]{
- {__.repeat(out()).times(0), __.repeat(out()).times(0), Collections.emptyList()},
- {__.<Vertex>times(0).repeat(out()), __.<Vertex>times(0).repeat(out()), Collections.emptyList()},
- {__.identity(), __.identity(), Collections.emptyList()},
- {out().as("a").in().repeat(__.outE("created").bothV()).times(2).in(), out().as("a").in().outE("created").bothV().barrier(repeatBarrierSize).outE("created").bothV().barrier(repeatBarrierSize).in(), Collections.emptyList()},
- {out().repeat(__.outE("created").bothV()).times(1).in(), out().outE("created").bothV().barrier(repeatBarrierSize).in(), Collections.emptyList()},
- {__.repeat(__.outE("created").bothV()).times(1).in(), __.outE("created").bothV().barrier(repeatBarrierSize).in(), Collections.emptyList()},
- {__.repeat(out()).times(2).as("x").repeat(__.in().as("b")).times(3), out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize).as("x").in().as("b").barrier(repeatBarrierSize).in().as("b").barrier(repeatBarrierSize).in().as("b").barrier(repeatBarrierSize), Collections.emptyList()},
- {__.repeat(__.outE("created").inV()).times(2), __.outE("created").inV().barrier(repeatBarrierSize).outE("created").inV().barrier(repeatBarrierSize), Collections.emptyList()},
- {__.repeat(out()).times(3), out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize), Collections.emptyList()},
- {__.repeat(__.local(__.select("a").out("knows"))).times(2), __.local(__.select("a").out("knows")).barrier(repeatBarrierSize).local(__.select("a").out("knows")).barrier(repeatBarrierSize), Collections.emptyList()},
- {__.<Vertex>times(2).repeat(out()), out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize), Collections.emptyList()},
- {__.<Vertex>out().times(2).repeat(out().as("a")).as("x"), out().out().as("a").barrier(repeatBarrierSize).out().as("a").barrier(repeatBarrierSize).as("x"), Collections.emptyList()},
- {__.repeat(out()).emit().times(2), __.repeat(out()).emit().times(2), Collections.emptyList()},
- {__.repeat(out()).until(predicate), __.repeat(out()).until(predicate), Collections.emptyList()},
- {__.repeat(out()).until(predicate).repeat(out()).times(2), __.repeat(out()).until(predicate).out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize), Collections.emptyList()},
- {__.repeat(__.union(__.both(), __.identity())).times(2).out(), __.union(__.both(), __.identity()).barrier(repeatBarrierSize).union(__.both(), __.identity()).barrier(repeatBarrierSize).out(), Collections.emptyList()},
- {__.in().repeat(out("knows")).times(3).as("a").count().is(0), __.in().out("knows").barrier(repeatBarrierSize).out("knows").barrier(repeatBarrierSize).out("knows").as("a").count().is(0), Collections.emptyList()},
- //
- {__.repeat(__.outE().inV()).times(2), __.outE().inV().barrier(repeatBarrierSize).outE().inV().barrier(repeatBarrierSize), Collections.emptyList()},
- {__.repeat(__.outE().filter(path()).inV()).times(2), __.outE().filter(path()).inV().barrier(repeatBarrierSize).outE().filter(path()).inV().barrier(repeatBarrierSize), Collections.singletonList(IncidentToAdjacentStrategy.instance())},
- {__.repeat(__.outE().inV()).times(2), __.out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize), Collections.singletonList(IncidentToAdjacentStrategy.instance())},
- // Nested Loop tests
- {__.repeat(out().repeat(out()).times(0)).times(1), __.out().repeat(out()).times(0).barrier(repeatBarrierSize), Collections.emptyList()},
- {__.repeat(out().repeat(out()).times(1)).times(1), __.out().out().barrier(repeatBarrierSize), Collections.emptyList()},
- {__.repeat(out()).until(__.repeat(out()).until(predicate)), __.repeat(out()).until(__.repeat(out()).until(predicate)), Collections.emptyList()},
- {__.repeat(__.repeat(out()).times(2)).until(predicate), __.repeat(__.out().barrier(repeatBarrierSize).out().barrier(repeatBarrierSize)).until(predicate), Collections.emptyList()},
- {__.repeat(__.repeat(out("created")).until(__.has("name", "ripple"))), __.repeat(__.repeat(out("created")).until(__.has("name", "ripple"))), Collections.emptyList()},
- });
+ /**
+ * Tests that GValueManager is being used correctly in RepeatUnrollStrategy
+ * to ensure that GValue state is properly maintained when steps are cloned and copied.
+ */
+ @RunWith(Parameterized.class)
+ public static class GValueTest {
+
+ @Parameterized.Parameter(value = 0)
+ public Traversal.Admin<?, ?> traversal;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> generateTestParameters() {
+ return Arrays.asList(new Object[][]{
+ {
+ __.repeat(out(GValue.of("x", "created"))).times(2).asAdmin()
+ },
+ {
+ __.repeat(out(GValue.of("x", "created"), GValue.of("y", "knows"))).times(2).asAdmin()
+ },
+ {
+ __.repeat(__.outE(GValue.of("x", "created")).inV()).times(2).asAdmin()
+ },
+ {
+ __.repeat(__.out(GValue.of("x", "created")).union(out(GValue.of("y", "knows")), in(GValue.of("z", "created")))).times(2).asAdmin()
+ }
+ });
+ }
+
+ @Test
+ public void shouldMaintainGValueState() {
+ // Verify that after applying the strategy, the GValue state is properly maintained
+ GValueManagerVerifier.verify(traversal, RepeatUnrollStrategy.instance())
+ .afterApplying()
+ .variablesArePreserved();
+ }
}
-}
+}
\ No newline at end of file