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/31 20:16:20 UTC
(tinkerpop) 03/03: wip
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch gvalue
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 3775b6fe71298d6131ed19e9b1cf96d978e486c5
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Wed Jul 31 16:15:58 2024 -0400
wip
---
.../language/grammar/TraversalMethodVisitor.java | 61 +++++++++++++----
.../grammar/TraversalSourceSpawnMethodVisitor.java | 14 +++-
.../traversal/dsl/graph/GraphTraversal.java | 72 +++++++++++++++++++
.../traversal/dsl/graph/GraphTraversalSource.java | 46 +++++++++++++
.../gremlin/process/traversal/step/GType.java | 16 ++++-
.../gremlin/process/traversal/step/GValue.java | 5 +-
.../process/traversal/step/filter/CoinStep.java | 6 +-
.../process/traversal/step/map/CombineStep.java | 11 +--
.../process/traversal/step/map/ConjoinStep.java | 17 +++--
.../process/traversal/step/map/ConstantStep.java | 6 +-
.../process/traversal/step/map/GraphStep.java | 80 +++++++++++++---------
.../process/traversal/step/map/IntersectStep.java | 11 +--
.../process/traversal/step/map/MergeStep.java | 5 +-
.../traversal/step/map/TraversalMergeStep.java | 19 +++--
.../traversal/step/sideEffect/InjectStep.java | 14 ++--
.../process/traversal/step/util/AbstractStep.java | 27 ++++++++
.../process/traversal/util/ListFunction.java | 16 ++---
.../gremlin/process/traversal/step/GValueTest.java | 30 ++++----
.../tinkerpop/gremlin/features/StepDefinition.java | 32 +++++++--
.../apache/tinkerpop/gremlin/features/World.java | 9 +++
.../gremlin/test/features/map/Conjoin.feature | 12 ++++
.../traversal/step/sideEffect/Neo4jGraphStep.java | 4 +-
.../traversal/step/sideEffect/TinkerGraphStep.java | 4 +-
.../tinkerpop/gremlin/tinkergraph/TinkerWorld.java | 5 ++
24 files changed, 399 insertions(+), 123 deletions(-)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
index 4f523c092f..fe500e6210 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
@@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality;
+import java.util.Collection;
import java.util.Map;
import java.util.function.BiFunction;
@@ -40,11 +41,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
/**
* This object is used to append the traversal methods.
*/
- private final GraphTraversal graphTraversal;
+ private final GraphTraversal.Admin graphTraversal;
public TraversalMethodVisitor(final GremlinAntlrToJava antlr, final GraphTraversal graphTraversal) {
super(antlr, graphTraversal);
- this.graphTraversal = graphTraversal;
+ this.graphTraversal = graphTraversal.asAdmin();
}
/**
@@ -92,7 +93,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
*/
@Override
public GraphTraversal visitTraversalMethod_mergeV_Map(final GremlinParser.TraversalMethod_mergeV_MapContext ctx) {
- return this.graphTraversal.mergeV(antlr.argumentVisitor.parseMap(ctx.genericLiteralMapNullableArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralMapNullableArgument(ctx.genericLiteralMapNullableArgument());
+ if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType() == GType.MAP)
+ return graphTraversal.mergeV((GValue<Map<Object, Object>>) literalOrVar);
+ else
+ return graphTraversal.mergeV((Map) literalOrVar);
}
/**
@@ -124,7 +129,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
*/
@Override
public GraphTraversal visitTraversalMethod_mergeE_Map(final GremlinParser.TraversalMethod_mergeE_MapContext ctx) {
- return this.graphTraversal.mergeE(antlr.argumentVisitor.parseMap(ctx.genericLiteralMapNullableArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralMapNullableArgument(ctx.genericLiteralMapNullableArgument());
+ if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType() == GType.MAP)
+ return graphTraversal.mergeE((GValue<Map<Object, Object>>) literalOrVar);
+ else
+ return graphTraversal.mergeE((Map) literalOrVar);
}
/**
@@ -435,7 +444,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
*/
@Override
public GraphTraversal visitTraversalMethod_combine_Object(final GremlinParser.TraversalMethod_combine_ObjectContext ctx) {
- return graphTraversal.combine(antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument());
+ if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
+ return graphTraversal.combine((GValue<Object>) literalOrVar);
+ else
+ return graphTraversal.combine(literalOrVar);
}
/**
@@ -447,9 +460,9 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
if (literalOrVar instanceof Number)
return graphTraversal.coin(((Number) literalOrVar).doubleValue());
else if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isNumeric())
- return graphTraversal.asAdmin().coin((GValue<Double>) literalOrVar);
+ return graphTraversal.coin((GValue<Double>) literalOrVar);
else
- throw new IllegalArgumentException("coin() argument must be a double");
+ throw new IllegalArgumentException("coin argument must be a double");
}
@@ -458,7 +471,13 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
*/
@Override
public GraphTraversal visitTraversalMethod_conjoin_String(final GremlinParser.TraversalMethod_conjoin_StringContext ctx) {
- return graphTraversal.conjoin(antlr.argumentVisitor.parseString(ctx.stringArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(ctx.stringArgument());
+ if (literalOrVar instanceof String)
+ return graphTraversal.conjoin((String) literalOrVar);
+ else if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType() == GType.STRING)
+ return graphTraversal.conjoin((GValue<String>) literalOrVar);
+ else
+ throw new IllegalArgumentException("conjoin argument must be a string");
}
/**
@@ -868,7 +887,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
*/
@Override
public GraphTraversal visitTraversalMethod_intersect_Object(final GremlinParser.TraversalMethod_intersect_ObjectContext ctx) {
- return graphTraversal.intersect(antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument());
+ if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
+ return graphTraversal.intersect((GValue<Object>) literalOrVar);
+ else
+ return graphTraversal.intersect(literalOrVar);
}
/**
@@ -1068,7 +1091,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
*/
@Override
public GraphTraversal visitTraversalMethod_merge_Object(final GremlinParser.TraversalMethod_merge_ObjectContext ctx) {
- return graphTraversal.merge(antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument());
+ if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isCollection())
+ return graphTraversal.merge((GValue<Object>) literalOrVar);
+ else
+ return graphTraversal.merge(literalOrVar);
}
/**
@@ -1108,8 +1135,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
*/
@Override
public GraphTraversal visitTraversalMethod_option_Object_Traversal(final GremlinParser.TraversalMethod_option_Object_TraversalContext ctx) {
- return graphTraversal.option(antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument()),
- antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument());
+ if (literalOrVar instanceof GValue)
+ return graphTraversal.option((GValue) literalOrVar, antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
+ else
+ return graphTraversal.option(literalOrVar, antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
}
/**
@@ -1125,8 +1155,11 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
*/
@Override
public GraphTraversal visitTraversalMethod_option_Merge_Map(final GremlinParser.TraversalMethod_option_Merge_MapContext ctx) {
- return graphTraversal.option(antlr.argumentVisitor.parseMerge(ctx.traversalMergeArgument()),
- (Map) antlr.argumentVisitor.visitGenericLiteralMapNullableArgument(ctx.genericLiteralMapNullableArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralMapNullableArgument(ctx.genericLiteralMapNullableArgument());
+ if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType() == GType.MAP)
+ return graphTraversal.option(antlr.argumentVisitor.parseMerge(ctx.traversalMergeArgument()), (GValue<Map>) literalOrVar);
+ else
+ return graphTraversal.option(antlr.argumentVisitor.parseMerge(ctx.traversalMergeArgument()), (Map) literalOrVar);
}
/**
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
index 2227635f80..8e5ba3ded2 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
@@ -21,6 +21,8 @@ package org.apache.tinkerpop.gremlin.language.grammar;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GType;
import java.util.Map;
@@ -119,7 +121,11 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
*/
@Override
public GraphTraversal visitTraversalSourceSpawnMethod_mergeV_Map(final GremlinParser.TraversalSourceSpawnMethod_mergeV_MapContext ctx) {
- return this.traversalSource.mergeV(antlr.argumentVisitor.parseMap(ctx.genericLiteralMapNullableArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralMapNullableArgument(ctx.genericLiteralMapNullableArgument());
+ if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType() == GType.MAP)
+ return this.traversalSource.asAdmin().mergeV((GValue) literalOrVar);
+ else
+ return this.traversalSource.mergeV((Map) literalOrVar);
}
/**
@@ -143,7 +149,11 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
*/
@Override
public GraphTraversal visitTraversalSourceSpawnMethod_mergeE_Map(final GremlinParser.TraversalSourceSpawnMethod_mergeE_MapContext ctx) {
- return this.traversalSource.mergeE(antlr.argumentVisitor.parseMap(ctx.genericLiteralMapNullableArgument()));
+ final Object literalOrVar = antlr.argumentVisitor.visitGenericLiteralMapNullableArgument(ctx.genericLiteralMapNullableArgument());
+ if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType() == GType.MAP)
+ return this.traversalSource.asAdmin().mergeE((GValue) literalOrVar);
+ else
+ return this.traversalSource.mergeE((Map) literalOrVar);
}
/**
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 30f6a16b9f..7967688a87 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
@@ -459,6 +459,30 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
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 token the token that would trigger this option which may be a {@link Pick}, {@link Merge},
+ * a {@link Traversal}, {@link Predicate}, or object depending on the step being modulated.
+ * @param traversalOption the option as a traversal
+ * @return the traversal with the modulated step
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#choose-step" target="_blank">Reference Documentation - Choose Step</a>
+ * @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.0.0-incubating
+ */
+ public default <M, E2> GraphTraversal<S, E> option(final GValue<M> token, final Traversal<?, E2> traversalOption) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.option, token, traversalOption);
+
+ // handle null similar to how option() with Map handles it, otherwise we get a NPE if this one gets used
+ final Traversal.Admin<E,E2> t = null == traversalOption ?
+ new ConstantTraversal<>(null) : (Traversal.Admin<E, E2>) traversalOption.asAdmin();
+ ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption(token.get(), t);
+ 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
@@ -502,6 +526,54 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
return this;
}
+ /**
+ * Combines the list traverser and list argument. Also known as concatenation or append.
+ *
+ * @return the traversal with an appended {@link CombineStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#combine-step" target="_blank">Reference Documentation - Combine Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, List<?>> combine(final GValue<Object> values) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.combine, values);
+ return this.asAdmin().addStep(new CombineStep<>(this.asAdmin(), values));
+ }
+
+ /**
+ * Joins together the elements of the incoming list traverser together with the provided delimiter.
+ *
+ * @return the traversal with an appended {@link ConjoinStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#conjoin-step" target="_blank">Reference Documentation - Conjoin Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, String> conjoin(final GValue<String> delimiter) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.conjoin, delimiter);
+ return this.asAdmin().addStep(new ConjoinStep<>(this.asAdmin(), delimiter));
+ }
+
+ /**
+ * Calculates the intersection between the list traverser and list argument.
+ *
+ * @return the traversal with an appended {@link IntersectStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#intersect-step" target="_blank">Reference Documentation - Intersect Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Set<?>> intersect(final GValue<Object> values) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.intersect, values);
+ return this.asAdmin().addStep(new IntersectStep<>(this.asAdmin(), values));
+ }
+
+ /**
+ * Merges the list traverser and list argument. Also known as union.
+ *
+ * @return the traversal with an appended {@link TraversalMergeStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#merge-step" target="_blank">Reference Documentation - Merge Step</a>
+ * @since 3.7.1
+ */
+ public default <E2> GraphTraversal<S, E2> merge(final GValue<Object> values) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.merge, values);
+ return this.asAdmin().addStep(new TraversalMergeStep<>(this.asAdmin(), values));
+ }
+
@Override
public default <E2> GraphTraversal.Admin<S, E2> addStep(final Step<?, E2> step) {
return (GraphTraversal.Admin<S, E2>) Traversal.Admin.super.addStep((Step) step);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
index dc15306fce..0c0670a7ca 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
@@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStartStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
@@ -68,6 +69,7 @@ public class GraphTraversalSource implements TraversalSource {
protected final Graph graph;
protected TraversalStrategies strategies;
protected Bytecode bytecode = new Bytecode();
+ protected Admin admin;
////////////////
@@ -100,6 +102,12 @@ public class GraphTraversalSource implements TraversalSource {
this.strategies.addStrategies(new RemoteStrategy(connection));
}
+ public GraphTraversalSource.Admin asAdmin() {
+ if (null == this.admin)
+ this.admin = new Admin();
+ return this.admin;
+ }
+
@Override
public Optional<Class<?>> getAnonymousTraversalClass() {
return Optional.of(__.class);
@@ -624,4 +632,42 @@ public class GraphTraversalSource implements TraversalSource {
return StringFactory.traversalSourceString(this);
}
+
+ /**
+ * This class masks spawn steps that are more reserved for advanced usage.
+ */
+ public class Admin {
+
+ /**
+ * Spawns a {@link GraphTraversal} by doing 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.6.0
+ */
+ public GraphTraversal<Vertex, Vertex> mergeV(final GValue<Map<Object, Object>> searchCreate) {
+ final GraphTraversalSource clone = GraphTraversalSource.this.clone();
+ clone.bytecode.addStep(GraphTraversal.Symbols.mergeV, searchCreate);
+ final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone);
+ return traversal.addStep(new MergeVertexStep(traversal, true, searchCreate));
+ }
+
+ /**
+ * 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 4.0.0
+ */
+ public GraphTraversal<Edge, Edge> mergeE(final GValue<Map<?, Object>> searchCreate) {
+ final GraphTraversalSource clone = GraphTraversalSource.this.clone();
+ clone.bytecode.addStep(GraphTraversal.Symbols.mergeE, searchCreate);
+ final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone);
+ return traversal.addStep(new MergeEdgeStep(traversal, true, searchCreate));
+ }
+
+ }
+
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java
index 89a8b79098..58c1fdfe4d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java
@@ -52,12 +52,26 @@ public enum GType {
GType() {}
/**
- * Returns true if the type is a number.
+ * Returns {@code true} if the type is a number.
*/
public boolean isNumeric() {
return this == INTEGER || this == DOUBLE || this == LONG || this == BIG_INTEGER || this == BIG_DECIMAL;
}
+ /**
+ * Returns {@code true} if the type is a collection.v
+ */
+ public boolean isCollection() {
+ return this == LIST || this == SET;
+ }
+
+ /**
+ * Returns {@code true} if the type is an element.
+ */
+ public boolean isElement() {
+ return this == VERTEX || this == EDGE;
+ }
+
/**
* Convert an object to a matching {@link GType} and if not matched return {@link GType#UNKNOWN}.
*/
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 4de2ca1f8e..e05a9e91f4 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
@@ -66,8 +66,8 @@ public class GValue<V> implements Cloneable, Serializable {
/**
* 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);
+ public String getName() {
+ return this.name;
}
/**
@@ -123,6 +123,7 @@ public class GValue<V> implements Cloneable, Serializable {
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.
*/
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 e220952200..f9cfda6399 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
@@ -58,14 +58,10 @@ public final class CoinStep<S> extends FilterStep<S> implements Seedable {
random.setSeed(seed);
}
- public GValue<Double> getProbabilityGValue() {
+ public GValue<Double> getProbability() {
return probability;
}
- public double getProbability() {
- return this.probability.get();
- }
-
@Override
protected boolean filter(final Traverser.Admin<S> traverser) {
long newBulk = 0l;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CombineStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CombineStep.java
index ccf601acab..587b7a7c72 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CombineStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CombineStep.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.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.process.traversal.util.ListFunction;
@@ -37,7 +38,7 @@ import java.util.Set;
*/
public final class CombineStep<S, E> extends ScalarMapStep<S, List<?>> implements TraversalParent, ListFunction {
private Traversal.Admin<S, E> valueTraversal;
- private Object parameterItems;
+ private GValue<Object> parameterItems;
public CombineStep(final Traversal.Admin traversal, final Object values) {
super(traversal);
@@ -45,7 +46,7 @@ public final class CombineStep<S, E> extends ScalarMapStep<S, List<?>> implement
if (values instanceof Traversal) {
valueTraversal = integrateChild(((Traversal<S, E>) values).asAdmin());
} else {
- parameterItems = values;
+ parameterItems = values instanceof GValue ? (GValue<Object>) values : GValue.of(values);
}
}
@@ -53,9 +54,9 @@ public final class CombineStep<S, E> extends ScalarMapStep<S, List<?>> implement
public String getStepName() { return "combine"; }
@Override
- protected List<?> map(Traverser.Admin<S> traverser) {
+ protected List<?> map(final Traverser.Admin<S> traverser) {
final Collection listA = convertTraverserToCollection(traverser);
- final Collection listB = (null != valueTraversal) ? convertTraversalToCollection(traverser, valueTraversal) : convertArgumentToCollection(parameterItems);
+ final Collection listB = (null != valueTraversal) ? convertTraversalToCollection(traverser, valueTraversal) : convertArgumentToCollection(parameterItems.get());
final List combined = new ArrayList(listA);
combined.addAll(listB);
@@ -68,7 +69,7 @@ public final class CombineStep<S, E> extends ScalarMapStep<S, List<?>> implement
return (null == valueTraversal) ? Collections.emptyList() : Collections.singletonList(valueTraversal);
}
- public Object getParameterItems() {
+ public GValue<Object> getParameterItems() {
return parameterItems;
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConjoinStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConjoinStep.java
index ce6818b473..94457df007 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConjoinStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConjoinStep.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.process.traversal.util.ListFunction;
@@ -34,11 +35,15 @@ import java.util.Set;
* A map step that returns the result of joining every element in the traverser using the delimiter argument.
*/
public final class ConjoinStep<S> extends ScalarMapStep<S, String> implements ListFunction {
- private String delimiter;
+ private GValue<String> delimiter;
public ConjoinStep(final Traversal.Admin traversal, final String delimiter) {
+ this(traversal, GValue.of(delimiter));
+ }
+
+ public ConjoinStep(final Traversal.Admin traversal, final GValue<String> delimiter) {
super(traversal);
- if (null == delimiter) { throw new IllegalArgumentException("Input delimiter to conjoin step can't be null."); }
+ if (null == delimiter || null == delimiter.get()) { throw new IllegalArgumentException("Input delimiter to conjoin step can't be null."); }
this.delimiter = delimiter;
}
@@ -46,7 +51,7 @@ public final class ConjoinStep<S> extends ScalarMapStep<S, String> implements Li
public String getStepName() { return "conjoin"; }
@Override
- protected String map(Traverser.Admin<S> traverser) {
+ protected String map(final Traverser.Admin<S> traverser) {
final Collection elements = convertTraverserToCollection(traverser);
if (elements.isEmpty()) { return ""; }
@@ -54,19 +59,19 @@ public final class ConjoinStep<S> extends ScalarMapStep<S, String> implements Li
for (Object elem : elements) {
if (elem != null) {
- joinResult.append(String.valueOf(elem)).append(delimiter);
+ joinResult.append(elem).append(delimiter.get());
}
}
if (joinResult.length() != 0) {
- joinResult.delete(joinResult.length() - delimiter.length(), joinResult.length());
+ joinResult.delete(joinResult.length() - delimiter.get().length(), joinResult.length());
return joinResult.toString();
} else {
return null;
}
}
- public String getDelimiter() {
+ public GValue<String> getDelimiter() {
return this.delimiter;
}
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 bf554d1aa9..245a443e5a 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
@@ -41,11 +41,7 @@ public class ConstantStep<S, E> extends ScalarMapStep<S, E> {
this.constant = null == constant ? GValue.of(null) : constant;
}
- public E getConstant() {
- return this.constant.get();
- }
-
- public GValue<E> getConstantGValue() {
+ public GValue<E> getConstant() {
return this.constant;
}
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 ee151aed12..5c03500d6e 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
@@ -44,6 +44,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Supplier;
/**
@@ -54,7 +55,7 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
protected Parameters parameters = new Parameters();
protected final Class<E> returnClass;
- protected Object[] ids;
+ protected GValue<?>[] ids;
protected transient Supplier<Iterator<E>> iteratorSupplier;
protected boolean isStart;
protected boolean done = false;
@@ -65,30 +66,42 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
public GraphStep(final Traversal.Admin traversal, final Class<E> returnClass, final boolean isStart, final Object... ids) {
super(traversal);
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;
+
+ // if ids is a single collection like g.V(['a','b','c']), then unroll it into an array of ids
+ this.ids = convertToGValues(tryUnrollSingleCollectionArgument(ids));
+
this.isStart = isStart;
this.iteratorSupplier = () -> (Iterator<E>) (Vertex.class.isAssignableFrom(this.returnClass) ?
- this.getTraversal().getGraph().get().vertices(convertGValuesToIds()) :
- this.getTraversal().getGraph().get().edges(convertGValuesToIds()));
+ this.getTraversal().getGraph().get().vertices(resolveToValues(this.ids)) :
+ this.getTraversal().getGraph().get().edges(resolveToValues(this.ids)));
}
/**
- * Converts {@link GValue} objects the ids array to their values to prevent them from leaking to the Graph API.
+ * Unrolls a single collection argument into an array of ids. This is useful for steps like
+ * {@code g.V(['a','b','c'])}.
*/
- 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];
- }
+ protected static Object[] tryUnrollSingleCollectionArgument(final Object[] ids) {
+ final Object[] tempIds;
+ if (ids != null && ids.length == 1) {
+ final Optional<Collection> opt;
+ if (ids[0] instanceof GValue && ((GValue<?>) ids[0]).getType().isCollection())
+ opt = Optional.of((Collection) ((GValue) ids[0]).get());
+ else if (ids[0] instanceof Collection)
+ opt = Optional.of((Collection) ids[0]);
+ else
+ opt = Optional.empty();
+
+ if (opt.isPresent()) {
+ tempIds = opt.get().toArray(new Object[opt.get().size()]);
+ } else
+ tempIds = ids;
+ } else {
+ tempIds = ids;
}
- return newIds;
+ return tempIds;
}
-
public String toString() {
return StringFactory.stepString(this, this.returnClass.getSimpleName().toLowerCase(), Arrays.toString(this.ids));
}
@@ -127,24 +140,28 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
this.iteratorSupplier = iteratorSupplier;
}
- public Object[] getIds() {
+ /**
+ * Get the ids associated with this step. If there are {@link GValue} objects present they will be returned
+ * alongside literal ids. Prefer {@link #getResolvedIds()} if you prefer to work with literal ids only.
+ */
+ public GValue[] getIds() {
return this.ids;
}
+ /**
+ * Gets the ids associated with this step as literal values rather than {@link GValue} objects.
+ */
+ public Object[] getResolvedIds() {
+ return resolveToValues(this.ids);
+ }
+
public void addIds(final Object... newIds) {
- if ((this.ids == null || this.ids.length == 0) &&
- newIds.length == 1 &&
- newIds[0] instanceof Collection && ((Collection) newIds[0]).isEmpty())
- this.ids = null;
- else
- this.ids = ArrayUtils.addAll(this.ids,
- (newIds.length == 1 && newIds[0] instanceof Collection) ?
- ((Collection) newIds[0]).toArray(new Object[((Collection) newIds[0]).size()]) :
- newIds);
+ final GValue[] gvalues = convertToGValues(tryUnrollSingleCollectionArgument(newIds));
+ this.ids = ArrayUtils.addAll(this.ids, gvalues);
}
public void clearIds() {
- this.ids = new Object[0];
+ this.ids = new GValue[0];
}
@Override
@@ -157,13 +174,10 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
if (null != this.ids) {
// 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();
+ final GValue<?> current = this.ids[i];
+ if (current.getType().isElement()) {
+ this.ids[i] = GValue.of(current.getName(), ((Element) current.get()).id());
+ }
}
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IntersectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IntersectStep.java
index 651dec7502..93e5885e18 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IntersectStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IntersectStep.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.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.process.traversal.util.ListFunction;
@@ -36,7 +37,7 @@ import java.util.Set;
*/
public final class IntersectStep<S, E> extends ScalarMapStep<S, Set<?>> implements TraversalParent, ListFunction {
private Traversal.Admin<S, E> valueTraversal;
- private Object parameterItems;
+ private GValue<Object> parameterItems;
public IntersectStep(final Traversal.Admin traversal, final Object values) {
super(traversal);
@@ -44,7 +45,7 @@ public final class IntersectStep<S, E> extends ScalarMapStep<S, Set<?>> implemen
if (values instanceof Traversal) {
valueTraversal = integrateChild(((Traversal<S, E>) values).asAdmin());
} else {
- parameterItems = values;
+ parameterItems = values instanceof GValue ? (GValue<Object>) values : GValue.of(values);
}
}
@@ -52,7 +53,7 @@ public final class IntersectStep<S, E> extends ScalarMapStep<S, Set<?>> implemen
return this.valueTraversal;
}
- public Object getParameterItems() {
+ public GValue<Object> getParameterItems() {
return this.parameterItems;
}
@@ -60,9 +61,9 @@ public final class IntersectStep<S, E> extends ScalarMapStep<S, Set<?>> implemen
public String getStepName() { return "intersect"; }
@Override
- protected Set<?> map(Traverser.Admin<S> traverser) {
+ protected Set<?> map(final Traverser.Admin<S> traverser) {
final Set setA = convertTraverserToSet(traverser);
- final Collection setB = (null != valueTraversal) ? convertTraversalToCollection(traverser, this.valueTraversal) : convertArgumentToCollection(parameterItems);
+ final Collection setB = (null != valueTraversal) ? convertTraversalToCollection(traverser, this.valueTraversal) : convertArgumentToCollection(parameterItems.get());
final Set intersection = new HashSet();
for (Object element : setB) {
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 e4649724af..5020b27410 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
@@ -332,8 +332,9 @@ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E>
/**
* null Map == empty Map
*/
- protected Map materializeMap(final Traverser.Admin<S> traverser, Traversal.Admin<S, ?> mapTraversal) {
- Map map = (Map) TraversalUtil.apply(traverser, mapTraversal);
+ protected Map materializeMap(final Traverser.Admin<S> traverser, final Traversal.Admin<S, ?> mapTraversal) {
+ final Object o = TraversalUtil.apply(traverser, mapTraversal);
+ Map map = o instanceof GValue ? (Map) ((GValue) o).get() : (Map) o;
// PartitionStrategy uses parameters as a mechanism for setting the partition key. trying to be as specific
// as possible here wrt parameters usage to avoid misuse
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalMergeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalMergeStep.java
index 3e339482b7..74d5c185de 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalMergeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalMergeStep.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.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.process.traversal.util.ListFunction;
@@ -40,7 +41,7 @@ import java.util.Set;
*/
public final class TraversalMergeStep<S, E> extends ScalarMapStep<S, E> implements TraversalParent, ListFunction {
private Traversal.Admin<S, E> valueTraversal;
- private Object parameterItems;
+ private GValue<Object> parameterItems;
public TraversalMergeStep(final Traversal.Admin traversal, final Object values) {
super(traversal);
@@ -48,10 +49,18 @@ public final class TraversalMergeStep<S, E> extends ScalarMapStep<S, E> implemen
if (values instanceof Traversal) {
valueTraversal = integrateChild(((Traversal<S, E>) values).asAdmin());
} else {
- parameterItems = values;
+ parameterItems = values instanceof GValue ? (GValue<Object>) values : GValue.of(values);
}
}
+ public Traversal.Admin<S, E> getValueTraversal() {
+ return valueTraversal;
+ }
+
+ public GValue<Object> getParameterItems() {
+ return parameterItems;
+ }
+
@Override
public String getStepName() { return "merge"; }
@@ -61,7 +70,7 @@ public final class TraversalMergeStep<S, E> extends ScalarMapStep<S, E> implemen
final Map mapA = (incoming instanceof Map) ? (Map) incoming : null;
if (mapA != null) {
- final Object mapB = (valueTraversal != null) ? TraversalUtil.apply(traverser, valueTraversal) : parameterItems;
+ final Object mapB = (valueTraversal != null) ? TraversalUtil.apply(traverser, valueTraversal) : parameterItems.get();
if (!(mapB instanceof Map)) {
throw new IllegalArgumentException(
String.format(
@@ -76,13 +85,13 @@ public final class TraversalMergeStep<S, E> extends ScalarMapStep<S, E> implemen
} else {
final Collection listA = convertTraverserToCollection(traverser);
- if (parameterItems instanceof Map) {
+ if (parameterItems.get() instanceof Map) {
throw new IllegalArgumentException(getStepName() + " step type mismatch: expected argument to be Iterable but got Map");
}
final Collection listB =
(null != valueTraversal)
? convertTraversalToCollection(traverser, valueTraversal)
- : convertArgumentToCollection(parameterItems);
+ : convertArgumentToCollection(parameterItems.get());
final Set elements = new HashSet();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java
index 0502f18541..ab57c05ce5 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.util.iterator.ArrayIterator;
/**
@@ -26,13 +27,13 @@ import org.apache.tinkerpop.gremlin.util.iterator.ArrayIterator;
*/
public final class InjectStep<S> extends StartStep<S> {
- private final S[] injections;
+ private final GValue<S>[] injections;
@SafeVarargs
public InjectStep(final Traversal.Admin traversal, final S... injections) {
super(traversal);
- this.injections = injections;
- this.start = new ArrayIterator<>(this.injections);
+ this.injections = convertToGValues(injections);
+ this.start = new ArrayIterator<>(resolveToValues(this.injections));
}
@Override
@@ -45,10 +46,13 @@ public final class InjectStep<S> extends StartStep<S> {
@Override
public void reset() {
super.reset();
- this.start = new ArrayIterator<>(this.injections);
+ this.start = new ArrayIterator<>(resolveToValues(this.injections));
}
- public S[] getInjections() {
+ /**
+ * Get the injections of the step.
+ */
+ public GValue<S>[] getInjections() {
return this.injections;
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/AbstractStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/AbstractStep.java
index d4545ead5c..15ddacdc91 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/AbstractStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/AbstractStep.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.util;
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.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
import org.apache.tinkerpop.gremlin.process.traversal.util.EmptyTraversal;
@@ -33,6 +34,7 @@ import java.util.LinkedHashSet;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Stream;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -229,4 +231,29 @@ public abstract class AbstractStep<S, E> implements Step<S, E> {
return traverser;
}
+ /**
+ * The elements in object array argument are examined to see if they are {@link GValue} objects. If they are, they
+ * are preserved as is. If they are not then they are wrapped in a {@link GValue} object.
+ */
+ protected static <T> GValue<T>[] convertToGValues(final Object[] args) {
+ return Stream.of(args).map(id -> {
+ if (id instanceof GValue)
+ return (GValue<?>) id;
+ else
+ return GValue.of(id);
+ }).toArray(GValue[]::new);
+ }
+
+ /**
+ * Converts {@link GValue} objects the argument array to their values to prevent them from leaking to the Graph API.
+ * Providers extending from this step should use this method to get actual values to prevent any {@link GValue}
+ * objects from leaking to the Graph API.
+ */
+ protected Object[] resolveToValues(final GValue<?>[] gvalues) {
+ final Object[] newIds = new Object[gvalues.length];
+ for (int i = 0; i < gvalues.length; i++) {
+ newIds[i] = gvalues[i].get();
+ }
+ return newIds;
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ListFunction.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ListFunction.java
index ab693b9e29..5bf91c41e8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ListFunction.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ListFunction.java
@@ -39,7 +39,7 @@ public interface ListFunction {
* @param iterable an Iterable or array.
* @return The iterable type as a Collection or null if argument isn't iterable.
*/
- public static Collection asCollection(Object iterable) {
+ public static Collection asCollection(final Object iterable) {
if (iterable instanceof Collection) {
return (Collection) iterable;
} else if ((iterable != null && iterable.getClass().isArray()) || iterable instanceof Iterable) {
@@ -56,7 +56,7 @@ public interface ListFunction {
* @param iterable an Iterable or array.
* @return The iterable type as a Collection or null if argument isn't iterable.
*/
- public static Set asSet(Object iterable) {
+ public static Set asSet(final Object iterable) {
if (iterable instanceof Iterable || iterable != null && iterable.getClass().isArray()) {
return IteratorUtils.asSet(iterable);
} else {
@@ -71,7 +71,7 @@ public interface ListFunction {
*/
public String getStepName();
- public default Collection convertArgumentToCollection(Object arg) {
+ public default Collection convertArgumentToCollection(final Object arg) {
if (null == arg) {
throw new IllegalArgumentException(
String.format("Argument provided for %s step can't be null.", getStepName()));
@@ -90,7 +90,7 @@ public interface ListFunction {
return incoming;
}
- public default <S> Collection convertTraverserToCollection(Traverser.Admin<S> traverser) {
+ public default <S> Collection convertTraverserToCollection(final Traverser.Admin<S> traverser) {
final S items = traverser.get();
if (null == items) {
@@ -111,7 +111,7 @@ public interface ListFunction {
return incoming;
}
- public default <S, E> Collection convertTraversalToCollection(Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
+ public default <S, E> Collection convertTraversalToCollection(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
final Object array = TraversalUtil.apply(traverser, traversal);
if (null == array) {
@@ -134,7 +134,7 @@ public interface ListFunction {
return input;
}
- public default Set convertArgumentToSet(Object arg) {
+ public default Set convertArgumentToSet(final Object arg) {
if (null == arg) {
throw new IllegalArgumentException(
String.format("Argument provided for %s step can't be null.", getStepName()));
@@ -153,7 +153,7 @@ public interface ListFunction {
return incoming;
}
- public default <S> Set convertTraverserToSet(Traverser.Admin<S> traverser) {
+ public default <S> Set convertTraverserToSet(final Traverser.Admin<S> traverser) {
final S items = traverser.get();
if (null == items) {
@@ -174,7 +174,7 @@ public interface ListFunction {
return incoming;
}
- public default <S, E> Set convertTraversalToSet(Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
+ public default <S, E> Set convertTraversalToSet(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
final Object array = TraversalUtil.apply(traverser, traversal);
if (null == array) {
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
index fcfac9b9d9..d1742e3e29 100644
--- 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
@@ -54,7 +54,7 @@ public class GValueTest {
final GValue<Integer> gValue = GValue.of("varName", 123);
assertEquals(123, gValue.get().intValue());
assertEquals(GType.INTEGER, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -71,7 +71,7 @@ public class GValueTest {
final GValue<String> gValue = GValue.ofString("varName", "test");
assertEquals("test", gValue.get());
assertEquals(GType.STRING, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -88,7 +88,7 @@ public class GValueTest {
final GValue<Integer> gValue = GValue.ofInteger("varName", 123);
assertEquals(123, gValue.get().intValue());
assertEquals(GType.INTEGER, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -105,7 +105,7 @@ public class GValueTest {
final GValue<Boolean> gValue = GValue.ofBoolean("varName", true);
assertEquals(true, gValue.get());
assertEquals(GType.BOOLEAN, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -122,7 +122,7 @@ public class GValueTest {
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());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -139,7 +139,7 @@ public class GValueTest {
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());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -156,7 +156,7 @@ public class GValueTest {
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());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -173,7 +173,7 @@ public class GValueTest {
final GValue<Long> gValue = GValue.ofLong("varName", 123L);
assertEquals(123L, gValue.get().longValue());
assertEquals(GType.LONG, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -196,7 +196,7 @@ public class GValueTest {
final GValue<Map> gValue = GValue.ofMap("varName", map);
assertEquals(map, gValue.get());
assertEquals(GType.MAP, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -215,7 +215,7 @@ public class GValueTest {
final GValue<List> gValue = GValue.ofList("varName", list);
assertEquals(list, gValue.get());
assertEquals(GType.LIST, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -234,7 +234,7 @@ public class GValueTest {
final GValue<Set> gValue = GValue.ofSet("varName", set);
assertEquals(set, gValue.get());
assertEquals(GType.SET, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -253,7 +253,7 @@ public class GValueTest {
final GValue<Vertex> gValue = GValue.ofVertex("varName", vertex);
assertEquals(vertex, gValue.get());
assertEquals(GType.VERTEX, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -272,7 +272,7 @@ public class GValueTest {
final GValue<Edge> gValue = GValue.ofEdge("varName", edge);
assertEquals(edge, gValue.get());
assertEquals(GType.EDGE, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -291,7 +291,7 @@ public class GValueTest {
final GValue<Path> gValue = GValue.ofPath("varName", path);
assertEquals(path, gValue.get());
assertEquals(GType.PATH, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
@@ -310,7 +310,7 @@ public class GValueTest {
final GValue<Property> gValue = GValue.ofProperty("varName", property);
assertEquals(property, gValue.get());
assertEquals(GType.PROPERTY, gValue.getType());
- assertEquals("varName", gValue.getName().get());
+ assertEquals("varName", gValue.getName());
assertThat(gValue.isVariable(), is(true));
}
}
\ No newline at end of file
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
index 27f36888cf..380313e29a 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
@@ -32,6 +32,7 @@ import org.antlr.v4.runtime.CommonTokenStream;
import org.apache.tinkerpop.gremlin.language.grammar.GremlinAntlrToJava;
import org.apache.tinkerpop.gremlin.language.grammar.GremlinLexer;
import org.apache.tinkerpop.gremlin.language.grammar.GremlinParser;
+import org.apache.tinkerpop.gremlin.language.grammar.VariableResolver;
import org.apache.tinkerpop.gremlin.language.translator.GremlinTranslator;
import org.apache.tinkerpop.gremlin.language.translator.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Merge;
@@ -92,7 +93,7 @@ public final class StepDefinition {
private World world;
private GraphTraversalSource g;
- private final Map<String, String> stringParameters = new HashMap<>();
+ private final Map<String, Object> stringParameters = new HashMap<>();
private Traversal traversal;
private Object result;
private Throwable error;
@@ -285,14 +286,26 @@ public final class StepDefinition {
@Given("using the parameter {word} defined as {string}")
public void usingTheParameterXDefinedAsX(final String key, final String value) {
- stringParameters.put(key, convertToString(value));
+ // when parameters are used literally, they are converted to string representations that can be recognized by
+ // the grammar and parsed inline. when they are used as variables, they are converted to objects that can be
+ // applied to the script as variables.
+ if (world.useParametersLiterally())
+ stringParameters.put(key, convertToString(value));
+ else
+ stringParameters.put(key, convertToObject(value));
}
@Given("the traversal of")
public void theTraversalOf(final String docString) {
try {
- final String gremlin = tryUpdateDataFilePath(docString);
- traversal = parseGremlin(applyParameters(gremlin));
+ final String rawGremlin = tryUpdateDataFilePath(docString);
+
+ // when parameters are used literally, they are converted to string representations that can be recognized by
+ // the grammar and parsed inline. when they are used as variables, they are converted to objects that can be
+ // applied to the script as variables.
+ final String gremlin = world.useParametersLiterally() ? applyParameters(rawGremlin) : rawGremlin;
+
+ traversal = parseGremlin(gremlin);
} catch (Exception ex) {
ex.printStackTrace();
error = ex;
@@ -447,7 +460,14 @@ public final class StepDefinition {
final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString(normalizedGremlin));
final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer));
final GremlinParser.QueryContext ctx = parser.query();
- return (Traversal) new GremlinAntlrToJava(g).visitQuery(ctx);
+
+ // when parameters are used literally, they are converted to string representations that can be recognized by
+ // the grammar and parsed inline. when they are used as variables, they are converted to objects that can be
+ // applied to the script as variables.
+ if (world.useParametersLiterally())
+ return (Traversal) new GremlinAntlrToJava(g).visitQuery(ctx);
+ else
+ return (Traversal) new GremlinAntlrToJava(g, new VariableResolver.DefaultVariableResolver(this.stringParameters)).visitQuery(ctx);
}
private List<Object> translateResultsToActual() {
@@ -571,7 +591,7 @@ public final class StepDefinition {
final List<String> paramNames = new ArrayList<>(stringParameters.keySet());
paramNames.sort((a,b) -> b.length() - a.length());
for (String k : paramNames) {
- replaced = replaced.replace(k, stringParameters.get(k));
+ replaced = replaced.replace(k, stringParameters.get(k).toString());
}
return replaced;
}
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/World.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/World.java
index 30e6daadb2..2dc31751da 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/World.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/World.java
@@ -81,4 +81,13 @@ public interface World {
public default String convertIdToScript(final Object id, final Class<? extends Element> type) {
return id.toString();
}
+
+ /**
+ * Determines if the test should use parameters literally or treat them as variables to be applied to the script.
+ * By default, they are treated literally.
+ */
+ public default boolean useParametersLiterally() {
+ return true;
+ }
+
}
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Conjoin.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Conjoin.feature
index 9d9d54547e..4c9cdb483b 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Conjoin.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Conjoin.feature
@@ -70,6 +70,18 @@ Feature: Step - conjoin()
| result |
| 27;29;32;35 |
+ Scenario: g_V_valuesXageX_order_fold_conjoinX_X
+ Given the modern graph
+ And using the parameter xx1 defined as "/"
+ And the traversal of
+ """
+ g.V().values("age").order().fold().conjoin(xx1)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | 27/29/32/35 |
+
@GraphComputerVerificationReferenceOnly
Scenario: g_V_out_path_byXvaluesXnameX_toUpperX_conjoinXMARKOX
Given the modern graph
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java
index 7ab286543c..abd0a6b801 100644
--- a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java
@@ -66,7 +66,7 @@ public final class Neo4jGraphStep<S, E extends Element> extends GraphStep<S, E>
private Iterator<? extends Edge> edges() {
if (null == this.ids)
return Collections.emptyIterator();
- return IteratorUtils.filter(this.getTraversal().getGraph().get().edges(this.ids), edge -> HasContainer.testAll(edge, this.hasContainers));
+ return IteratorUtils.filter(this.getTraversal().getGraph().get().edges(this.getResolvedIds()), edge -> HasContainer.testAll(edge, this.hasContainers));
}
private Iterator<? extends Vertex> vertices() {
@@ -76,7 +76,7 @@ public final class Neo4jGraphStep<S, E extends Element> extends GraphStep<S, E>
// ids are present, filter on them first
if (ids.length > 0)
- return IteratorUtils.filter(graph.vertices(ids), vertex -> HasContainer.testAll(vertex, hasContainers));
+ return IteratorUtils.filter(graph.vertices(this.getResolvedIds()), vertex -> HasContainer.testAll(vertex, hasContainers));
////// do index lookups //////
graph.tx().readWrite();
// get a label being search on
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java
index 6a3e965629..e529d78390 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java
@@ -75,7 +75,7 @@ public final class TinkerGraphStep<S, E extends Element> extends GraphStep<S, E>
if (null == this.ids)
iterator = Collections.emptyIterator();
else if (this.ids.length > 0)
- iterator = this.iteratorList(graph.edges(this.ids));
+ iterator = this.iteratorList(graph.edges(this.getResolvedIds()));
else
iterator = null == indexedContainer ?
this.iteratorList(graph.edges()) :
@@ -97,7 +97,7 @@ public final class TinkerGraphStep<S, E extends Element> extends GraphStep<S, E>
if (null == this.ids)
iterator = Collections.emptyIterator();
else if (this.ids.length > 0)
- iterator = this.iteratorList(graph.vertices(this.ids));
+ iterator = this.iteratorList(graph.vertices(this.getResolvedIds()));
else
iterator = (null == indexedContainer ?
this.iteratorList(graph.vertices()) :
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java
index 329a07fbae..52fd414164 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java
@@ -109,6 +109,11 @@ public abstract class TinkerWorld implements World {
public AbstractTinkerGraph open(final Configuration configuration) {
return TinkerGraph.open(configuration);
}
+
+ @Override
+ public boolean useParametersLiterally() {
+ return false;
+ }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////