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 <[email protected]> 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"));
