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 2024/07/29 20:09:31 UTC
(tinkerpop) 02/02: wip - params
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch gvalue-3.7
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 8b97ff5998f666fafdb768d7f57aa07dda2bc2ad
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Mon Jul 29 16:06:03 2024 -0400
wip - params
---
.../gremlin/process/traversal/Bytecode.java | 10 +-
.../traversal/dsl/graph/GraphTraversal.java | 258 ++++++++++++++++-
.../traversal/lambda/ConstantTraversal.java | 15 +-
.../gremlin/process/traversal/step/GValue.java | 179 +++++++++++-
.../process/traversal/step/filter/CoinStep.java | 2 +-
.../process/traversal/step/map/AddEdgeStep.java | 6 +
.../process/traversal/step/map/AddVertexStep.java | 5 +
.../process/traversal/step/map/ConstantStep.java | 19 +-
.../process/traversal/step/map/GraphStep.java | 30 +-
.../process/traversal/step/map/MergeEdgeStep.java | 5 +
.../process/traversal/step/map/MergeStep.java | 18 +-
.../traversal/step/map/MergeVertexStep.java | 5 +
.../process/traversal/step/map/VertexStep.java | 37 ++-
.../process/traversal/step/util/Parameters.java | 21 +-
.../strategy/decoration/SubgraphStrategy.java | 2 +-
.../optimization/AdjacentToIncidentStrategy.java | 2 +-
.../optimization/IncidentToAdjacentStrategy.java | 2 +-
.../optimization/InlineFilterStrategy.java | 20 +-
.../gremlin/structure/io/gryo/GryoVersion.java | 12 +-
.../gremlin/process/traversal/step/GTypeTest.java | 77 +++++
.../gremlin/process/traversal/step/GValueTest.java | 316 +++++++++++++++++++++
.../traversal/step/util/ParametersTest.java | 11 +
.../translator/JavascriptTranslatorTest.java | 8 +
23 files changed, 1009 insertions(+), 51 deletions(-)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
index 5b88cea5fc..862ee98bd9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
@@ -295,8 +296,13 @@ public class Bytecode implements Cloneable, Serializable {
if (null != variable)
return new Binding<>(variable, convertArgument(argument, false));
}
- //
- if (argument instanceof Traversal) {
+
+ // a GValue gets converted to its value because we don't serialize it. it really doesn't have anything to do
+ // with bytecode/remoting. it's an internal construct related to parsing with the grammar that is leaking
+ // over here. in 4.x with bytecode removed, we won't need to worry about this.
+ if (argument instanceof GValue)
+ return convertArgument(((GValue) argument).get(), false);
+ else if (argument instanceof Traversal) {
// prevent use of "g" to spawn child traversals
if (((Traversal) argument).asAdmin().getTraversalSource().isPresent())
throw new IllegalStateException(String.format(
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
index 103ad64255..3d9cd3e088 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
@@ -47,7 +47,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.lambda.TrueTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
-import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
import org.apache.tinkerpop.gremlin.process.traversal.step.ReadWriting;
@@ -243,7 +242,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
public interface Admin<S, E> extends Traversal.Admin<S, E>, GraphTraversal<S, E> {
/**
- * Filter the <code>E</code> object given a biased coin toss.
+ * Filter the <code>E</code> object given a biased coin toss. For internal use for parameterization features.
*
* @param probability the probability that the object will pass through the filter
* @return the traversal with an appended {@link CoinStep}.
@@ -251,26 +250,255 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
* @since 3.7.3
*/
public default GraphTraversal<S, E> coin(final GValue<Double> probability) {
- this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.coin, probability.get());
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.coin, probability);
return this.asAdmin().addStep(new CoinStep<>(this.asAdmin(), probability));
}
/**
- * Filter the <code>E</code> object given a biased coin toss where the argument can be either a {@link GValue}
- * object or a double.
+ * Map any object to a fixed <code>E</code> value. For internal use for parameterization features.
*
- * @param probability the probability that the object will pass through and must be a {@link GValue} object or a double.
- * @return the traversal with an appended {@link CoinStep}.
- * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#coin-step" target="_blank">Reference Documentation - Coin Step</a>
+ * @return the traversal with an appended {@link ConstantStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#constant-step" target="_blank">Reference Documentation - Constant Step</a>
+ * @since 3.7.3
+ */
+ public default <E2> GraphTraversal<S, E2> constant(final GValue<E2> e) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.constant, e);
+ return this.asAdmin().addStep(new ConstantStep<E, E2>(this.asAdmin(), e));
+ }
+
+ /**
+ * Map the {@link Vertex} to its adjacent vertices given a direction and edge labels. The arguments for the
+ * labels must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param direction the direction to traverse from the current vertex
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> to(final Direction direction, final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.to, direction, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, direction, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its outgoing adjacent vertices given the edge labels. The arguments for the
+ * labels must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> out(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.out, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.OUT, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its incoming adjacent vertices given the edge labels. The arguments for the
+ * labels must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> in(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.in, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.IN, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its adjacent vertices given the edge labels. The arguments for the labels must be
+ * either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> both(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.both, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.BOTH, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its incident edges given the direction and edge labels. The arguments for the
+ * labels must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param direction the direction to traverse from the current vertex
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> toE(final Direction direction, final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.toE, direction, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, direction, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its outgoing incident edges given the edge labels. The arguments for the labels
+ * must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> outE(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.outE, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.OUT, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its incoming incident edges given the edge labels. The arguments for the labels
+ * must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> inE(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.inE, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.IN, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its incident edges given the edge labels. The arguments for the labels must be
+ * either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> bothE(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.bothE, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.BOTH, edgeLabels));
+ }
+
+ /**
+ * Adds a {@link Vertex}.
+ *
+ * @param vertexLabel the label of the {@link Vertex} to add
+ * @return the traversal with the {@link AddVertexStep} added
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addvertex-step" target="_blank">Reference Documentation - AddVertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> addV(final GValue<String> vertexLabel) {
+ if (null == vertexLabel || null == vertexLabel.get()) throw new IllegalArgumentException("vertexLabel cannot be null");
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.addV, vertexLabel);
+ return this.asAdmin().addStep(new AddVertexStep<>(this.asAdmin(), vertexLabel));
+ }
+
+ /**
+ * Performs a merge (i.e. upsert) style operation for an {@link Vertex} using a {@code Map} as an argument.
+ * The {@code Map} represents search criteria and will match each of the supplied key/value pairs where the keys
+ * may be {@code String} property values or a value of {@link T}. If a match is not made it will use that search
+ * criteria to create the new {@link Vertex}.
+ *
+ * @param searchCreate This {@code Map} can have a key of {@link T} or a {@code String}.
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> mergeV(final GValue<Map<Object, Object>> searchCreate) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.mergeV, searchCreate);
+ final MergeVertexStep<S> step = new MergeVertexStep(this.asAdmin(), false, null == searchCreate ? GValue.ofMap(null) : searchCreate);
+ return this.asAdmin().addStep(step);
+ }
+
+ /**
+ * Spawns a {@link GraphTraversal} by doing a merge (i.e. upsert) style operation for an {@link Edge} using a
+ * {@code Map} as an argument.
+ *
+ * @param searchCreate This {@code Map} can have a key of {@link T} {@link Direction} or a {@code String}.
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> mergeE(final GValue<Map<Object, Object>> searchCreate) {
+ // get a construction time exception if the Map is bad
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.mergeE, searchCreate);
+ final MergeEdgeStep<S> step = new MergeEdgeStep(this.asAdmin(), false, null == searchCreate ? GValue.ofMap(null) : searchCreate);
+ return this.asAdmin().addStep(step);
+ }
+
+ /**
+ * Adds an {@link Edge} with the specified edge label.
+ *
+ * @param edgeLabel the label of the newly added edge
+ * @return the traversal with the {@link AddEdgeStep} added
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - AddEdge Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> addE(final GValue<String> edgeLabel) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.addE, edgeLabel);
+ return this.asAdmin().addStep(new AddEdgeStep<>(this.asAdmin(), edgeLabel));
+ }
+
+ /**
+ * Filters vertices, edges and vertex properties based on their label.
+ *
+ * @param label the label of the {@link Element}
+ * @param otherLabels additional labels of the {@link Element}
+ * @return the traversal with an appended {@link HasStep}
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#has-step" target="_blank">Reference Documentation - Has Step</a>
+ * @since 3.2.2
+ */
+ public default GraphTraversal<S, E> hasLabel(final Object label, final Object... otherLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.hasLabel, label, otherLabels);
+
+ // groovy evaluation seems to do strange things with varargs given hasLabel(null, null). odd someone would
+ // do this but the failure is ugly if not handled.
+ final int otherLabelsLength = null == otherLabels ? 0 : otherLabels.length;
+ final Object[] labels = new Object[otherLabelsLength + 1];
+ labels[0] = label;
+ if (otherLabelsLength > 0)
+ System.arraycopy(otherLabels, 0, labels, 1, otherLabelsLength);
+ return TraversalHelper.addHasContainer(this.asAdmin(), new HasContainer(T.label.getAccessor(), labels.length == 1 ? P.eq(labels[0]) : P.within(labels)));
+ }
+
+ /**
+ * This is a step modulator to a {@link TraversalOptionParent} like {@code choose()} or {@code mergeV()} where the
+ * provided argument associated to the {@code token} is applied according to the semantics of the step. Please see
+ * the documentation of such steps to understand the usage context.
+ *
+ * @param m Provides a {@code Map} as the option which is the same as doing {@code constant(m)}.
+ * @return the traversal with the modulated step
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergev-step" target="_blank">Reference Documentation - MergeV Step</a>
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergee-step" target="_blank">Reference Documentation - MergeE Step</a>
+ * @since 3.7.3
+ */
+ public default <M, E2> GraphTraversal<S, E> option(final M token, final GValue<Map<Object, Object>> m) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.option, token, m);
+ ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption(token, (Traversal.Admin<E, E2>) new ConstantTraversal<>(m).asAdmin());
+ return this;
+ }
+
+ /**
+ * This is a step modulator to a {@link TraversalOptionParent} like {@code choose()} or {@code mergeV()} where the
+ * provided argument associated to the {@code token} is applied according to the semantics of the step. Please see
+ * the documentation of such steps to understand the usage context.
+ *
+ * @param m Provides a {@code Map} as the option which is the same as doing {@code constant(m)}.
+ * @return the traversal with the modulated step
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergev-step" target="_blank">Reference Documentation - MergeV Step</a>
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergee-step" target="_blank">Reference Documentation - MergeE Step</a>
* @since 3.7.3
*/
- public default GraphTraversal<S, E> coin(final Object probability) {
- if (probability instanceof Number)
- return this.coin(((Number) probability).doubleValue());
- else if (probability instanceof GValue && ((GValue) probability).getType() == GType.DOUBLE)
- return this.coin(probability);
- else
- throw new IllegalArgumentException("The probability must be a GValue<Double> or a double");
+ public default <M, E2> GraphTraversal<S, E> option(final Merge merge, final GValue<Map<Object, Object>> m, final VertexProperty.Cardinality cardinality) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.option, merge, m, cardinality);
+
+ final Map<Object, Object> map = m.get();
+
+ // do explicit cardinality for every single pair in the map
+ for (Object k : map.keySet()) {
+ final Object o = map.get(k);
+ if (!(o instanceof CardinalityValueTraversal))
+ map.put(k, new CardinalityValueTraversal(cardinality, o));
+ }
+ ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption((M) merge, (Traversal.Admin<E, E2>) new ConstantTraversal<>(m).asAdmin());
+ return this;
}
@Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
index b22d88e66c..3bac69ce48 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal.lambda;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import java.util.Objects;
@@ -29,15 +30,23 @@ import java.util.Objects;
*/
public final class ConstantTraversal<S, E> extends AbstractLambdaTraversal<S, E> {
- private final E end;
+ private final GValue<E> end;
public ConstantTraversal(final E end) {
- this.end = end;
+ this.end = null == end ? GValue.of(null) : GValue.of(end);
+ }
+
+ public ConstantTraversal(final GValue<E> end) {
+ this.end = null == end ? GValue.of(null) : end;
}
@Override
public E next() {
- return this.end;
+ return this.end.get();
+ }
+
+ public GValue<E> getEnd() {
+ return end;
}
@Override
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 cb6930e432..4de2ca1f8e 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
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.Vertex;
+import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
@@ -38,7 +39,7 @@ import java.util.Set;
* the name is not given, the value was provided literally in the traversal. The value of the variable can be any
* object. The {@code GValue} also includes the {@link GType} that describes the type it contains.
*/
-public class GValue<V> {
+public class GValue<V> implements Cloneable, Serializable {
private final String name;
private final GType type;
@@ -54,18 +55,33 @@ public class GValue<V> {
this.value = value;
}
+ /**
+ * Determines if the value held by this object was defined as a variable or a literal value. Literal values simply
+ * have no name.
+ */
public boolean isVariable() {
return this.name != null;
}
+ /**
+ * Gets the name of the variable if it was defined as such and returns empty if the value was a literal.
+ */
public Optional<String> getName() {
return Optional.ofNullable(this.name);
}
+ /**
+ * Gets the type of the value. The explicit type could be determined with {@code instanceof} on the value, but this
+ * might be helpful for cases where the value was constructed with a {@code null} value which might just return as
+ * {@code Object}.
+ */
public GType getType() {
return this.type;
}
+ /**
+ * Gets the value.
+ */
public V get() {
return this.value;
}
@@ -76,6 +92,28 @@ public class GValue<V> {
String.format("%s&%s", name, value) : Objects.toString(value);
}
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ GValue<?> gValue = (GValue<?>) o;
+ return Objects.equals(name, gValue.name) && type == gValue.type && Objects.equals(value, gValue.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, type, value);
+ }
+
+ /**
+ * Create a new {@code Var} from a particular value but without the specified name.
+ *
+ * @param value the value of the variable
+ */
+ public static <V> GValue<V> of(final V value) {
+ return new GValue<>(GType.getType(value), value);
+ }
+
/**
* Create a new {@code Var} with the specified name and value.
*
@@ -85,63 +123,198 @@ public class GValue<V> {
public static <V> GValue<V> of(final String name, final V value) {
return new GValue<>(name, GType.getType(value), value);
}
+ /**
+ * Create a new {@code GValue} for a string value.
+ */
+ public static GValue<String> ofString(final String value) {
+ return new GValue<>(GType.STRING, value);
+ }
+ /**
+ * Create a new {@code GValue} for a string value with a specified name.
+ */
public static GValue<String> ofString(final String name, final String value) {
return new GValue<>(name, GType.STRING, value);
}
+ /**
+ * Create a new {@code GValue} for an integer value.
+ */
+ public static GValue<Integer> ofInteger(final Integer value) {
+ return new GValue<>(GType.INTEGER, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for an integer value with a specified name.
+ */
public static GValue<Integer> ofInteger(final String name, final Integer value) {
return new GValue<>(name, GType.INTEGER, value);
}
+ /**
+ * Create a new {@code GValue} for a boolean value.
+ */
+ public static GValue<Boolean> ofBoolean(final Boolean value) {
+ return new GValue<>(GType.BOOLEAN, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a boolean value with a specified name.
+ */
public static GValue<Boolean> ofBoolean(final String name, final Boolean value) {
return new GValue<>(name, GType.BOOLEAN, value);
}
+ /**
+ * Create a new {@code GValue} for a double value.
+ */
+ public static GValue<Double> ofDouble(final Double value) {
+ return new GValue<>(GType.DOUBLE, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a double value with a specified name.
+ */
public static GValue<Double> ofDouble(final String name, final Double value) {
return new GValue<>(name, GType.DOUBLE, value);
}
- public static GValue<Double> ofDouble(final Double value) {
- return new GValue<>(GType.DOUBLE, value);
+ /**
+ * Create a new {@code GValue} for a BigInteger value.
+ */
+ public static GValue<BigInteger> ofBigInteger(final BigInteger value) {
+ return new GValue<>(GType.BIG_INTEGER, value);
}
+ /**
+ * Create a new {@code GValue} for a BigInteger value with a specified name.
+ */
public static GValue<BigInteger> ofBigInteger(final String name, final BigInteger value) {
return new GValue<>(name, GType.BIG_INTEGER, value);
}
+ /**
+ * Create a new {@code GValue} for a BigDecimal value.
+ */
+ public static GValue<BigDecimal> ofBigDecimal(final BigDecimal value) {
+ return new GValue<>(GType.BIG_DECIMAL, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a BigDecimal value with a specified name.
+ */
public static GValue<BigDecimal> ofBigDecimal(final String name, final BigDecimal value) {
return new GValue<>(name, GType.BIG_DECIMAL, value);
}
+ /**
+ * Create a new {@code GValue} for a long value.
+ */
+ public static GValue<Long> ofLong(final Long value) {
+ return new GValue<>(GType.LONG, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a long value with a specified name.
+ */
public static GValue<Long> ofLong(final String name, final Long value) {
return new GValue<>(name, GType.LONG, value);
}
+ /**
+ * Create a new {@code GValue} for a map value.
+ */
+ public static GValue<Map> ofMap(final Map value) {
+ return new GValue<>(GType.MAP, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a map value with a specified name.
+ */
public static GValue<Map> ofMap(final String name, final Map value) {
return new GValue<>(name, GType.MAP, value);
}
+ /**
+ * Create a new {@code GValue} for a list value.
+ */
+ public static GValue<List> ofList(final List value) {
+ return new GValue<>(GType.LIST, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a list value with a specified name.
+ */
public static GValue<List> ofList(final String name, final List value) {
return new GValue<>(name, GType.LIST, value);
}
+ /**
+ * Create a new {@code GValue} for a set value.
+ */
+ public static GValue<Set> ofSet(final Set value) {
+ return new GValue<>(GType.SET, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a set value with a specified name.
+ */
public static GValue<Set> ofSet(final String name, final Set value) {
return new GValue<>(name, GType.SET, value);
}
+ /**
+ * Create a new {@code GValue} for a vertex value.
+ */
+ public static GValue<Vertex> ofVertex(final Vertex value) {
+ return new GValue<>(GType.VERTEX, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a vertex value with a specified name.
+ */
public static GValue<Vertex> ofVertex(final String name, final Vertex value) {
return new GValue<>(name, GType.VERTEX, value);
}
+ /**
+ * Create a new {@code GValue} for an edge value.
+ */
+ public static GValue<Edge> ofEdge(final Edge value) {
+ return new GValue<>(GType.EDGE, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for an edge value with a specified name.
+ */
public static GValue<Edge> ofEdge(final String name, final Edge value) {
return new GValue<>(name, GType.EDGE, value);
}
+ /**
+ * Create a new {@code GValue} for a path value.
+ */
+ public static GValue<Path> ofPath(final Path value) {
+ return new GValue<>(GType.PATH, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a path value with a specified name.
+ */
public static GValue<Path> ofPath(final String name, final Path value) {
return new GValue<>(name, GType.PATH, value);
}
+ /**
+ * Create a new {@code GValue} for a property value.
+ */
+ public static GValue<Property> ofProperty(final Property value) {
+ return new GValue<>(GType.PROPERTY, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a property value with a specified name.
+ */
public static GValue<Property> ofProperty(final String name, final Property value) {
return new GValue<>(name, GType.PROPERTY, value);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
index 7395dd8544..e220952200 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
@@ -50,7 +50,7 @@ public final class CoinStep<S> extends FilterStep<S> implements Seedable {
public CoinStep(final Traversal.Admin traversal, final GValue<Double> probability) {
super(traversal);
- this.probability = probability;
+ this.probability = null == probability ? GValue.ofDouble(null) : probability;
}
@Override
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 f5f7280764..9dfa4ce754 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
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
@@ -59,6 +60,11 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
this.parameters.set(this, T.label, edgeLabel);
}
+ public AddEdgeStep(final Traversal.Admin traversal, final GValue<String> edgeLabel) {
+ super(traversal);
+ this.parameters.set(this, T.label, edgeLabel);
+ }
+
public AddEdgeStep(final Traversal.Admin traversal, final Traversal.Admin<S,String> edgeLabelTraversal) {
super(traversal);
this.parameters.set(this, T.label, edgeLabelTraversal);
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 80597ca86c..3a433463e8 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
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
@@ -50,6 +51,10 @@ public class AddVertexStep<S> extends ScalarMapStep<S, Vertex>
super(traversal);
this.parameters.set(this, T.label, null == label ? Vertex.DEFAULT_LABEL : label);
}
+ public AddVertexStep(final Traversal.Admin traversal, final GValue<String> label) {
+ super(traversal);
+ this.parameters.set(this, T.label, null == label ? Vertex.DEFAULT_LABEL : label);
+ }
public AddVertexStep(final Traversal.Admin traversal, final Traversal.Admin<S,String> vertexLabelTraversal) {
super(traversal);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java
index 9434cc3b47..bf554d1aa9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -29,29 +30,37 @@ import java.util.Set;
public class ConstantStep<S, E> extends ScalarMapStep<S, E> {
- private final E constant;
+ private final GValue<E> constant;
public ConstantStep(final Traversal.Admin traversal, final E constant) {
+ this(traversal, GValue.of(constant));
+ }
+
+ public ConstantStep(final Traversal.Admin traversal, final GValue<E> constant) {
super(traversal);
- this.constant = constant;
+ this.constant = null == constant ? GValue.of(null) : constant;
}
public E getConstant() {
+ return this.constant.get();
+ }
+
+ public GValue<E> getConstantGValue() {
return this.constant;
}
@Override
protected E map(final Traverser.Admin<S> traverser) {
- return this.constant;
+ return this.constant.get();
}
@Override
public String toString() {
- return StringFactory.stepString(this, this.constant);
+ return StringFactory.stepString(this, this.constant.get());
}
@Override
public int hashCode() {
- return super.hashCode() ^ Objects.hashCode(this.constant);
+ return super.hashCode() ^ Objects.hashCode(this.constant.get());
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
index 3b1720ff9a..ee151aed12 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.GraphComputing;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
@@ -66,11 +67,28 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
this.returnClass = returnClass;
this.ids = (ids != null && ids.length == 1 && ids[0] instanceof Collection) ? ((Collection) ids[0]).toArray(new Object[((Collection) ids[0]).size()]) : ids;
this.isStart = isStart;
+
this.iteratorSupplier = () -> (Iterator<E>) (Vertex.class.isAssignableFrom(this.returnClass) ?
- this.getTraversal().getGraph().get().vertices(this.ids) :
- this.getTraversal().getGraph().get().edges(this.ids));
+ this.getTraversal().getGraph().get().vertices(convertGValuesToIds()) :
+ this.getTraversal().getGraph().get().edges(convertGValuesToIds()));
}
+ /**
+ * Converts {@link GValue} objects the ids array to their values to prevent them from leaking to the Graph API.
+ */
+ private Object[] convertGValuesToIds() {
+ final Object[] newIds = new Object[this.ids.length];
+ for (int i = 0; i < this.ids.length; i++) {
+ if (newIds[i] instanceof GValue) {
+ newIds[i] = ((GValue) this.ids[i]).get();
+ } else {
+ newIds[i] = this.ids[i];
+ }
+ }
+ return newIds;
+ }
+
+
public String toString() {
return StringFactory.stepString(this, this.returnClass.getSimpleName().toLowerCase(), Arrays.toString(this.ids));
}
@@ -137,7 +155,13 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
public void convertElementsToIds() {
if (null != this.ids) {
- for (int i = 0; i < this.ids.length; i++) { // if this is going to OLAP, convert to ids so you don't serialize elements
+ // if this is going to OLAP, convert to ids so you don't serialize elements
+ for (int i = 0; i < this.ids.length; i++) {
+
+ // spare the Graph API from GValue objects, as they are Gremlin level objects
+ if (this.ids[i] instanceof GValue)
+ this.ids[i] = ((GValue) this.ids[i]).get();
+
if (this.ids[i] instanceof Element)
this.ids[i] = ((Element) this.ids[i]).id();
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
index 6272a497a9..3204d7746f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
@@ -34,6 +34,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventUtil;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -71,6 +72,10 @@ public class MergeEdgeStep<S> extends MergeStep<S, Edge, Object> {
super(traversal, isStart, merge);
}
+ public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> merge) {
+ super(traversal, isStart, merge);
+ }
+
public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map> mergeTraversal) {
super(traversal, isStart, mergeTraversal);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java
index 9b0f12d1d6..229e507a15 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java
@@ -35,6 +35,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.step.Deleting;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
@@ -81,6 +83,11 @@ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E>
validate(mergeMap, false);
}
+ public MergeStep(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> mergeMap) {
+ this(traversal, isStart, new ConstantTraversal<>(mergeMap));
+ validate(mergeMap.get(), false);
+ }
+
public MergeStep(final Traversal.Admin traversal, final boolean isStart,
final Traversal.Admin mergeTraversal) {
super(traversal);
@@ -288,7 +295,7 @@ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E>
op, allowedTokens, k));
}
if (k == T.label) {
- if (!(v instanceof String)) {
+ if (!(isStringType(v))) {
throw new IllegalArgumentException(String.format(
"%s() and option(onCreate) args expect T.label value to be of String - found: %s", op,
v.getClass().getSimpleName()));
@@ -309,6 +316,13 @@ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E>
});
}
+ /**
+ * Determines if the object is a string or a {@link GValue} with a string value.
+ */
+ protected static boolean isStringType(final Object k) {
+ return k instanceof String || (k instanceof GValue && ((GValue) k).getType() == GType.STRING);
+ }
+
/**
* Prohibit overrides to the existence criteria (id/label/from/to) in onCreate.
*/
@@ -369,7 +383,7 @@ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E>
return id != null ? graph.traversal().V(id) : graph.traversal().V();
}
- protected GraphTraversal searchVerticesLabelConstraint(GraphTraversal t, final String label) {
+ protected GraphTraversal searchVerticesLabelConstraint(final GraphTraversal t, final String label) {
return label != null ? t.hasLabel(label) : t;
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
index 11a9fc2ca4..51820f0c2b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
@@ -30,6 +30,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventUtil;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.T;
@@ -60,6 +61,10 @@ public class MergeVertexStep<S> extends MergeStep<S, Vertex, Map> {
super(traversal, isStart, merge);
}
+ public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> merge) {
+ super(traversal, isStart, merge);
+ }
+
public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map> mergeTraversal) {
super(traversal, isStart, mergeTraversal);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
index bd429f049c..e27fbb3802 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -39,20 +40,41 @@ import java.util.Set;
import java.util.stream.Collectors;
/**
+ * Handles the logic of traversing to adjacent vertices or edges given a direction and edge labels for steps like,
+ * {@code out}, {@code in}, {@code both}, {@code outE}, {@code inE}, and {@code bothE}.
+ *
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implements AutoCloseable, Configuring {
protected Parameters parameters = new Parameters();
private final String[] edgeLabels;
+ private final GValue<String>[] edgeLabelsGValue;
private Direction direction;
private final Class<E> returnClass;
public VertexStep(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final String... edgeLabels) {
+ this(traversal, returnClass, direction, (Object[]) edgeLabels);
+ }
+
+ public VertexStep(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final Object... edgeLabels) {
super(traversal);
this.direction = direction;
- this.edgeLabels = edgeLabels;
this.returnClass = returnClass;
+
+ // check each edgeLabel to ensure it is a string or a GValue with a GType.STRING in it. if it is just a string
+ // then convert edgeLabels to a GValue<String> or otherwise throw an exception
+ this.edgeLabelsGValue = Arrays.stream(edgeLabels).map(edgeLabel -> {
+ if (edgeLabel instanceof String)
+ return GValue.ofString((String) edgeLabel);
+ else if (edgeLabel instanceof GValue && ((GValue<String>) edgeLabel).get().getClass().equals(String.class))
+ return (GValue<String>) edgeLabel;
+ else
+ throw new IllegalArgumentException("All edge labels must be strings");
+ }).toArray(GValue[]::new);
+
+ // convert the GValue<String> to a String[] for the edgeLabels field to cache the values
+ this.edgeLabels = Arrays.stream(this.edgeLabelsGValue).map(GValue::get).toArray(String[]::new);
}
@Override
@@ -67,6 +89,9 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
@Override
protected Iterator<E> flatMap(final Traverser.Admin<Vertex> traverser) {
+ // not passing GValue to graphs at this point. if a graph wants to support GValue, it should implement
+ // its own step to do so. in this way, we keep things backwards compatible and don't force folks to have
+ // deal with this until they are ready.
return Vertex.class.isAssignableFrom(this.returnClass) ?
(Iterator<E>) traverser.get().vertices(this.direction, this.edgeLabels) :
(Iterator<E>) traverser.get().edges(this.direction, this.edgeLabels);
@@ -80,6 +105,10 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
return this.edgeLabels;
}
+ public GValue<String>[] getEdgeLabelsGValue() {
+ return this.edgeLabelsGValue;
+ }
+
public Class<E> getReturnClass() {
return this.returnClass;
}
@@ -88,10 +117,16 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
this.direction = this.direction.opposite();
}
+ /**
+ * Determines if the step returns vertices.
+ */
public boolean returnsVertex() {
return this.returnClass.equals(Vertex.class);
}
+ /**
+ * Determines if the step returns edges.
+ */
public boolean returnsEdge() {
return this.returnClass.equals(Edge.class);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
index d1654a344e..9b4a926c74 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
@@ -22,6 +22,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.util;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
@@ -165,7 +166,7 @@ public class Parameters implements Cloneable, Serializable {
/**
* Gets the array of keys/values of the parameters while resolving parameter values that contain
- * {@link Traversal} instances.
+ * {@link Traversal} or {@link GValue} instances.
*/
public <S> Object[] getKeyValues(final Traverser.Admin<S> traverser, final Object... exceptKeys) {
if (this.parameters.isEmpty()) return EMPTY_ARRAY;
@@ -173,14 +174,28 @@ public class Parameters implements Cloneable, Serializable {
for (final Map.Entry<Object, List<Object>> entry : this.parameters.entrySet()) {
if (!ArrayUtils.contains(exceptKeys, entry.getKey())) {
for (final Object value : entry.getValue()) {
- keyValues.add(entry.getKey() instanceof Traversal.Admin ? TraversalUtil.apply(traverser, (Traversal.Admin<S, ?>) entry.getKey()) : entry.getKey());
- keyValues.add(value instanceof Traversal.Admin ? TraversalUtil.apply(traverser, (Traversal.Admin<S, ?>) value) : value);
+ keyValues.add(resolve(entry.getKey(), traverser));
+ keyValues.add(resolve(value, traverser));
}
}
}
return keyValues.toArray(new Object[keyValues.size()]);
}
+ /**
+ * Takes an object and tests if it is a {@link GValue} or a {@link Traversal} and if so, resolves it to its value
+ * otherwise it just returns itself.
+ */
+ private static <S> Object resolve(final Object object, final Traverser.Admin<S> traverser) {
+ if (object instanceof Traversal.Admin) {
+ return TraversalUtil.apply(traverser, (Traversal.Admin<S, ?>) object);
+ } else if (object instanceof GValue) {
+ return ((GValue) object).get();
+ } else {
+ return object;
+ }
+ }
+
/**
* Gets an immutable set of the parameters without evaluating them in the context of a {@link Traverser} as
* is done in {@link #getKeyValues(Traverser.Admin, Object...)}.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java
index 6f34d62226..b44b62f0e3 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java
@@ -193,7 +193,7 @@ public final class SubgraphStrategy extends AbstractTraversalStrategy<TraversalS
continue;
if (edgeCriterion != null) {
- final VertexStep<Edge> someEStep = new VertexStep<>(traversal, Edge.class, step.getDirection(), step.getEdgeLabels());
+ final VertexStep<Edge> someEStep = new VertexStep<>(traversal, Edge.class, step.getDirection(), step.getEdgeLabelsGValue());
final Step<Edge, Vertex> someVStep = step.getDirection() == Direction.BOTH ?
new EdgeOtherVertexStep(traversal) :
new EdgeVertexStep(traversal, step.getDirection().opposite());
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
index d8e538ab37..b33d527c2c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
@@ -118,7 +118,7 @@ public final class AdjacentToIncidentStrategy extends AbstractTraversalStrategy<
final Step newStep;
if (step instanceof VertexStep) {
final VertexStep vs = (VertexStep) step;
- newStep = new VertexStep<>(traversal, Edge.class, vs.getDirection(), vs.getEdgeLabels());
+ newStep = new VertexStep<>(traversal, Edge.class, vs.getDirection(), vs.getEdgeLabelsGValue());
} else if (step instanceof PropertiesStep) {
final PropertiesStep ps = (PropertiesStep) step;
newStep = new PropertiesStep(traversal, PropertyType.PROPERTY, ps.getPropertyKeys());
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
index 75d55adcd0..813b60b955 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
@@ -110,7 +110,7 @@ public final class IncidentToAdjacentStrategy extends AbstractTraversalStrategy<
* @param step2 the vertex-emitting step to replace
*/
private static void optimizeSteps(final Traversal.Admin traversal, final VertexStep step1, final Step step2) {
- final Step newStep = new VertexStep(traversal, Vertex.class, step1.getDirection(), step1.getEdgeLabels());
+ final Step newStep = new VertexStep(traversal, Vertex.class, step1.getDirection(), step1.getEdgeLabelsGValue());
for (final String label : (Iterable<String>) step2.getLabels()) {
newStep.addLabel(label);
}
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..5ff63b3e0f 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
@@ -26,6 +26,8 @@ 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.GType;
+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;
@@ -124,26 +126,28 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
&& ((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<Object> edgeLabels = new ArrayList<>();
for (final HasContainer hasContainer : new ArrayList<>(step.getHasContainers())) {
if (hasContainer.getKey().equals(T.label.getAccessor())) {
if (hasContainer.getBiPredicate() == Compare.eq &&
- hasContainer.getValue() instanceof String &&
+ (hasContainer.getValue() instanceof String ||
+ (hasContainer.getValue() instanceof GValue && ((GValue) hasContainer.getValue()).getType() == GType.STRING)) &&
edgeLabels.isEmpty()) {
- edgeLabels.add((String) hasContainer.getValue());
+ edgeLabels.add(hasContainer.getValue());
step.removeHasContainer(hasContainer);
} else if (hasContainer.getBiPredicate() == Contains.within &&
hasContainer.getValue() instanceof Collection &&
((Collection) hasContainer.getValue()).containsAll(edgeLabels)) {
- edgeLabels.addAll((Collection<String>) hasContainer.getValue());
+ edgeLabels.addAll((Collection) hasContainer.getValue());
step.removeHasContainer(hasContainer);
} 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<>();
+ final List<Object> newEdges = new ArrayList<>();
for (int i = 0; i < orps.size(); i++) {
- if (orps.get(i).getBiPredicate() == Compare.eq && orps.get(i).getValue() instanceof String)
- newEdges.add((String) orps.get(i).getValue());
+ if (orps.get(i).getBiPredicate() == Compare.eq && (orps.get(i).getValue() instanceof String ||
+ (orps.get(i).getValue() instanceof GValue && ((GValue) orps.get(i).getValue()).getType() == GType.STRING)))
+ newEdges.add(orps.get(i).getValue());
else {
removeContainer = false;
break;
@@ -157,7 +161,7 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
}
}
if (!edgeLabels.isEmpty()) {
- final VertexStep<Edge> newVertexStep = new VertexStep<>(traversal, Edge.class, previousStep.getDirection(), edgeLabels.toArray(new String[edgeLabels.size()]));
+ final VertexStep<Edge> newVertexStep = new VertexStep<>(traversal, Edge.class, previousStep.getDirection(), edgeLabels.toArray(new Object[edgeLabels.size()]));
TraversalHelper.replaceStep(previousStep, newVertexStep, traversal);
TraversalHelper.copyLabels(previousStep, newVertexStep, false);
if (step.getHasContainers().isEmpty()) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
index 4ee9f99bf9..9edea31512 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
@@ -38,6 +38,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.Text;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
+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.map.FoldStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupCountStep;
@@ -324,7 +326,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(Bytecode.class, 122, new GryoSerializersV3.BytecodeSerializer()));
add(GryoTypeReg.of(P.class, 124, new GryoSerializersV3.PSerializer()));
add(GryoTypeReg.of(TextP.class, 186, new GryoSerializersV3.TextPSerializer()));
- add(GryoTypeReg.of(Text.RegexPredicate.class, 197)); // ***LAST ID***
+ add(GryoTypeReg.of(Text.RegexPredicate.class, 197));
add(GryoTypeReg.of(Lambda.class, 125, new GryoSerializersV3.LambdaSerializer()));
add(GryoTypeReg.of(Bytecode.Binding.class, 126, new GryoSerializersV3.BindingSerializer()));
add(GryoTypeReg.of(Order.class, 127));
@@ -422,6 +424,9 @@ public enum GryoVersion {
add(GryoTypeReg.of(Stack.class, 181));
add(GryoTypeReg.of(ReferenceMap.class, 182));
+ add(GryoTypeReg.of(GValue.class, 199, new JavaSerializer()));
+ add(GryoTypeReg.of(GType.class, 200, new JavaSerializer())); // ***LAST ID***
+
// placeholder serializers for classes that don't live here in core. this will allow them to be used if
// present or ignored if the class isn't available. either way the registration numbers are held as
// placeholders so that the format stays stable
@@ -537,7 +542,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(Pop.class, 133));
add(GryoTypeReg.of(SackFunctions.Barrier.class, 135));
add(GryoTypeReg.of(Pick.class, 137));
- add(GryoTypeReg.of(DT.class, 198)); // ***LAST ID***
+ add(GryoTypeReg.of(DT.class, 198));
add(GryoTypeReg.of(Merge.class, 196));
add(GryoTypeReg.of(HashSetSupplier.class, 136, new UtilSerializers.HashSetSupplierSerializer()));
add(GryoTypeReg.of(MultiComparator.class, 165));
@@ -628,6 +633,9 @@ public enum GryoVersion {
add(GryoTypeReg.of(LabelledCounter.class, 180));
add(GryoTypeReg.of(Stack.class, 181));
add(GryoTypeReg.of(ReferenceMap.class, 182));
+
+ add(GryoTypeReg.of(GValue.class, 199, new JavaSerializer()));
+ add(GryoTypeReg.of(GType.class, 200, new JavaSerializer())); // ***LAST ID***
}};
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GTypeTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GTypeTest.java
new file mode 100644
index 0000000000..9596445860
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GTypeTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class GTypeTest {
+
+ @Test
+ public void shouldReturnTrueForNumericTypes() {
+ assertThat(GType.INTEGER.isNumeric(), is(true));
+ assertThat(GType.DOUBLE.isNumeric(), is(true));
+ assertThat(GType.LONG.isNumeric(), is(true));
+ assertThat(GType.BIG_INTEGER.isNumeric(), is(true));
+ assertThat(GType.BIG_DECIMAL.isNumeric(), is(true));
+ }
+
+ @Test
+ public void shouldReturnFalseForNonNumericTypes() {
+ assertThat(GType.STRING.isNumeric(), is(false));
+ assertThat(GType.BOOLEAN.isNumeric(), is(false));
+ assertThat(GType.EDGE.isNumeric(), is(false));
+ assertThat(GType.VERTEX.isNumeric(), is(false));
+ }
+
+ @Test
+ public void shouldReturnCorrectGType() {
+ assertEquals(GType.STRING, GType.getType("test"));
+ assertEquals(GType.INTEGER, GType.getType(123));
+ assertEquals(GType.BOOLEAN, GType.getType(true));
+ assertEquals(GType.DOUBLE, GType.getType(123.45));
+ assertEquals(GType.LONG, GType.getType(123L));
+ assertEquals(GType.MAP, GType.getType(Collections.emptyMap()));
+ assertEquals(GType.LIST, GType.getType(Collections.emptyList()));
+ assertEquals(GType.SET, GType.getType(Collections.emptySet()));
+ assertEquals(GType.VERTEX, GType.getType(mock(Vertex.class)));
+ assertEquals(GType.EDGE, GType.getType(mock(Edge.class)));
+ assertEquals(GType.PATH, GType.getType(mock(Path.class)));
+ assertEquals(GType.PROPERTY, GType.getType(mock(Property.class)));
+ assertEquals(GType.BIG_INTEGER, GType.getType(BigInteger.ONE));
+ assertEquals(GType.BIG_DECIMAL, GType.getType(BigDecimal.ONE));
+ }
+
+ @Test
+ public void shouldReturnUnknownForUnmatchedTypes() {
+ assertEquals(GType.UNKNOWN, GType.getType(new Object()));
+ }
+}
\ No newline at end of file
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java
new file mode 100644
index 0000000000..fcfac9b9d9
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+
+public class GValueTest {
+
+ @Test
+ public void shouldCreateGValueFromValue() {
+ final GValue<Integer> gValue = GValue.of(123);
+ assertEquals(123, gValue.get().intValue());
+ assertEquals(GType.INTEGER, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromNameAndValue() {
+ final GValue<Integer> gValue = GValue.of("varName", 123);
+ assertEquals(123, gValue.get().intValue());
+ assertEquals(GType.INTEGER, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromString() {
+ final GValue<String> gValue = GValue.ofString("test");
+ assertEquals("test", gValue.get());
+ assertEquals(GType.STRING, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromStringWithName() {
+ final GValue<String> gValue = GValue.ofString("varName", "test");
+ assertEquals("test", gValue.get());
+ assertEquals(GType.STRING, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromInteger() {
+ final GValue<Integer> gValue = GValue.ofInteger(123);
+ assertEquals(123, gValue.get().intValue());
+ assertEquals(GType.INTEGER, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromIntegerWithName() {
+ final GValue<Integer> gValue = GValue.ofInteger("varName", 123);
+ assertEquals(123, gValue.get().intValue());
+ assertEquals(GType.INTEGER, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBoolean() {
+ final GValue<Boolean> gValue = GValue.ofBoolean(true);
+ assertEquals(true, gValue.get());
+ assertEquals(GType.BOOLEAN, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBooleanWithName() {
+ final GValue<Boolean> gValue = GValue.ofBoolean("varName", true);
+ assertEquals(true, gValue.get());
+ assertEquals(GType.BOOLEAN, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromDouble() {
+ final GValue<Double> gValue = GValue.ofDouble(123.45);
+ assertEquals(123.45, gValue.get(), 0.0);
+ assertEquals(GType.DOUBLE, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromDoubleWithName() {
+ final GValue<Double> gValue = GValue.ofDouble("varName", 123.45);
+ assertEquals(123.45, gValue.get(), 0.0);
+ assertEquals(GType.DOUBLE, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBigInteger() {
+ final GValue<BigInteger> gValue = GValue.ofBigInteger(BigInteger.ONE);
+ assertEquals(BigInteger.ONE, gValue.get());
+ assertEquals(GType.BIG_INTEGER, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBigIntegerWithName() {
+ final GValue<BigInteger> gValue = GValue.ofBigInteger("varName", BigInteger.ONE);
+ assertEquals(BigInteger.ONE, gValue.get());
+ assertEquals(GType.BIG_INTEGER, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBigDecimal() {
+ final GValue<BigDecimal> gValue = GValue.ofBigDecimal(BigDecimal.ONE);
+ assertEquals(BigDecimal.ONE, gValue.get());
+ assertEquals(GType.BIG_DECIMAL, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBigDecimalWithName() {
+ final GValue<BigDecimal> gValue = GValue.ofBigDecimal("varName", BigDecimal.ONE);
+ assertEquals(BigDecimal.ONE, gValue.get());
+ assertEquals(GType.BIG_DECIMAL, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromLong() {
+ final GValue<Long> gValue = GValue.ofLong(123L);
+ assertEquals(123L, gValue.get().longValue());
+ assertEquals(GType.LONG, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromLongWithName() {
+ final GValue<Long> gValue = GValue.ofLong("varName", 123L);
+ assertEquals(123L, gValue.get().longValue());
+ assertEquals(GType.LONG, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromMap() {
+ final Map<String, String> map = new HashMap<String,String>() {{
+ put("key", "value");
+ }};
+ final GValue<Map> gValue = GValue.ofMap(map);
+ assertEquals(map, gValue.get());
+ assertEquals(GType.MAP, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromMapWithName() {
+ final Map<String, String> map = new HashMap<String,String>() {{
+ put("key", "value");
+ }};
+ final GValue<Map> gValue = GValue.ofMap("varName", map);
+ assertEquals(map, gValue.get());
+ assertEquals(GType.MAP, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromList() {
+ final List<String> list = Arrays.asList("value1", "value2");
+ final GValue<List> gValue = GValue.ofList(list);
+ assertEquals(list, gValue.get());
+ assertEquals(GType.LIST, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromListWithName() {
+ final List<String> list = Arrays.asList("value1", "value2");
+ final GValue<List> gValue = GValue.ofList("varName", list);
+ assertEquals(list, gValue.get());
+ assertEquals(GType.LIST, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromSet() {
+ final Set<String> set = new HashSet<>(Arrays.asList("value1", "value2"));
+ final GValue<Set> gValue = GValue.ofSet(set);
+ assertEquals(set, gValue.get());
+ assertEquals(GType.SET, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromSetWithName() {
+ final Set<String> set = new HashSet<>(Arrays.asList("value1", "value2"));
+ final GValue<Set> gValue = GValue.ofSet("varName", set);
+ assertEquals(set, gValue.get());
+ assertEquals(GType.SET, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromVertex() {
+ final Vertex vertex = mock(Vertex.class);
+ final GValue<Vertex> gValue = GValue.ofVertex(vertex);
+ assertEquals(vertex, gValue.get());
+ assertEquals(GType.VERTEX, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromVertexWithName() {
+ final Vertex vertex = mock(Vertex.class);
+ final GValue<Vertex> gValue = GValue.ofVertex("varName", vertex);
+ assertEquals(vertex, gValue.get());
+ assertEquals(GType.VERTEX, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromEdge() {
+ final Edge edge = mock(Edge.class);
+ final GValue<Edge> gValue = GValue.ofEdge(edge);
+ assertEquals(edge, gValue.get());
+ assertEquals(GType.EDGE, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromEdgeWithName() {
+ final Edge edge = mock(Edge.class);
+ final GValue<Edge> gValue = GValue.ofEdge("varName", edge);
+ assertEquals(edge, gValue.get());
+ assertEquals(GType.EDGE, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromPath() {
+ final Path path = mock(Path.class);
+ final GValue<Path> gValue = GValue.ofPath(path);
+ assertEquals(path, gValue.get());
+ assertEquals(GType.PATH, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromPathWithName() {
+ final Path path = mock(Path.class);
+ final GValue<Path> gValue = GValue.ofPath("varName", path);
+ assertEquals(path, gValue.get());
+ assertEquals(GType.PATH, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromProperty() {
+ final Property property = mock(Property.class);
+ final GValue<Property> gValue = GValue.ofProperty(property);
+ assertEquals(property, gValue.get());
+ assertEquals(GType.PROPERTY, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromPropertyWithName() {
+ final Property property = mock(Property.class);
+ final GValue<Property> gValue = GValue.ofProperty("varName", property);
+ assertEquals(property, gValue.get());
+ assertEquals(GType.PROPERTY, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+}
\ No newline at end of file
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ParametersTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ParametersTest.java
index 27d8e44e16..dd36f4c8b5 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ParametersTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ParametersTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.util;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
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.TraversalParent;
import org.junit.Test;
@@ -332,4 +333,14 @@ public class ParametersTest {
verify(mock).integrateChild(__.outE("knows").asAdmin());
}
+
+ @Test
+ public void shouldGetKeyValuesAndResolveGValues() {
+ final Parameters parameters = new Parameters();
+ parameters.set(null, "a", "axe", "b", GValue.of("B", "bat"), "c", GValue.of("C", "cat"));
+
+ final Object[] params = parameters.getKeyValues(mock(Traverser.Admin.class));
+ assertEquals(6, params.length);
+ assertThat(Arrays.equals(new Object[] {"a", "axe", "b", "bat", "c", "cat"}, params), is(true));
+ }
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
index d799fd2ddf..ad7f10635e 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.translator;
import org.apache.tinkerpop.gremlin.process.traversal.GraphOp;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
@@ -110,6 +111,13 @@ public class JavascriptTranslatorTest {
assertTranslation("Pop.last", Pop.last);
}
+ @Test
+ public void shouldTranslateP() {
+ assertTranslation("P.eq(\"ark\")", P.eq("ark"));
+ assertTranslation("P.within([\"ark\"])", P.within("ark"));
+ assertTranslation("P.neq(\"ark\")", P.neq("ark"));
+ }
+
@Test
public void shouldTranslateTextP() {
assertTranslation("TextP.containing(\"ark\")", TextP.containing("ark"));