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 2022/01/21 12:59:52 UTC
[tinkerpop] 01/03: wip
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch TINKERPOP-2681
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 7b9ef8983d2ccc0df48a0981cfcb2fa5a8b6855c
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Wed Jan 12 10:12:16 2022 -0500
wip
---
docs/src/dev/developer/for-committers.asciidoc | 1 +
.../tinkerpop/gremlin/jsr223/CoreImports.java | 7 +-
.../language/grammar/GenericLiteralVisitor.java | 17 +-
.../language/grammar/GremlinBaseVisitor.java | 55 ++++
.../language/grammar/TraversalMethodVisitor.java | 39 +++
.../grammar/TraversalSourceSpawnMethodVisitor.java | 22 ++
.../tinkerpop/gremlin/process/traversal/Merge.java | 46 +++
.../{step/TraversalOptionParent.java => Pick.java} | 13 +-
.../gremlin/process/traversal/Translator.java | 9 +-
.../traversal/dsl/graph/GraphTraversal.java | 102 ++++++-
.../traversal/dsl/graph/GraphTraversalSource.java | 82 ++++-
.../gremlin/process/traversal/dsl/graph/__.java | 30 +-
.../traversal/step/TraversalOptionParent.java | 11 +-
.../process/traversal/step/branch/BranchStep.java | 3 +-
.../process/traversal/step/branch/ChooseStep.java | 9 +-
.../process/traversal/step/branch/UnionStep.java | 10 +-
.../process/traversal/step/map/MergeEdgeStep.java | 334 +++++++++++++++++++++
.../traversal/step/map/MergeVertexStep.java | 287 ++++++++++++++++++
.../traversal/translator/DotNetTranslator.java | 5 +-
.../traversal/translator/GroovyTranslator.java | 6 +-
.../traversal/translator/JavascriptTranslator.java | 4 +-
.../traversal/translator/PythonTranslator.java | 4 +-
.../io/binary/TypeSerializerRegistry.java | 4 +-
.../structure/io/binary/types/EnumSerializer.java | 4 +-
.../structure/io/graphson/GraphSONModule.java | 14 +-
.../io/graphson/GraphSONTypeSerializerV2d0.java | 6 +-
.../io/graphson/GraphSONTypeSerializerV3d0.java | 6 +-
.../gremlin/structure/io/gryo/GryoVersion.java | 6 +-
.../language/grammar/TraversalEnumParserTest.java | 4 +-
.../traversal/step/branch/BranchStepTest.java | 2 +-
.../traversal/step/branch/ChooseStepTest.java | 2 +-
gremlin-dotnet/build/generate.groovy | 10 +
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 24 ++
.../GraphBinaryReaderWriterRoundTripTest.java | 4 +-
.../ast/VarAsBindingASTTransformation.groovy | 7 +-
.../jsr223/GremlinGroovyScriptEngineTest.java | 9 +-
gremlin-javascript/build/generate.groovy | 11 +
.../lib/process/graph-traversal.js | 22 ++
.../gremlin-javascript/lib/process/traversal.js | 1 +
.../gremlin-javascript/test/cucumber/gremlin.js | 25 ++
gremlin-language/src/main/antlr4/Gremlin.g4 | 33 ++
.../language/corpus/DocumentationReader.java | 2 +
.../language/grammar/ReferenceGrammarTest.java | 16 +-
gremlin-python/build/generate.groovy | 10 +
gremlin-python/src/main/python/radish/gremlin.py | 24 ++
gremlin-test/features/map/MergeEdge.feature | 184 ++++++++++++
gremlin-test/features/map/MergeVertex.feature | 265 ++++++++++++++++
gremlin-test/features/sideEffect/Group.feature | 2 +-
.../tinkerpop/gremlin/features/StepDefinition.java | 27 +-
.../process/ProcessLimitedStandardSuite.java | 56 ----
.../process/traversal/step/branch/BranchTest.java | 4 +-
.../process/traversal/step/branch/ChooseTest.java | 6 +-
.../decoration/EventStrategyProcessTest.java | 62 ++++
.../tinkerpop/gremlin/structure/io/Model.java | 4 +-
.../_3_2_3/manual-graphson-generator.groovy | 5 +-
.../_3_2_4/manual-graphson-generator.groovy | 5 +-
.../io/gryo/_3_2_3/manual-gryo-generator.groovy | 8 +-
.../io/gryo/_3_2_4/manual-gryo-generator.groovy | 8 +-
.../traversal/step/map/TinkerMergeVertexStep.java | 87 ++++++
.../TinkerMergeVertexStepStrategy.java | 53 ++++
.../gremlin/tinkergraph/structure/TinkerGraph.java | 4 +-
61 files changed, 1953 insertions(+), 169 deletions(-)
diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc
index 20486b0..510c7e6 100644
--- a/docs/src/dev/developer/for-committers.asciidoc
+++ b/docs/src/dev/developer/for-committers.asciidoc
@@ -517,6 +517,7 @@ tag when one is necessary will cause provider tests to fail:
* `@AllowNullPropertyValues` - The scenario requires that the graph be configured with `AllowNullPropertyValues` as
`true` (meaning that it can store `null` values).
+* `@AllowUserSuppliedIds` - The scenario requires that the graph be configured with `UserSuppliedIds` as `true`.
* `@GraphComputerVerificationInjectionNotSupported` - The scenario will not work on with `GraphComputer` because the
`inject()` step is not supported.
* `@GraphComputerVerificationMidVNotSupported` - The scenario will not work on with `GraphComputer` because the
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
index 194a192..67be548 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
@@ -55,9 +55,11 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.verifica
import org.apache.tinkerpop.gremlin.process.remote.RemoteConnection;
import org.apache.tinkerpop.gremlin.process.traversal.Bindings;
import org.apache.tinkerpop.gremlin.process.traversal.IO;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
@@ -194,13 +196,14 @@ public final class CoreImports {
CLASS_IMPORTS.add(VertexProperty.Cardinality.class);
CLASS_IMPORTS.add(Column.class);
CLASS_IMPORTS.add(Direction.class);
+ CLASS_IMPORTS.add(Merge.class);
CLASS_IMPORTS.add(Operator.class);
CLASS_IMPORTS.add(Order.class);
CLASS_IMPORTS.add(Pop.class);
CLASS_IMPORTS.add(Scope.class);
CLASS_IMPORTS.add(T.class);
CLASS_IMPORTS.add(TraversalOptionParent.class);
- CLASS_IMPORTS.add(TraversalOptionParent.Pick.class);
+ CLASS_IMPORTS.add(Pick.class);
CLASS_IMPORTS.add(P.class);
CLASS_IMPORTS.add(TextP.class);
CLASS_IMPORTS.add(WithOptions.class);
@@ -349,7 +352,7 @@ public final class CoreImports {
Collections.addAll(ENUM_IMPORTS, Pop.values());
Collections.addAll(ENUM_IMPORTS, Scope.values());
Collections.addAll(ENUM_IMPORTS, T.values());
- Collections.addAll(ENUM_IMPORTS, TraversalOptionParent.Pick.values());
+ Collections.addAll(ENUM_IMPORTS, Pick.values());
}
private CoreImports() {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
index 336eecb..5aea5e0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
@@ -20,9 +20,9 @@ package org.apache.tinkerpop.gremlin.language.grammar;
import org.antlr.v4.runtime.tree.ParseTree;
import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
@@ -33,6 +33,7 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -81,6 +82,13 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
}
/**
+ * Parse a map literal context and return the map literal
+ */
+ public static Map getMapLiteral(final GremlinParser.GenericLiteralMapContext mapLiteral) {
+ return (Map) (instance().visitGenericLiteralMap(mapLiteral));
+ }
+
+ /**
* Parse a boolean literal context and return the boolean literal
*/
public static boolean getBooleanLiteral(final GremlinParser.BooleanLiteralContext booleanLiteral) {
@@ -299,6 +307,11 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
return antlr.tvisitor.visitNestedTraversal(ctx);
}
+ @Override
+ public Object visitStructureVertex(final GremlinParser.StructureVertexContext ctx) {
+ return StructureElementVisitor.instance().visitStructureVertex(ctx);
+ }
+
/**
* {@inheritDoc}
*/
@@ -464,7 +477,7 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
*/
@Override
public Object visitTraversalOptionParent(final GremlinParser.TraversalOptionParentContext ctx) {
- return TraversalEnumParser.parseTraversalEnumFromContext(TraversalOptionParent.Pick.class, ctx);
+ return TraversalEnumParser.parseTraversalEnumFromContext(Pick.class, ctx);
}
@Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
index 18f42f1..e987683 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
@@ -1529,4 +1529,59 @@ public class GremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> implement
public T visitStructureVertex(final GremlinParser.StructureVertexContext ctx) {
notImplemented(ctx); return null;
}
+
+ @Override
+ public T visitTraversalSourceSpawnMethod_mergeV_Map(final GremlinParser.TraversalSourceSpawnMethod_mergeV_MapContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalSourceSpawnMethod_mergeV_Traversal(final GremlinParser.TraversalSourceSpawnMethod_mergeV_TraversalContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalMethod_option_Merge_Map(GremlinParser.TraversalMethod_option_Merge_MapContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalMethod_option_Merge_Traversal(final GremlinParser.TraversalMethod_option_Merge_TraversalContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalMerge(final GremlinParser.TraversalMergeContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalMethod_mergeV_Map(final GremlinParser.TraversalMethod_mergeV_MapContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalMethod_mergeV_Traversal(final GremlinParser.TraversalMethod_mergeV_TraversalContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalMethod_mergeE_Map(final GremlinParser.TraversalMethod_mergeE_MapContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalMethod_mergeE_Traversal(final GremlinParser.TraversalMethod_mergeE_TraversalContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalSourceSpawnMethod_mergeE_Map(final GremlinParser.TraversalSourceSpawnMethod_mergeE_MapContext ctx) {
+ notImplemented(ctx); return null;
+ }
+
+ @Override
+ public T visitTraversalSourceSpawnMethod_mergeE_Traversal(final GremlinParser.TraversalSourceSpawnMethod_mergeE_TraversalContext ctx) {
+ notImplemented(ctx); return null;
+ }
}
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 e866598..c89ed86 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
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.language.grammar;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
@@ -89,6 +90,26 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
}
@Override
+ public GraphTraversal visitTraversalMethod_mergeV_Map(final GremlinParser.TraversalMethod_mergeV_MapContext ctx) {
+ return this.graphTraversal.mergeV(GenericLiteralVisitor.getMapLiteral(ctx.genericLiteralMap()));
+ }
+
+ @Override
+ public GraphTraversal visitTraversalMethod_mergeV_Traversal(final GremlinParser.TraversalMethod_mergeV_TraversalContext ctx) {
+ return this.graphTraversal.mergeV(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
+ }
+
+ @Override
+ public GraphTraversal visitTraversalMethod_mergeE_Map(final GremlinParser.TraversalMethod_mergeE_MapContext ctx) {
+ return this.graphTraversal.mergeE(GenericLiteralVisitor.getMapLiteral(ctx.genericLiteralMap()));
+ }
+
+ @Override
+ public GraphTraversal visitTraversalMethod_mergeE_Traversal(final GremlinParser.TraversalMethod_mergeE_TraversalContext ctx) {
+ return this.graphTraversal.mergeE(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
+ }
+
+ @Override
public GraphTraversal visitTraversalMethod_addE_Traversal(final GremlinParser.TraversalMethod_addE_TraversalContext ctx) {
return this.graphTraversal.addE(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
}
@@ -976,6 +997,24 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
* {@inheritDoc}
*/
@Override
+ public GraphTraversal visitTraversalMethod_option_Merge_Map(final GremlinParser.TraversalMethod_option_Merge_MapContext ctx) {
+ return graphTraversal.option(TraversalEnumParser.parseTraversalEnumFromContext(Merge.class, ctx.traversalMerge()),
+ (Map) new GenericLiteralVisitor(antlr).visitGenericLiteralMap(ctx.genericLiteralMap()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public GraphTraversal visitTraversalMethod_option_Merge_Traversal(final GremlinParser.TraversalMethod_option_Merge_TraversalContext ctx) {
+ return this.graphTraversal.option(TraversalEnumParser.parseTraversalEnumFromContext(Merge.class, ctx.traversalMerge()),
+ antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public GraphTraversal visitTraversalMethod_optional(final GremlinParser.TraversalMethod_optionalContext ctx) {
return this.graphTraversal.optional(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
}
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 73e8612..f7d04f7 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
@@ -22,6 +22,8 @@ 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 java.util.Map;
+
/**
* Use a {@link GraphTraversalSource} as the source and returns a {@link GraphTraversal} object.
*/
@@ -114,4 +116,24 @@ public class TraversalSourceSpawnMethodVisitor extends GremlinBaseVisitor<GraphT
}
return graphTraversal;
}
+
+ @Override
+ public GraphTraversal visitTraversalSourceSpawnMethod_mergeV_Map(final GremlinParser.TraversalSourceSpawnMethod_mergeV_MapContext ctx) {
+ return this.traversalSource.mergeV(GenericLiteralVisitor.getMapLiteral(ctx.genericLiteralMap()));
+ }
+
+ @Override
+ public GraphTraversal visitTraversalSourceSpawnMethod_mergeV_Traversal(final GremlinParser.TraversalSourceSpawnMethod_mergeV_TraversalContext ctx) {
+ return this.traversalSource.mergeV(anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal()));
+ }
+
+ @Override
+ public GraphTraversal visitTraversalSourceSpawnMethod_mergeE_Traversal(final GremlinParser.TraversalSourceSpawnMethod_mergeE_TraversalContext ctx) {
+ return this.traversalSource.mergeE(anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal()));
+ }
+
+ @Override
+ public GraphTraversal visitTraversalSourceSpawnMethod_mergeE_Map(final GremlinParser.TraversalSourceSpawnMethod_mergeE_MapContext ctx) {
+ return this.traversalSource.mergeE(GenericLiteralVisitor.getMapLiteral(ctx.genericLiteralMap()));
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Merge.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Merge.java
new file mode 100644
index 0000000..a64d8ce
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Merge.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.Element;
+
+import java.util.Map;
+
+/**
+ * Options relevant to upsert-like steps such as {@link GraphTraversalSource#mergeV(Map)}.
+ */
+public enum Merge {
+
+ /**
+ * Allows definition of the action to take when a merge operation ends up not matching the search criteria.
+ * Typically, this event means that an {@link Element} will be created.
+ *
+ * @since 3.6.0
+ */
+ onCreate,
+
+ /**
+ * Allows definition of the action to take when a merge operation ends up successfully matching the search criteria.
+ * Typically, this event means that the matched {@link Element} will be returned.
+ *
+ * @since 3.6.0
+ */
+ onMatch
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalOptionParent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Pick.java
similarity index 66%
copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalOptionParent.java
copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Pick.java
index 6eb60a2..980c7dd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalOptionParent.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Pick.java
@@ -16,16 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.tinkerpop.gremlin.process.traversal.step;
-
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+package org.apache.tinkerpop.gremlin.process.traversal;
/**
- * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * A token used with {@code option()}.
*/
-public interface TraversalOptionParent<M, S, E> extends TraversalParent {
-
- public static enum Pick {any, none}
-
- public void addGlobalChildOption(final M pickToken, final Traversal.Admin<S, E> traversalOption);
+public enum Pick {
+ any, none
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java
index 30dccc4..0be7555 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java
@@ -19,7 +19,6 @@
package org.apache.tinkerpop.gremlin.process.traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -165,9 +164,9 @@ public interface Translator<S, T> {
protected abstract String getSyntax(final VertexProperty.Cardinality o);
/**
- * Take the {@link TraversalOptionParent.Pick} argument and convert it to a string representation in the target language.
+ * Take the {@link Pick} argument and convert it to a string representation in the target language.
*/
- protected abstract String getSyntax(final TraversalOptionParent.Pick o);
+ protected abstract String getSyntax(final Pick o);
/**
* Take the numeric argument and convert it to a string representation in the target language. Languages
@@ -308,8 +307,8 @@ public interface Translator<S, T> {
return script.append(getSyntax((SackFunctions.Barrier) object));
} else if (object instanceof VertexProperty.Cardinality) {
return script.append(getSyntax((VertexProperty.Cardinality) object));
- } else if (object instanceof TraversalOptionParent.Pick) {
- return script.append(getSyntax((TraversalOptionParent.Pick) object));
+ } else if (object instanceof Pick) {
+ return script.append(getSyntax((Pick) object));
} else if (object instanceof Enum) {
return produceScript((Enum<?>) object);
} else if (object instanceof Vertex) {
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 376ed6b..9b0bdc5 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
@@ -26,15 +26,18 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PeerPres
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ProgramVertexProgramStep;
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ShortestPathVertexProgramStep;
import org.apache.tinkerpop.gremlin.process.traversal.Failure;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
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.lambda.ColumnTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.FunctionTraverser;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.LoopTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.PredicateTraverser;
@@ -100,6 +103,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxLocalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanLocalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MinGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MinLocalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.NoOpBarrierStep;
@@ -1072,6 +1077,63 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
}
/**
+ * 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.6.0
+ */
+ public default GraphTraversal<S, Vertex> mergeV(final Map<Object, Object> searchCreate) {
+ this.asAdmin().getBytecode().addStep(Symbols.mergeV, searchCreate);
+ final MergeVertexStep<S> step = new MergeVertexStep<>(this.asAdmin(), false, searchCreate);
+ return this.asAdmin().addStep(step);
+ }
+
+ /**
+ * 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 anonymous {@link Traversal} must produce a {@code Map} that may have a keys of
+ * {@link T} or a {@code String}.
+ * @since 3.6.0
+ */
+ public default GraphTraversal<S, Vertex> mergeV(final Traversal<?, Map<Object, Object>> searchCreate) {
+ this.asAdmin().getBytecode().addStep(Symbols.mergeV, searchCreate);
+ final MergeVertexStep<S> step = new MergeVertexStep(this.asAdmin(), false, searchCreate.asAdmin());
+ 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.6.0
+ */
+ public default GraphTraversal<S, Edge> mergeE(final Map<?, Object> searchCreate) {
+ this.asAdmin().getBytecode().addStep(Symbols.mergeE, searchCreate);
+ final MergeEdgeStep<S> step = new MergeEdgeStep(this.asAdmin(), false, 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.6.0
+ */
+ public default GraphTraversal<S, Edge> mergeE(final Traversal<?, Map<Object, Object>> searchCreate) {
+ this.asAdmin().getBytecode().addStep(Symbols.mergeE, searchCreate);
+ final MergeEdgeStep<S> step = new MergeEdgeStep(this.asAdmin(), false, searchCreate.asAdmin());
+ return this.asAdmin().addStep(step);
+ }
+
+ /**
* Adds an {@link Edge} with the specified edge label.
*
* @param edgeLabel the label of the newly added edge
@@ -3060,7 +3122,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
/**
* This step modifies {@link #choose(Function)} to specifies the available choices that might be executed.
*
- * @param pick the token that would trigger this option which may be a {@link TraversalOptionParent.Pick},
+ * @param pick the token that would trigger this option which may be a {@link Pick},
* 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
@@ -3069,7 +3131,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
*/
public default <M, E2> GraphTraversal<S, E> option(final M pick, final Traversal<?, E2> traversalOption) {
this.asAdmin().getBytecode().addStep(Symbols.option, pick, traversalOption);
- ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addGlobalChildOption(pick, (Traversal.Admin<E, E2>) traversalOption.asAdmin());
+ ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption(pick, (Traversal.Admin<E, E2>) traversalOption.asAdmin());
return this;
}
@@ -3083,7 +3145,39 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
*/
public default <E2> GraphTraversal<S, E> option(final Traversal<?, E2> traversalOption) {
this.asAdmin().getBytecode().addStep(Symbols.option, traversalOption);
- ((TraversalOptionParent<Object, E, E2>) this.asAdmin().getEndStep()).addGlobalChildOption(TraversalOptionParent.Pick.any, (Traversal.Admin<E, E2>) traversalOption.asAdmin());
+ ((TraversalOptionParent<Object, E, E2>) this.asAdmin().getEndStep()).addChildOption(Pick.any, (Traversal.Admin<E, E2>) traversalOption.asAdmin());
+ return this;
+ }
+
+ /**
+ * This step modifies merge related steps to specify the available choices that might be executed on the available
+ * {@link Merge} options.
+ *
+ * @param traversal the option as a traversal
+ * @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.6.0
+ */
+ public default <E2> GraphTraversal<S, E> option(final Merge merge, final Traversal<?, E2> traversal) {
+ this.asAdmin().getBytecode().addStep(Symbols.option, merge, traversal);
+ ((TraversalOptionParent<Merge, E, E2>) this.asAdmin().getEndStep()).addChildOption(merge, (Traversal.Admin<E, E2>) traversal.asAdmin());
+ return this;
+ }
+
+ /**
+ * This step modifies merge related steps to specify the available choices that might be executed on the available
+ * {@link Merge} options.
+ *
+ * @param 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.6.0
+ */
+ public default <E2> GraphTraversal<S, E> option(final Merge merge, final Map<Object, Object> m) {
+ this.asAdmin().getBytecode().addStep(Symbols.option, merge, m);
+ ((TraversalOptionParent<Merge, E, E2>) this.asAdmin().getEndStep()).addChildOption(merge, (Traversal.Admin<E, E2>) new ConstantTraversal<>(m).asAdmin());
return this;
}
@@ -3186,6 +3280,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
public static final String tree = "tree";
public static final String addV = "addV";
public static final String addE = "addE";
+ public static final String mergeV = "mergeV";
+ public static final String mergeE = "mergeE";
public static final String from = "from";
public static final String filter = "filter";
public static final String or = "or";
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 fbb873f..5806227 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
@@ -30,17 +30,22 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStartStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IoStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.RequirementsStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Transaction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import java.util.Map;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Supplier;
@@ -298,6 +303,8 @@ public class GraphTraversalSource implements TraversalSource {
/**
* Spawns a {@link GraphTraversal} by adding a vertex with the specified label. If the {@code label} is
* {@code null} then it will default to {@link Vertex#DEFAULT_LABEL}.
+ *
+ * @since 3.1.0-incubating
*/
public GraphTraversal<Vertex, Vertex> addV(final String vertexLabel) {
if (null == vertexLabel) throw new IllegalArgumentException("vertexLabel cannot be null");
@@ -310,6 +317,8 @@ public class GraphTraversalSource implements TraversalSource {
/**
* Spawns a {@link GraphTraversal} by adding a vertex with the label as determined by a {@link Traversal}. If the
* {@code vertexLabelTraversal} is {@code null} then it will default to {@link Vertex#DEFAULT_LABEL}.
+ *
+ * @since 3.3.1
*/
public GraphTraversal<Vertex, Vertex> addV(final Traversal<?, String> vertexLabelTraversal) {
if (null == vertexLabelTraversal) throw new IllegalArgumentException("vertexLabelTraversal cannot be null");
@@ -321,6 +330,8 @@ public class GraphTraversalSource implements TraversalSource {
/**
* Spawns a {@link GraphTraversal} by adding a vertex with the default label.
+ *
+ * @since 3.1.0-incubating
*/
public GraphTraversal<Vertex, Vertex> addV() {
final GraphTraversalSource clone = this.clone();
@@ -330,7 +341,9 @@ public class GraphTraversalSource implements TraversalSource {
}
/**
- * Spawns a {@link GraphTraversal} by adding a edge with the specified label.
+ * Spawns a {@link GraphTraversal} by adding an edge with the specified label.
+ *
+ * @since 3.1.0-incubating
*/
public GraphTraversal<Edge, Edge> addE(final String label) {
final GraphTraversalSource clone = this.clone();
@@ -341,6 +354,8 @@ public class GraphTraversalSource implements TraversalSource {
/**
* Spawns a {@link GraphTraversal} by adding a edge with a label as specified by the provided {@link Traversal}.
+ *
+ * @since 3.3.1
*/
public GraphTraversal<Edge, Edge> addE(final Traversal<?, String> edgeLabelTraversal) {
final GraphTraversalSource clone = this.clone();
@@ -350,6 +365,67 @@ public class GraphTraversalSource implements TraversalSource {
}
/**
+ * 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 Map<Object, Object> searchCreate) {
+ final GraphTraversalSource clone = 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 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 anonymous {@link Traversal} must produce a {@code Map} that may have a keys of
+ * {@link T} or a {@code String}.
+ * @since 3.6.0
+ */
+ public <S> GraphTraversal<S, Vertex> mergeV(final Traversal<?, Map<Object, Object>> searchCreate) {
+ final GraphTraversalSource clone = this.clone();
+ clone.bytecode.addStep(GraphTraversal.Symbols.mergeV, searchCreate);
+ final GraphTraversal.Admin<S, Vertex> traversal = new DefaultGraphTraversal<>(clone);
+ return traversal.addStep(new MergeVertexStep(traversal, true, searchCreate.asAdmin()));
+ }
+
+ /**
+ * 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.6.0
+ */
+ public GraphTraversal<Edge, Edge> mergeE(final Map<?, Object> searchCreate) {
+ final GraphTraversalSource clone = 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));
+ }
+
+ /**
+ * 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.6.0
+ */
+ public GraphTraversal<Edge, Edge> mergeE(final Traversal<?, Map<Object, Object>> searchCreate) {
+ final GraphTraversalSource clone = 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.asAdmin()));
+ }
+
+ /**
* Spawns a {@link GraphTraversal} starting it with arbitrary values.
*/
public <S> GraphTraversal<S, S> inject(S... starts) {
@@ -364,6 +440,8 @@ public class GraphTraversalSource implements TraversalSource {
/**
* Spawns a {@link GraphTraversal} starting with all vertices or some subset of vertices as specified by their
* unique identifier.
+ *
+ * @since 3.0.0-incubating
*/
public GraphTraversal<Vertex, Vertex> V(final Object... vertexIds) {
// a single null is [null]
@@ -377,6 +455,8 @@ public class GraphTraversalSource implements TraversalSource {
/**
* Spawns a {@link GraphTraversal} starting with all edges or some subset of edges as specified by their unique
* identifier.
+ *
+ * @since 3.0.0-incubating
*/
public GraphTraversal<Edge, Edge> E(final Object... edgeIds) {
// a single null is [null]
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
index 0502ed0..01e5c0f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
@@ -516,6 +516,20 @@ public class __ {
}
/**
+ * @see GraphTraversal#mergeV(Map)
+ */
+ public static <A> GraphTraversal<A, Vertex> mergeV(final Map<Object, Object> searchCreate) {
+ return __.<A>start().mergeV(searchCreate);
+ }
+
+ /**
+ * @see GraphTraversal#mergeV(Traversal)
+ */
+ public static <A> GraphTraversal<A, Vertex> mergeV(final Traversal<?, Map<Object, Object>> searchCreate) {
+ return __.<A>start().mergeV(searchCreate);
+ }
+
+ /**
* @see GraphTraversal#addE(String)
*/
public static <A> GraphTraversal<A, Edge> addE(final String edgeLabel) {
@@ -523,13 +537,27 @@ public class __ {
}
/**
- * @see GraphTraversal#addE(org.apache.tinkerpop.gremlin.process.traversal.Traversal)
+ * @see GraphTraversal#addE(Traversal)
*/
public static <A> GraphTraversal<A, Edge> addE(final Traversal<?, String> edgeLabelTraversal) {
return __.<A>start().addE(edgeLabelTraversal);
}
/**
+ * @see GraphTraversal#mergeE(Map)
+ */
+ public static <A> GraphTraversal<A, Edge> mergeE(final Map<?, Object> searchCreate) {
+ return __.<A>start().mergeE(searchCreate);
+ }
+
+ /**
+ * @see GraphTraversal#mergeE(Traversal)
+ */
+ public static <A> GraphTraversal<A, Edge> mergeE(final Traversal<?, Map<Object, Object>> searchCreate) {
+ return __.<A>start().mergeE(searchCreate);
+ }
+
+ /**
* @see GraphTraversal#math(String)
*/
public static <A> GraphTraversal<A, Double> math(final String expression) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalOptionParent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalOptionParent.java
index 6eb60a2..ef9b92c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalOptionParent.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalOptionParent.java
@@ -18,14 +18,19 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
/**
+ * Describes steps that can be parent to a {@link Traversal} from {@code option()}.
+ *
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public interface TraversalOptionParent<M, S, E> extends TraversalParent {
- public static enum Pick {any, none}
-
- public void addGlobalChildOption(final M pickToken, final Traversal.Admin<S, E> traversalOption);
+ /**
+ * The child as defined by the token it takes, like {@link Pick}. This traversal may be of local or global scope
+ * depending on the step implementation that works with {@code option()}.
+ */
+ public void addChildOption(final M token, final Traversal.Admin<S, E> traversalOption);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java
index 361ed54..085cf57 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.branch;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.PredicateTraversal;
@@ -63,7 +64,7 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav
}
@Override
- public void addGlobalChildOption(final M pickToken, final Traversal.Admin<S, E> traversalOption) {
+ public void addChildOption(final M pickToken, final Traversal.Admin<S, E> traversalOption) {
if (pickToken instanceof Pick) {
if (this.traversalPickOptions.containsKey(pickToken))
this.traversalPickOptions.get(pickToken).add(traversalOption);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java
index a804d8f..86ec39e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.branch;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.HasNextStep;
@@ -36,18 +37,18 @@ public final class ChooseStep<S, E, M> extends BranchStep<S, E, M> {
public ChooseStep(final Traversal.Admin traversal, final Traversal.Admin<S, ?> predicateTraversal, final Traversal.Admin<S, E> trueChoice, final Traversal.Admin<S, E> falseChoice) {
this(traversal, (Traversal.Admin<S, M>) predicateTraversal.addStep(new HasNextStep<>(predicateTraversal)));
- this.addGlobalChildOption((M) Boolean.TRUE, trueChoice);
- this.addGlobalChildOption((M) Boolean.FALSE, falseChoice);
+ this.addChildOption((M) Boolean.TRUE, trueChoice);
+ this.addChildOption((M) Boolean.FALSE, falseChoice);
}
@Override
- public void addGlobalChildOption(final M pickToken, final Traversal.Admin<S, E> traversalOption) {
+ public void addChildOption(final M pickToken, final Traversal.Admin<S, E> traversalOption) {
if (pickToken instanceof Pick) {
if (Pick.any.equals(pickToken))
throw new IllegalArgumentException("Choose step can not have an any-option as only one option per traverser is allowed");
if (this.traversalPickOptions.containsKey(pickToken))
throw new IllegalArgumentException("Choose step can only have one traversal per pick token: " + pickToken);
}
- super.addGlobalChildOption(pickToken, traversalOption);
+ super.addChildOption(pickToken, traversalOption);
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/UnionStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/UnionStep.java
index de70479..1ce6137 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/UnionStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/UnionStep.java
@@ -18,8 +18,8 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.branch;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -28,21 +28,21 @@ import java.util.Collections;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class UnionStep<S, E> extends BranchStep<S, E, TraversalOptionParent.Pick> {
+public final class UnionStep<S, E> extends BranchStep<S, E, Pick> {
public UnionStep(final Traversal.Admin traversal, final Traversal.Admin<?, E>... unionTraversals) {
super(traversal);
this.setBranchTraversal(new ConstantTraversal<>(Pick.any));
for (final Traversal.Admin<?, E> union : unionTraversals) {
- this.addGlobalChildOption(Pick.any, (Traversal.Admin) union);
+ this.addChildOption(Pick.any, (Traversal.Admin) union);
}
}
@Override
- public void addGlobalChildOption(final Pick pickToken, final Traversal.Admin<S, E> traversalOption) {
+ public void addChildOption(final Pick pickToken, final Traversal.Admin<S, E> traversalOption) {
if (Pick.any != pickToken)
throw new IllegalArgumentException("Union step only supports the any token: " + pickToken);
- super.addGlobalChildOption(pickToken, traversalOption);
+ super.addChildOption(pickToken, traversalOption);
}
@Override
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
new file mode 100644
index 0000000..e5e4a19
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
@@ -0,0 +1,334 @@
+/*
+ * 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.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
+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.TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.Attachable;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+
+/**
+ * Implementation for the {@code mergeE()} step covering both the start step version and the one used mid-traversal.
+ */
+public class MergeEdgeStep<S> extends FlatMapStep<S, Edge> implements Mutating<Event>,
+ TraversalOptionParent<Merge, S, Edge> {
+
+ private final boolean isStart;
+ private boolean first = true;
+ private Traversal.Admin<S,Map<Object, Object>> searchCreateTraversal;
+ private Traversal.Admin<S, Map<Object, Object>> onCreateTraversal = null;
+ private Traversal.Admin<S, Map<String, Object>> onMatchTraversal = null;
+
+ private CallbackRegistry<Event> callbackRegistry;
+
+ public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final Map<Object, Object> searchCreate) {
+ this(traversal, isStart, new ConstantTraversal<>(searchCreate));
+ }
+
+ public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<?,Map<Object, Object>> searchCreateTraversal) {
+ super(traversal);
+ this.isStart = isStart;
+ this.searchCreateTraversal = integrateChild(searchCreateTraversal);
+ }
+
+ public Traversal.Admin<S, Map<Object, Object>> getSearchCreateTraversal() {
+ return searchCreateTraversal;
+ }
+
+ public Traversal.Admin<S, Map<Object, Object>> getOnCreateTraversal() {
+ return onCreateTraversal;
+ }
+
+ public Traversal.Admin<S, Map<String, Object>> getOnMatchTraversal() {
+ return onMatchTraversal;
+ }
+
+ /**
+ * Determines if this is a start step.
+ */
+ public boolean isStart() {
+ return isStart;
+ }
+
+ public boolean isFirst() {
+ return first;
+ }
+
+ public CallbackRegistry<Event> getCallbackRegistry() {
+ return callbackRegistry;
+ }
+
+ @Override
+ public void addChildOption(final Merge token, final Traversal.Admin<S, Edge> traversalOption) {
+ if (token == Merge.onCreate) {
+ this.onCreateTraversal = this.integrateChild(traversalOption);
+ } else if (token == Merge.onMatch) {
+ this.onMatchTraversal = this.integrateChild(traversalOption);
+ } else {
+ throw new UnsupportedOperationException(String.format("Option %s for Merge is not supported", token.name()));
+ }
+ }
+
+ @Override
+ public <S, E> List<Traversal.Admin<S, E>> getLocalChildren() {
+ final List<Traversal.Admin<S, E>> children = new ArrayList<>();
+ if (searchCreateTraversal != null) children.add((Traversal.Admin<S, E>) searchCreateTraversal);
+ if (onMatchTraversal != null) children.add((Traversal.Admin<S, E>) onMatchTraversal);
+ if (onCreateTraversal != null) children.add((Traversal.Admin<S, E>) onCreateTraversal);
+ return children;
+ }
+
+ @Override
+ public void configure(final Object... keyValues) {
+ // this is a Mutating step but property() should not be folded into this step. this exception should not
+ // end up visible to users really
+ }
+
+ @Override
+ public Parameters getParameters() {
+ // merge doesn't take fold ups of property() calls. those need to get treated as regular old PropertyStep
+ // instances. not sure if this should support with() though.....none of the other Mutating steps do.
+ return null;
+ }
+
+ @Override
+ protected Traverser.Admin<Edge> processNextStart() {
+ // when it's a start step a traverser needs to be created to kick off the traversal.
+ if (isStart && first) {
+ first = false;
+ final TraverserGenerator generator = this.getTraversal().getTraverserGenerator();
+ this.addStart(generator.generate(false, (Step) this, 1L));
+ }
+ return super.processNextStart();
+ }
+
+ /**
+ * Use the {@code Map} of search criteria to most efficiently return a {@code Stream<Edge>} of matching elements.
+ * Providers might override this method when extending this step to provide their own optimized mechanisms for
+ * matching the list of edges. This implementation is only optimized for the {@link T#id} so any other usage
+ * will simply be in-memory filtering which could be slow.
+ */
+ protected Stream<Edge> createSearchStream(final Map<Object,Object> search) {
+ final Graph graph = this.getTraversal().getGraph().get();
+
+ final Optional<Direction> directionUsedInLookup;
+ Stream<Edge> stream;
+ // prioritize lookup by id, then use vertices as starting point if possible
+ if (search.containsKey(T.id)) {
+ stream = IteratorUtils.stream(graph.edges(search.get(T.id)));
+ directionUsedInLookup = Optional.empty();
+ } else if (search.containsKey(Direction.OUT)) {
+ stream = IteratorUtils.stream(graph.vertices(search.get(Direction.OUT))).flatMap(v -> IteratorUtils.stream(v.edges(Direction.OUT)));
+ directionUsedInLookup = Optional.of(Direction.OUT);
+ } else if (search.containsKey(Direction.IN)) {
+ stream = IteratorUtils.stream(graph.vertices(search.get(Direction.IN))).flatMap(v -> IteratorUtils.stream(v.edges(Direction.IN)));
+ directionUsedInLookup = Optional.of(Direction.IN);
+ } else {
+ stream = IteratorUtils.stream(graph.edges());
+ directionUsedInLookup = Optional.empty();
+ }
+
+ // in-memory filter is not going to be nice here. it will be up to graphs to optimize this step as they do
+ // for other Mutation steps
+ stream = stream.filter(e -> {
+ // try to match on all search criteria skipping T.id as it was handled above
+ return search.entrySet().stream().filter(
+ kv -> kv.getKey() != T.id && !(directionUsedInLookup.isPresent() && kv.getKey() == directionUsedInLookup.get())).
+ allMatch(kv -> {
+ if (kv.getKey() == T.label) {
+ return e.label().equals(kv.getValue());
+ } else if (kv.getKey() instanceof Direction) {
+ final Direction direction = (Direction) kv.getKey();
+
+ // try to take advantage of string id conversions of the graph by doing a lookup rather
+ // than direct compare on id
+ final Iterator<Vertex> found = graph.vertices(kv.getValue());
+ return found.hasNext() && e.vertices(direction).next().equals(found.next());
+ } else {
+ final Property<Object> vp = e.property(kv.getKey().toString());
+ return vp.isPresent() && kv.getValue().equals(vp.value());
+ }
+ });
+ });
+
+ return stream;
+ }
+
+ @Override
+ protected Iterator<Edge> flatMap(final Traverser.Admin<S> traverser) {
+ final S possibleVertex = traverser.get();
+ final Map<Object,Object> searchCreate = TraversalUtil.apply(traverser, searchCreateTraversal);
+
+ Vertex outV = (Vertex) searchCreate.getOrDefault(Direction.OUT, possibleVertex);
+ Vertex inV = (Vertex) searchCreate.getOrDefault(Direction.IN, possibleVertex);
+
+ if (inV instanceof Attachable)
+ inV = ((Attachable<Vertex>) inV)
+ .attach(Attachable.Method.getOrCreate(this.getTraversal().getGraph().orElse(EmptyGraph.instance())));
+ if (outV instanceof Attachable)
+ outV = ((Attachable<Vertex>) outV)
+ .attach(Attachable.Method.getOrCreate(this.getTraversal().getGraph().orElse(EmptyGraph.instance())));
+
+ final Vertex fromV = outV;
+ final Vertex toV = inV;
+
+ Stream<Edge> stream = createSearchStream(searchCreate);
+ stream = stream.map(e -> {
+ // if no onMatch is defined then there is no update - return the vertex unchanged
+ if (null == onMatchTraversal) return e;
+
+ // assume good input from GraphTraversal - folks might drop in a T here even though it is immutable
+ final Map<String, Object> onMatchMap = TraversalUtil.apply(traverser, onMatchTraversal);
+ onMatchMap.forEach((key, value) -> {
+ // trigger callbacks for eventing - in this case, it's a VertexPropertyChangedEvent. if there's no
+ // registry/callbacks then just set the property
+ if (this.callbackRegistry != null && !callbackRegistry.getCallbacks().isEmpty()) {
+ final EventStrategy eventStrategy = getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
+ final Property<Object> oldValue = eventStrategy.detach(e.property(key));
+ final Event.EdgePropertyChangedEvent vpce = new Event.EdgePropertyChangedEvent(eventStrategy.detach(e), oldValue, value);
+ this.callbackRegistry.getCallbacks().forEach(c -> c.accept(vpce));
+ }
+ e.property(key, value);
+ });
+
+ return e;
+ });
+
+ // if the stream has something then there is a match (possibly updated) and is returned, otherwise a new
+ // edge is created
+ final Iterator<Edge> edges = stream.iterator();
+ if (edges.hasNext()) {
+ return edges;
+ } else {
+ final Edge edge;
+
+ // if there is an onCreateTraversal then the search criteria is ignored for the creation as it is provided
+ // by way of the traversal which will return the Map
+ final Map<Object,Object> m = null == onCreateTraversal ? searchCreate : TraversalUtil.apply(traverser, onCreateTraversal);
+ final List<Object> keyValues = new ArrayList<>();
+ String label = Edge.DEFAULT_LABEL;
+ for (Map.Entry<Object, Object> entry : m.entrySet()) {
+ // skip Direction keys as they are already handled in getting fromV/toV
+ if (entry.getKey() instanceof Direction) continue;
+
+ if (entry.getKey().equals(T.label)) {
+ label = (String) entry.getValue();
+ } else {
+ keyValues.add(entry.getKey());
+ keyValues.add(entry.getValue());
+ }
+ }
+
+ edge = fromV.addEdge(label, toV, keyValues.toArray(new Object[keyValues.size()]));
+
+ // trigger callbacks for eventing - in this case, it's a VertexAddedEvent
+ if (this.callbackRegistry != null && !callbackRegistry.getCallbacks().isEmpty()) {
+ final EventStrategy eventStrategy = getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
+ final Event.EdgeAddedEvent vae = new Event.EdgeAddedEvent(eventStrategy.detach(edge));
+ this.callbackRegistry.getCallbacks().forEach(c -> c.accept(vae));
+ }
+
+ return IteratorUtils.of(edge);
+ }
+ }
+
+ @Override
+ public CallbackRegistry<Event> getMutatingCallbackRegistry() {
+ if (null == callbackRegistry) callbackRegistry = new ListCallbackRegistry<>();
+ return callbackRegistry;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ if (searchCreateTraversal != null)
+ result ^= searchCreateTraversal.hashCode();
+ if (onCreateTraversal != null)
+ result ^= onCreateTraversal.hashCode();
+ if (onMatchTraversal != null)
+ result ^= onMatchTraversal.hashCode();
+ return result;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ first = true;
+ searchCreateTraversal.reset();
+ if (onCreateTraversal != null) onCreateTraversal.reset();
+ if (onMatchTraversal != null) onMatchTraversal.reset();
+ }
+
+ @Override
+ public Set<TraverserRequirement> getRequirements() {
+ return this.getSelfAndChildRequirements();
+ }
+
+ @Override
+ public String toString() {
+ return StringFactory.stepString(this, searchCreateTraversal, onCreateTraversal, onMatchTraversal);
+ }
+
+ @Override
+ public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) {
+ super.setTraversal(parentTraversal);
+ this.integrateChild(searchCreateTraversal);
+ this.integrateChild(onCreateTraversal);
+ this.integrateChild(onMatchTraversal);
+ }
+
+ @Override
+ public MergeEdgeStep<S> clone() {
+ final MergeEdgeStep<S> clone = (MergeEdgeStep<S>) super.clone();
+ clone.searchCreateTraversal = searchCreateTraversal.clone();
+ clone.onCreateTraversal = onCreateTraversal != null ? onCreateTraversal.clone() : null;
+ clone.onMatchTraversal = onMatchTraversal != null ? onMatchTraversal.clone() : null;
+ return clone;
+ }
+}
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
new file mode 100644
index 0000000..319db5d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
@@ -0,0 +1,287 @@
+/*
+ * 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.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
+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.TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+/**
+ * Implementation for the {@code mergeV()} step covering both the start step version and the one used mid-traversal.
+ */
+public class MergeVertexStep<S> extends FlatMapStep<S, Vertex> implements Mutating<Event>,
+ TraversalOptionParent<Merge, S, Vertex> {
+
+ private final boolean isStart;
+ private boolean first = true;
+ private Traversal.Admin<S,Map<Object, Object>> searchCreateTraversal;
+ private Traversal.Admin<S, Map<Object, Object>> onCreateTraversal = null;
+ private Traversal.Admin<S, Map<String, Object>> onMatchTraversal = null;
+
+ private CallbackRegistry<Event> callbackRegistry;
+
+ public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Map<Object, Object> searchCreate) {
+ this(traversal, isStart, new ConstantTraversal<>(searchCreate));
+ }
+
+ public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map<Object, Object>> searchCreateTraversal) {
+ super(traversal);
+ this.isStart = isStart;
+ this.searchCreateTraversal = integrateChild(searchCreateTraversal);
+ }
+
+ public Traversal.Admin<S, Map<Object, Object>> getSearchCreateTraversal() {
+ return searchCreateTraversal;
+ }
+
+ public Traversal.Admin<S, Map<Object, Object>> getOnCreateTraversal() {
+ return onCreateTraversal;
+ }
+
+ public Traversal.Admin<S, Map<String, Object>> getOnMatchTraversal() {
+ return onMatchTraversal;
+ }
+
+ /**
+ * Determines if this is a start step.
+ */
+ public boolean isStart() {
+ return isStart;
+ }
+
+ public boolean isFirst() {
+ return first;
+ }
+
+ public CallbackRegistry<Event> getCallbackRegistry() {
+ return callbackRegistry;
+ }
+
+ @Override
+ public void addChildOption(final Merge token, final Traversal.Admin<S, Vertex> traversalOption) {
+ if (token == Merge.onCreate) {
+ this.onCreateTraversal = this.integrateChild(traversalOption);
+ } else if (token == Merge.onMatch) {
+ this.onMatchTraversal = this.integrateChild(traversalOption);
+ } else {
+ throw new UnsupportedOperationException(String.format("Option %s for Merge is not supported", token.name()));
+ }
+ }
+
+ @Override
+ public <S, E> List<Traversal.Admin<S, E>> getLocalChildren() {
+ final List<Traversal.Admin<S, E>> children = new ArrayList<>();
+ if (searchCreateTraversal != null) children.add((Traversal.Admin<S, E>) searchCreateTraversal);
+ if (onMatchTraversal != null) children.add((Traversal.Admin<S, E>) onMatchTraversal);
+ if (onCreateTraversal != null) children.add((Traversal.Admin<S, E>) onCreateTraversal);
+ return children;
+ }
+
+ @Override
+ public void configure(final Object... keyValues) {
+ // this is a Mutating step but property() should not be folded into this step. this exception should not
+ // end up visible to users really
+ }
+
+ @Override
+ public Parameters getParameters() {
+ // merge doesn't take fold ups of property() calls. those need to get treated as regular old PropertyStep
+ // instances. not sure if this should support with() though.....none of the other Mutating steps do.
+ return null;
+ }
+
+ @Override
+ protected Traverser.Admin<Vertex> processNextStart() {
+ // when it's a start step a traverser needs to be created to kick off the traversal.
+ if (isStart && first) {
+ first = false;
+ final TraverserGenerator generator = this.getTraversal().getTraverserGenerator();
+ this.addStart(generator.generate(false, (Step) this, 1L));
+ }
+ return super.processNextStart();
+ }
+
+ /**
+ * Use the {@code Map} of search criteria to most efficiently return a {@code Stream<Vertex>} of matching elements.
+ * Providers might override this method when extending this step to provide their own optimized mechanisms for
+ * matching the list of vertices. This implementation is only optimized for the {@link T#id} so any other usage
+ * will simply be in-memory filtering which could be slow.
+ */
+ protected Stream<Vertex> createSearchStream(final Map<Object,Object> search) {
+ final Graph graph = this.getTraversal().getGraph().get();
+
+ Stream<Vertex> stream;
+ // prioritize lookup by id
+ if (search.containsKey(T.id))
+ stream = IteratorUtils.stream(graph.vertices(search.get(T.id)));
+ else
+ stream = IteratorUtils.stream(graph.vertices());
+
+ // in-memory filter is not going to be nice here. it will be up to graphs to optimize this step as they do
+ // for other Mutation steps
+ stream = stream.filter(v -> {
+ // try to match on all search criteria skipping T.id as it was handled above
+ return search.entrySet().stream().filter(kv -> kv.getKey() != T.id).allMatch(kv -> {
+ if (kv.getKey() == T.label) {
+ return v.label().equals(kv.getValue());
+ } else {
+ final VertexProperty<Object> vp = v.property(kv.getKey().toString());
+ return vp.isPresent() && kv.getValue().equals(vp.value());
+ }
+ });
+ });
+
+ return stream;
+ }
+
+ @Override
+ protected Iterator<Vertex> flatMap(final Traverser.Admin<S> traverser) {
+ final Map<Object,Object> searchCreate = TraversalUtil.apply(traverser, searchCreateTraversal);
+
+ Stream<Vertex> stream = createSearchStream(searchCreate);
+ stream = stream.map(v -> {
+ // if no onMatch is defined then there is no update - return the vertex unchanged
+ if (null == onMatchTraversal) return v;
+
+ // assume good input from GraphTraversal - folks might drop in a T here even though it is immutable
+ final Map<String, Object> onMatchMap = TraversalUtil.apply(traverser, onMatchTraversal);
+ onMatchMap.forEach((key, value) -> {
+ // trigger callbacks for eventing - in this case, it's a VertexPropertyChangedEvent. if there's no
+ // registry/callbacks then just set the property
+ if (this.callbackRegistry != null && !callbackRegistry.getCallbacks().isEmpty()) {
+ final EventStrategy eventStrategy = getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
+ final Property<Object> oldValue = eventStrategy.detach(v.property(key));
+ final Event.VertexPropertyChangedEvent vpce = new Event.VertexPropertyChangedEvent(eventStrategy.detach(v), oldValue, value);
+ this.callbackRegistry.getCallbacks().forEach(c -> c.accept(vpce));
+ }
+ v.property(key, value);
+ });
+
+ return v;
+ });
+
+ // if the stream has something then there is a match (possibly updated) and is returned, otherwise a new
+ // vertex is created
+ final Iterator<Vertex> vertices = stream.iterator();
+ if (vertices.hasNext()) {
+ return vertices;
+ } else {
+ final Vertex vertex;
+
+ // if there is an onCreateTraversal then the search criteria is ignored for the creation as it is provided
+ // by way of the traversal which will return the Map
+ final Map<Object,Object> m = null == onCreateTraversal ? searchCreate : TraversalUtil.apply(traverser, onCreateTraversal);
+ final List<Object> keyValues = new ArrayList<>();
+ for (Map.Entry<Object, Object> entry : m.entrySet()) {
+ keyValues.add(entry.getKey());
+ keyValues.add(entry.getValue());
+ }
+ vertex = this.getTraversal().getGraph().get().addVertex(keyValues.toArray(new Object[keyValues.size()]));
+
+ // trigger callbacks for eventing - in this case, it's a VertexAddedEvent
+ if (this.callbackRegistry != null && !callbackRegistry.getCallbacks().isEmpty()) {
+ final EventStrategy eventStrategy = getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
+ final Event.VertexAddedEvent vae = new Event.VertexAddedEvent(eventStrategy.detach(vertex));
+ this.callbackRegistry.getCallbacks().forEach(c -> c.accept(vae));
+ }
+
+ return IteratorUtils.of(vertex);
+ }
+ }
+
+ @Override
+ public CallbackRegistry<Event> getMutatingCallbackRegistry() {
+ if (null == callbackRegistry) callbackRegistry = new ListCallbackRegistry<>();
+ return callbackRegistry;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ if (searchCreateTraversal != null)
+ result ^= searchCreateTraversal.hashCode();
+ if (onCreateTraversal != null)
+ result ^= onCreateTraversal.hashCode();
+ if (onMatchTraversal != null)
+ result ^= onMatchTraversal.hashCode();
+ return result;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ first = true;
+ searchCreateTraversal.reset();
+ if (onCreateTraversal != null) onCreateTraversal.reset();
+ if (onMatchTraversal != null) onMatchTraversal.reset();
+ }
+
+ @Override
+ public Set<TraverserRequirement> getRequirements() {
+ return this.getSelfAndChildRequirements();
+ }
+
+ @Override
+ public String toString() {
+ return StringFactory.stepString(this, searchCreateTraversal, onCreateTraversal, onMatchTraversal);
+ }
+
+ @Override
+ public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) {
+ super.setTraversal(parentTraversal);
+ this.integrateChild(searchCreateTraversal);
+ this.integrateChild(onCreateTraversal);
+ this.integrateChild(onMatchTraversal);
+ }
+
+ @Override
+ public MergeVertexStep<S> clone() {
+ final MergeVertexStep<S> clone = (MergeVertexStep<S>) super.clone();
+ clone.searchCreateTraversal = searchCreateTraversal.clone();
+ clone.onCreateTraversal = onCreateTraversal != null ? onCreateTraversal.clone() : null;
+ clone.onMatchTraversal = onMatchTraversal != null ? onMatchTraversal.clone() : null;
+ return clone;
+ }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
index 4f375e5..5f3ae25 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
@@ -19,10 +19,10 @@
package org.apache.tinkerpop.gremlin.process.traversal.translator;
-import org.apache.commons.configuration2.ConfigurationConverter;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Script;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
@@ -30,7 +30,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
@@ -171,7 +170,7 @@ public final class DotNetTranslator implements Translator.ScriptTranslator {
}
@Override
- protected String getSyntax(final TraversalOptionParent.Pick o) {
+ protected String getSyntax(final Pick o) {
return "Pick." + SymbolHelper.toCSharp(o.toString());
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
index ef544b4..bfa3065 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
@@ -24,14 +24,12 @@ import org.apache.commons.text.StringEscapeUtils;
import org.apache.tinkerpop.gremlin.jsr223.CoreImports;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Script;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
@@ -175,7 +173,7 @@ public final class GroovyTranslator implements Translator.ScriptTranslator {
}
@Override
- protected String getSyntax(final TraversalOptionParent.Pick o) {
+ protected String getSyntax(final Pick o) {
return "TraversalOptionParent.Pick." + o.toString();
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslator.java
index e9cf748..f95d288 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslator.java
@@ -23,13 +23,13 @@ import org.apache.commons.configuration2.ConfigurationConverter;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Script;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
@@ -167,7 +167,7 @@ public final class JavascriptTranslator implements Translator.ScriptTranslator {
}
@Override
- protected String getSyntax(final TraversalOptionParent.Pick o) {
+ protected String getSyntax(final Pick o) {
return "Pick." + o.toString();
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslator.java
index 09e4447..987fa3e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslator.java
@@ -23,6 +23,7 @@ import org.apache.commons.configuration2.ConfigurationConverter;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Script;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
@@ -30,7 +31,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
@@ -188,7 +188,7 @@ public final class PythonTranslator implements Translator.ScriptTranslator {
}
@Override
- protected String getSyntax(final TraversalOptionParent.Pick o) {
+ protected String getSyntax(final Pick o) {
return "Pick." + resolveSymbol(o.toString());
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
index ced0088..0c4b913 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.structure.io.binary;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.structure.io.binary.types.*;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
@@ -30,7 +31,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
@@ -119,7 +119,7 @@ public class TypeSerializerRegistry {
new RegistryEntry<>(Direction.class, EnumSerializer.DirectionSerializer),
new RegistryEntry<>(Operator.class, EnumSerializer.OperatorSerializer),
new RegistryEntry<>(Order.class, EnumSerializer.OrderSerializer),
- new RegistryEntry<>(TraversalOptionParent.Pick.class, EnumSerializer.PickSerializer),
+ new RegistryEntry<>(Pick.class, EnumSerializer.PickSerializer),
new RegistryEntry<>(Pop.class, EnumSerializer.PopSerializer),
new RegistryEntry<>(Lambda.class, new LambdaSerializer()),
new RegistryEntry<>(P.class, new PSerializer<>(DataType.P, P.class)),
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EnumSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EnumSerializer.java
index 12e2d15..52b4c08 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EnumSerializer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EnumSerializer.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.structure.io.binary.types;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.structure.io.binary.DataType;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
@@ -26,7 +27,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.structure.Column;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.T;
@@ -49,7 +49,7 @@ public class EnumSerializer<E extends Enum> extends SimpleTypeSerializer<E> {
public static final EnumSerializer<Direction> DirectionSerializer = new EnumSerializer<>(DataType.DIRECTION, Direction::valueOf);
public static final EnumSerializer<Operator> OperatorSerializer = new EnumSerializer<>(DataType.OPERATOR, Operator::valueOf);
public static final EnumSerializer<Order> OrderSerializer = new EnumSerializer<>(DataType.ORDER, Order::valueOf);
- public static final EnumSerializer<TraversalOptionParent.Pick> PickSerializer = new EnumSerializer<>(DataType.PICK, TraversalOptionParent.Pick::valueOf);
+ public static final EnumSerializer<Pick> PickSerializer = new EnumSerializer<>(DataType.PICK, Pick::valueOf);
public static final EnumSerializer<Pop> PopSerializer = new EnumSerializer<>(DataType.POP, Pop::valueOf);
public static final EnumSerializer<Scope> ScopeSerializer = new EnumSerializer<>(DataType.SCOPE, Scope::valueOf);
public static final EnumSerializer<T> TSerializer = new EnumSerializer<>(DataType.T, T::valueOf);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index eda0d37..fccab3f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
@@ -32,7 +33,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
@@ -178,7 +178,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
Order.class,
Pop.class,
SackFunctions.Barrier.class,
- TraversalOptionParent.Pick.class,
+ Pick.class,
Scope.class,
T.class).forEach(e -> put(e, e.getSimpleName()));
Arrays.asList(
@@ -262,7 +262,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
Pop.class,
SackFunctions.Barrier.class,
Scope.class,
- TraversalOptionParent.Pick.class,
+ Pick.class,
T.class).forEach(e -> addSerializer(e, new TraversalSerializersV3d0.EnumJacksonSerializer()));
addSerializer(P.class, new TraversalSerializersV3d0.PJacksonSerializer());
addSerializer(Lambda.class, new TraversalSerializersV3d0.LambdaJacksonSerializer());
@@ -304,7 +304,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
Pop.values(),
SackFunctions.Barrier.values(),
Scope.values(),
- TraversalOptionParent.Pick.values(),
+ Pick.values(),
T.values()).flatMap(Stream::of).forEach(e -> addDeserializer(e.getClass(), new TraversalSerializersV3d0.EnumJacksonDeserializer(e.getDeclaringClass())));
addDeserializer(P.class, new TraversalSerializersV3d0.PJacksonDeserializer());
addDeserializer(TextP.class, new TraversalSerializersV3d0.TextPJacksonDeserializer());
@@ -416,7 +416,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
Order.class,
Pop.class,
SackFunctions.Barrier.class,
- TraversalOptionParent.Pick.class,
+ Pick.class,
Scope.class,
T.class).forEach(e -> put(e, e.getSimpleName()));
Arrays.asList(
@@ -496,7 +496,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
Pop.class,
SackFunctions.Barrier.class,
Scope.class,
- TraversalOptionParent.Pick.class,
+ Pick.class,
T.class).forEach(e -> addSerializer(e, new TraversalSerializersV2d0.EnumJacksonSerializer()));
addSerializer(P.class, new TraversalSerializersV2d0.PJacksonSerializer());
addSerializer(Lambda.class, new TraversalSerializersV2d0.LambdaJacksonSerializer());
@@ -532,7 +532,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
Pop.values(),
SackFunctions.Barrier.values(),
Scope.values(),
- TraversalOptionParent.Pick.values(),
+ Pick.values(),
T.values()).flatMap(Stream::of).forEach(e -> addDeserializer(e.getClass(), new TraversalSerializersV2d0.EnumJacksonDeserializer(e.getDeclaringClass())));
addDeserializer(P.class, new TraversalSerializersV2d0.PJacksonDeserializer());
addDeserializer(TextP.class, new TraversalSerializersV2d0.TextPJacksonDeserializer());
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java
index 3fdb50a..e50037c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java
@@ -21,11 +21,11 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics;
import org.apache.tinkerpop.gremlin.structure.Column;
@@ -131,8 +131,8 @@ public class GraphSONTypeSerializerV2d0 extends AbstractGraphSONTypeSerializer {
mapped = Pop.class;
else if (SackFunctions.Barrier.class.isAssignableFrom(c))
mapped = SackFunctions.Barrier.class;
- else if (TraversalOptionParent.Pick.class.isAssignableFrom(c))
- mapped = TraversalOptionParent.Pick.class;
+ else if (Pick.class.isAssignableFrom(c))
+ mapped = Pick.class;
else if (Scope.class.isAssignableFrom(c))
mapped = Scope.class;
else if (T.class.isAssignableFrom(c))
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java
index eca91ef..4349e47 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java
@@ -21,11 +21,11 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics;
@@ -165,8 +165,8 @@ public class GraphSONTypeSerializerV3d0 extends AbstractGraphSONTypeSerializer {
mapped = Pop.class;
else if (SackFunctions.Barrier.class.isAssignableFrom(c))
mapped = SackFunctions.Barrier.class;
- else if (TraversalOptionParent.Pick.class.isAssignableFrom(c))
- mapped = TraversalOptionParent.Pick.class;
+ else if (Pick.class.isAssignableFrom(c))
+ mapped = Pick.class;
else if (Scope.class.isAssignableFrom(c))
mapped = Scope.class;
else if (T.class.isAssignableFrom(c))
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 bd2b1b0..eda92fe 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
@@ -30,11 +30,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
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;
@@ -329,7 +329,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(Column.class, 132));
add(GryoTypeReg.of(Pop.class, 133));
add(GryoTypeReg.of(SackFunctions.Barrier.class, 135));
- add(GryoTypeReg.of(TraversalOptionParent.Pick.class, 137));
+ add(GryoTypeReg.of(Pick.class, 137));
add(GryoTypeReg.of(HashSetSupplier.class, 136, new UtilSerializers.HashSetSupplierSerializer()));
add(GryoTypeReg.of(MultiComparator.class, 165));
@@ -529,7 +529,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(Column.class, 132));
add(GryoTypeReg.of(Pop.class, 133));
add(GryoTypeReg.of(SackFunctions.Barrier.class, 135));
- add(GryoTypeReg.of(TraversalOptionParent.Pick.class, 137));
+ add(GryoTypeReg.of(Pick.class, 137));
add(GryoTypeReg.of(HashSetSupplier.class, 136, new UtilSerializers.HashSetSupplierSerializer()));
add(GryoTypeReg.of(MultiComparator.class, 165));
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalEnumParserTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalEnumParserTest.java
index 0d490df..6ed0d3f 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalEnumParserTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalEnumParserTest.java
@@ -53,13 +53,14 @@ public class TraversalEnumParserTest {
{"org.apache.tinkerpop.gremlin.process.traversal.Scope", "traversalScope"},
{"org.apache.tinkerpop.gremlin.process.traversal.Order", "traversalOrder"},
{"org.apache.tinkerpop.gremlin.process.traversal.Pop", "traversalPop"},
+ {"org.apache.tinkerpop.gremlin.process.traversal.Pick", "traversalOptionParent"},
+ {"org.apache.tinkerpop.gremlin.process.traversal.Merge", "traversalMerge"},
{"org.apache.tinkerpop.gremlin.process.traversal.SackFunctions$Barrier", "traversalSackMethod"},
{"org.apache.tinkerpop.gremlin.process.traversal.Operator", "traversalOperator"},
{"org.apache.tinkerpop.gremlin.structure.T", "traversalToken"},
{"org.apache.tinkerpop.gremlin.structure.Column", "traversalColumn"},
{"org.apache.tinkerpop.gremlin.structure.Direction", "traversalDirection"},
{"org.apache.tinkerpop.gremlin.structure.VertexProperty$Cardinality", "traversalCardinality"},
- {"org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent$Pick", "traversalOptionParent"},
});
}
@@ -91,6 +92,7 @@ public class TraversalEnumParserTest {
return Arrays.asList(new Object[][]{
{"org.apache.tinkerpop.gremlin.process.traversal.Scope", "traversalScope"},
{"org.apache.tinkerpop.gremlin.process.traversal.Order", "traversalOrder"},
+ {"org.apache.tinkerpop.gremlin.process.traversal.Merge", "traversalMerge"},
{"org.apache.tinkerpop.gremlin.structure.T", "traversalToken"}
});
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStepTest.java
index f68af08..629ea72 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStepTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStepTest.java
@@ -28,7 +28,7 @@ import java.util.List;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.values;
-import static org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick.none;
+import static org.apache.tinkerpop.gremlin.process.traversal.Pick.none;
/**
* @author Daniel Kuppitz (http://gremlin.guru)
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java
index 885da77..604ae20 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java
@@ -29,7 +29,7 @@ import java.util.List;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.values;
-import static org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick.none;
+import static org.apache.tinkerpop.gremlin.process.traversal.Pick.none;
/**
* @author Daniel Kuppitz (http://gremlin.guru)
diff --git a/gremlin-dotnet/build/generate.groovy b/gremlin-dotnet/build/generate.groovy
index 0d6e8ba..fedf0dd 100644
--- a/gremlin-dotnet/build/generate.groovy
+++ b/gremlin-dotnet/build/generate.groovy
@@ -31,6 +31,16 @@ import java.nio.file.Paths
import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal
+// getting an exception like:
+// > InvocationTargetException: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of
+// > method: org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal.mergeE() is applicable for
+// > argument types: (String) values: [4ffdea36-4a0e-4681-acba-e76875d1b25b]
+// usually means bindings are not being extracted properly by VarAsBindingASTTransformation which typically happens
+// when a step is taking an argument that cannot properly resolve to the type required by the step itself. there are
+// special cases in that VarAsBindingASTTransformation class which might need to be adjusted. Editing the
+// GremlinGroovyScriptEngineTest#shouldProduceBindingsForVars() with the failing step and argument can typically make
+// this issue relatively easy to debug and enforce.
+
// file is overwritten on each generation
radishGremlinFile = new File("${projectBaseDir}/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs")
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 021f484..f89e2a5 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -500,6 +500,30 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Mean<object>()}},
{"g_injectXnull_10_20_nullX_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Mean<object>()}},
{"g_injectXlistXnull_10_20_nullXX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Mean<object>(Scope.Local)}},
+ {"g_V_mergeEXlabel_self_weight_05X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (g,p) =>g.V().MergeE(p["xx1"]), (g,p) =>g.E().HasLabel("self").Has("weight",0.5)}},
+ {"g_mergeEXlabel_knows_out_marko_in_vadasX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").AddV("person").Property(T.Id,101).Property("name","vadas"), (g,p) =>g.MergeE(p["xx1"]), (g,p) =>g.V().Has("person","name","marko").Out("knows").Has("person","name","vadas")}},
+ {"g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b"), (g,p) =>g.MergeE(p["xx1"]), (g,p) =>g.V().Has("person","name","marko").OutE("knows").Has("weight",0.5).InV().Has("person","name","vadas"), (g,p) =>g.V().Has("p [...]
+ {"g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeE(p["xx1"]), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows").Has("weight",0.5)}},
+ {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeE(p["xx1"]).Option(Merge.OnCreate,p["xx2"]).Option(Merge.OnMatch,p["xx3"]), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows").Has("created","Y"), (g,p) =>g.E().HasLabel("knows").Has("created","N")}},
+ {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b"), (g,p) =>g.MergeE(p["xx1"]).Option(Merge.OnCreate,p["xx2"]).Option(Merge.OnMatch,p["xx3"]), (g,p) =>g.V(), (g,p) =>g. [...]
+ {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y"), (g,p) =>g.MergeE(p["xx1"]).Option(Merge.OnCreate,p["xx2"]).Option(Merge.OnMatch,p["x [...]
+ {"g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y").AddE("knows").From("a").To("b"), (g,p) =>g.V().Has("person","name","marko").Me [...]
+ {"g_VX100X_VX101X_mergeEXlabel_knows_out_marko_in_vadasX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko"), (g,p) =>g.V(100).V(101).MergeE(p["xx1"]), (g,p) =>g.V(), (g,p) =>g.E()}},
+ {"g_mergeVXlabel_person_name_stephenX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]), (g,p) =>g.V().Has("person","name","stephen")}},
+ {"g_mergeVXlabel_person_name_markoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]), (g,p) =>g.V().Has("person","name","marko")}},
+ {"g_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]).Option(Merge.OnCreate,p["xx2"]), (g,p) =>g.V().Has("person","name","stephen").Has("age",19)}},
+ {"g_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]).Option(Merge.OnMatch,p["xx2"]), (g,p) =>g.V().Has("person","name","marko").Has("age",19)}},
+ {"g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).MergeV(__.Select<object>("c")).Option(Merge.OnCreate,__.Select<object>("m")), (g,p) =>g.V().Has [...]
+ {"g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).MergeV(__.Select<object>("c")).Option(Merge.OnMatch,__.Select<object>("m")), (g,p) =>g.V().Has("person","name","marko").Has( [...]
+ {"g_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]).Property("name","vadas","acl","public"), (g,p) =>g.V().Properties<object>("name").HasValue("vadas").Has("acl","public")}},
+ {"g_injectX0X_mergeVXlabel_person_name_stephenX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]), (g,p) =>g.V().Has("person","name","stephen")}},
+ {"g_injectX0X_mergeVXlabel_person_name_markoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]), (g,p) =>g.V().Has("person","name","marko")}},
+ {"g_injectX0X_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]).Option(Merge.OnCreate,p["xx2"]), (g,p) =>g.V().Has("person","name","stephen").Has("age",19)}},
+ {"g_injectX0X_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]).Option(Merge.OnMatch,p["xx2"]), (g,p) =>g.V().Has("person","name","marko").Has("age",19)}},
+ {"g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_injectX0X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).Inject(0).MergeV(__.Select<object>("c")).Option(Merge.OnCreate,__.Select<object>("m") [...]
+ {"g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).Inject(0).MergeV(__.Select<object>("c")).Option(Merge.OnMatch,__.Select<object>("m")), (g,p) =>g.V().Has("person", [...]
+ {"g_injectX0X_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]).Property("name","vadas","acl","public"), (g,p) =>g.V().Properties<object>("name").HasValue("vadas").Has("acl","public")}},
+ {"g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(p["xx1"],p["xx2"]).MergeV(__.Identity()), (g,p) =>g.V().Has("person","name","stephen"), (g,p) =>g.V().Has("person","name","marko"), (g,p) =>g.V()}},
{"g_V_age_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Min<object>()}},
{"g_V_foo_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Min<object>()}},
{"g_V_name_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Min<object>()}},
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
index 3fd54e2..1800331 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
@@ -27,13 +27,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
@@ -221,7 +221,7 @@ public class GraphBinaryReaderWriterRoundTripTest {
new Object[] {"Operator", Operator.sum, null},
new Object[] {"Operator", Operator.div, null},
new Object[] {"Order", Order.desc, null},
- new Object[] {"Pick", TraversalOptionParent.Pick.any, null},
+ new Object[] {"Pick", Pick.any, null},
new Object[] {"Pop", Pop.mixed, null},
new Object[] {"Scope", Scope.global, null},
new Object[] {"T", T.label, null},
diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/VarAsBindingASTTransformation.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/VarAsBindingASTTransformation.groovy
index 1da973d..ea3c65a 100644
--- a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/VarAsBindingASTTransformation.groovy
+++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/VarAsBindingASTTransformation.groovy
@@ -20,6 +20,7 @@
package org.apache.tinkerpop.gremlin.groovy.jsr223.ast
import org.apache.tinkerpop.gremlin.process.traversal.Order
+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.__
import org.apache.tinkerpop.gremlin.process.traversal.Translator
@@ -115,7 +116,11 @@ class VarAsBindingASTTransformation implements ASTTransformation {
case GraphTraversal.Symbols.by:
if (i == 1) bindingValue = new PropertyExpression(new ClassExpression(new ClassNode(Order)), "desc")
break
- case GraphTraversal.Symbols.fail:
+ case GraphTraversal.Symbols.mergeE:
+ case GraphTraversal.Symbols.mergeV:
+ bindingValue = new MethodCallExpression(new ClassExpression(new ClassNode(Collections)), "emptyMap", new TupleExpression())
+ break
+ case GraphTraversal.Symbols.option:
if (i == 1) bindingValue = new MethodCallExpression(new ClassExpression(new ClassNode(Collections)), "emptyMap", new TupleExpression())
break
}
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
index 3baf20e..66b2dbd 100644
--- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
@@ -484,6 +484,8 @@ public class GremlinGroovyScriptEngineTest {
final Traversal.Admin<Vertex, Vertex> t = (Traversal.Admin<Vertex, Vertex>)
engine.eval("g.V(v1Id).has(\"person\",\"age\",29).has('person','active',x).in(\"knows\")." +
System.lineSeparator() +
+ "mergeE(m1).mergeV(m2).option(Merge.onCreate,m3)." +
+ System.lineSeparator() +
"choose(__.out().count()).option(two, __.values(\"name\")).option(three, __.values(\"age\"))." +
System.lineSeparator() +
"filter(outE().count().is(y))." +
@@ -498,15 +500,18 @@ public class GremlinGroovyScriptEngineTest {
final String gremlinAsPython = translator.translate(bytecode).getScript();
final Map<String,Object> bytecodeBindings = bytecode.getBindings();
- assertEquals(7, bytecodeBindings.size());
+ assertEquals(10, bytecodeBindings.size());
assertThat(bytecodeBindings.containsKey("x"), is(true));
assertThat(bytecodeBindings.containsKey("y"), is(true));
assertThat(bytecodeBindings.containsKey("v1Id"), is(true));
assertThat(bytecodeBindings.containsKey("l"), is(true));
assertThat(bytecodeBindings.containsKey("o"), is(true));
+ assertThat(bytecodeBindings.containsKey("m1"), is(true));
+ assertThat(bytecodeBindings.containsKey("m2"), is(true));
+ assertThat(bytecodeBindings.containsKey("m3"), is(true));
assertThat(bytecodeBindings.containsKey("two"), is(true));
assertThat(bytecodeBindings.containsKey("three"), is(true));
- assertEquals("g.V(v1Id).has('person','age',29).has('person','active',x).in_('knows').choose(__.out().count()).option(two,__.name).option(three,__.age).filter_(__.outE().count().is_(y)).map(l).order().by('name',o)", gremlinAsPython);
+ assertEquals("g.V(v1Id).has('person','age',29).has('person','active',x).in_('knows').mergeE(m1).mergeV(m2).option(Merge.onCreate,m3).choose(__.out().count()).option(two,__.name).option(three,__.age).filter_(__.outE().count().is_(y)).map(l).order().by('name',o)", gremlinAsPython);
}
}
\ No newline at end of file
diff --git a/gremlin-javascript/build/generate.groovy b/gremlin-javascript/build/generate.groovy
index 013f840..1b52ea4 100644
--- a/gremlin-javascript/build/generate.groovy
+++ b/gremlin-javascript/build/generate.groovy
@@ -31,6 +31,16 @@ import java.nio.file.Paths
import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal
+// getting an exception like:
+// > InvocationTargetException: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of
+// > method: org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal.mergeE() is applicable for
+// > argument types: (String) values: [4ffdea36-4a0e-4681-acba-e76875d1b25b]
+// usually means bindings are not being extracted properly by VarAsBindingASTTransformation which typically happens
+// when a step is taking an argument that cannot properly resolve to the type required by the step itself. there are
+// special cases in that VarAsBindingASTTransformation class which might need to be adjusted. Editing the
+// GremlinGroovyScriptEngineTest#shouldProduceBindingsForVars() with the failing step and argument can typically make
+// this issue relatively easy to debug and enforce.
+
// file is overwritten on each generation
radishGremlinFile = new File("${projectBaseDir}/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js")
@@ -84,6 +94,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
' IN: traversalModule.direction.in,\n' +
' OUT: traversalModule.direction.out\n' +
'};\n' +
+ 'const Merge = traversalModule.merge;\n' +
'const P = traversalModule.P;\n' +
'const Pick = traversalModule.pick\n' +
'const Pop = traversalModule.pop\n' +
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
index 6c49ec5..c609ba0 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
@@ -810,6 +810,26 @@ class GraphTraversal extends Traversal {
this.bytecode.addStep('mean', args);
return this;
}
+
+ /**
+ * Graph traversal mergeE method.
+ * @param {...Object} args
+ * @returns {GraphTraversal}
+ */
+ mergeE(...args) {
+ this.bytecode.addStep("mergeE", args);
+ return this;
+ }
+
+ /**
+ * Graph traversal mergeV method.
+ * @param {...Object} args
+ * @returns {GraphTraversal}
+ */
+ mergeV(...args) {
+ this.bytecode.addStep("mergeV", args);
+ return this;
+ }
/**
* Graph traversal min method.
@@ -1365,6 +1385,8 @@ const statics = {
math: (...args) => callOnEmptyTraversal('math', args),
max: (...args) => callOnEmptyTraversal('max', args),
mean: (...args) => callOnEmptyTraversal('mean', args),
+ mergeE: (...args) => callOnEmptyTraversal("mergeE", args),
+ mergeV: (...args) => callOnEmptyTraversal("mergeV", args),
min: (...args) => callOnEmptyTraversal('min', args),
not: (...args) => callOnEmptyTraversal('not', args),
optional: (...args) => callOnEmptyTraversal('optional', args),
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js
index f72c3f9..bbffeea 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js
@@ -496,6 +496,7 @@ module.exports = {
direction: toEnum('Direction', 'BOTH IN OUT'),
graphSONVersion: toEnum('GraphSONVersion', 'V1_0 V2_0 V3_0'),
gryoVersion: toEnum('GryoVersion', 'V1_0 V3_0'),
+ merge: toEnum('Merge', 'onCreate onMatch'),
operator: toEnum('Operator', 'addAll and assign div max min minus mult or sum sumLong'),
order: toEnum('Order', 'asc desc shuffle'),
pick: toEnum('Pick', 'any none'),
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 0a452a6..6079579 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -36,6 +36,7 @@ const Direction = {
IN: traversalModule.direction.in,
OUT: traversalModule.direction.out
};
+const Merge = traversalModule.merge;
const P = traversalModule.P;
const Pick = traversalModule.pick
const Pop = traversalModule.pop
@@ -488,6 +489,30 @@ const gremlins = {
g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").unfold().mean() }],
g_injectXnull_10_20_nullX_mean: [function({g, xx1, xx2}) { return g.inject(null,xx1,xx2,null).mean() }],
g_injectXlistXnull_10_20_nullXX_meanXlocalX: [function({g, xx1}) { return g.inject(xx1).mean(Scope.local) }],
+ g_V_mergeEXlabel_self_weight_05X: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29) }, function({g, xx1}) { return g.V().mergeE(xx1) }, function({g, xx1}) { return g.E().hasLabel("self").has("weight",0.5) }],
+ g_mergeEXlabel_knows_out_marko_in_vadasX: [function({g, xx1}) { return g.addV("person").property(T.id,100).property("name","marko").addV("person").property(T.id,101).property("name","vadas") }, function({g, xx1}) { return g.mergeE(xx1) }, function({g, xx1}) { return g.V().has("person","name","marko").out("knows").has("person","name","vadas") }],
+ g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists: [function({g, xx1}) { return g.addV("person").property(T.id,100).property("name","marko").as("a").addV("person").property(T.id,101).property("name","vadas").as("b").addE("knows").from_("a").to("b") }, function({g, xx1}) { return g.mergeE(xx1) }, function({g, xx1}) { return g.V().has("person","name","marko").outE("knows").has("weight",0.5).inV().has("person","name","vadas") }, function({g, xx1}) { return g.V().has("person","na [...]
+ g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X: [function({g, xx1}) { return g.mergeE(xx1) }, function({g, xx1}) { return g.V() }, function({g, xx1}) { return g.E().hasLabel("knows").has("weight",0.5) }],
+ g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX: [function({g, xx1, xx3, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) }, function({g, xx1, xx3, xx2}) { return g.V() }, function({g, xx1, xx3, xx2}) { return g.E().hasLabel("knows").has("created","Y") }, function({g, xx1, xx3, xx2}) { return g.E().hasLabel("knows").has("created","N") }],
+ g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists: [function({g, xx1, xx3, xx2}) { return g.addV("person").property(T.id,100).property("name","marko").as("a").addV("person").property(T.id,101).property("name","vadas").as("b").addE("knows").from_("a").to("b") }, function({g, xx1, xx3, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) }, function({g, xx1, xx3, xx2}) { return g.V() }, function({g, xx1, xx [...]
+ g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated: [function({g, xx1, xx3, xx2}) { return g.addV("person").property(T.id,100).property("name","marko").as("a").addV("person").property(T.id,101).property("name","vadas").as("b").addE("knows").from_("a").to("b").property("created","Y") }, function({g, xx1, xx3, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) }, function({g, xx1, xx3, xx2}) { retu [...]
+ g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated: [function({g, xx1, xx3, xx2}) { return g.addV("person").property(T.id,100).property("name","marko").as("a").addV("person").property(T.id,101).property("name","vadas").as("b").addE("knows").from_("a").to("b").property("created","Y").addE("knows").from_("a").to("b") }, function({g, xx1, xx3, xx2}) { return g.V().has("person","name","marko").mergeE(xx1).option(Merge.onCre [...]
+ g_VX100X_VX101X_mergeEXlabel_knows_out_marko_in_vadasX: [function({g, xx1}) { return g.addV("person").property(T.id,100).property("name","marko") }, function({g, xx1}) { return g.V(100).V(101).mergeE(xx1) }, function({g, xx1}) { return g.V() }, function({g, xx1}) { return g.E() }],
+ g_mergeVXlabel_person_name_stephenX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.mergeV(xx1) }, function({g, xx1}) { return g.V().has("person","name","stephen") }],
+ g_mergeVXlabel_person_name_markoX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.mergeV(xx1) }, function({g, xx1}) { return g.V().has("person","name","marko") }],
+ g_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.mergeV(xx1).option(Merge.onCreate,xx2) }, function({g, xx1, xx2}) { return g.V().has("person","name","stephen").has("age",19) }],
+ g_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.mergeV(xx1).option(Merge.onMatch,xx2) }, function({g, xx1, xx2}) { return g.V().has("person","name","marko").has("age",19) }],
+ g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.withSideEffect("c",xx1).withSideEffect("m",xx2).mergeV(__.select("c")).option(Merge.onCreate,__.select("m")) }, function({g, xx1, xx2}) { return g.V().has("person","name","stephen").has("age",19) }],
+ g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.withSideEffect("c",xx1).withSideEffect("m",xx2).mergeV(__.select("c")).option(Merge.onMatch,__.select("m")) }, function({g, xx1, xx2}) { return g.V().has("person","name","marko").has("age",19) }],
+ g_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.mergeV(xx1).property("name","vadas","acl","public") }, function({g, xx1}) { return g.V().properties("name").hasValue("vadas").has("acl","public") }],
+ g_injectX0X_mergeVXlabel_person_name_stephenX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.inject(0).mergeV(xx1) }, function({g, xx1}) { return g.V().has("person","name","stephen") }],
+ g_injectX0X_mergeVXlabel_person_name_markoX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.inject(0).mergeV(xx1) }, function({g, xx1}) { return g.V().has("person","name","marko") }],
+ g_injectX0X_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.inject(0).mergeV(xx1).option(Merge.onCreate,xx2) }, function({g, xx1, xx2}) { return g.V().has("person","name","stephen").has("age",19) }],
+ g_injectX0X_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.inject(0).mergeV(xx1).option(Merge.onMatch,xx2) }, function({g, xx1, xx2}) { return g.V().has("person","name","marko").has("age",19) }],
+ g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_injectX0X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.withSideEffect("c",xx1).withSideEffect("m",xx2).inject(0).mergeV(__.select("c")).option(Merge.onCreate,__.select("m")) }, function({g, xx1, xx2}) { return g.V().has("person","name","s [...]
+ g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.withSideEffect("c",xx1).withSideEffect("m",xx2).inject(0).mergeV(__.select("c")).option(Merge.onMatch,__.select("m")) }, function({g, xx1, xx2}) { return g.V().has("person","name","marko").has("age",19) }],
+ g_injectX0X_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.inject(0).mergeV(xx1).property("name","vadas","acl","public") }, function({g, xx1}) { return g.V().properties("name").hasValue("vadas").has("acl","public") }],
+ g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.inject(xx1,xx2).mergeV(__.identity()) }, function({g, xx1, xx2}) { return g.V().has("person","name","stephen") }, function({g, xx1, xx2}) { return g.V().has("person","name","marko") }, function({g, xx1, xx2}) { return g.V() }],
g_V_age_min: [function({g}) { return g.V().values("age").min() }],
g_V_foo_min: [function({g}) { return g.V().values("foo").min() }],
g_V_name_min: [function({g}) { return g.V().values("name").min() }],
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4
index 78d8928..242b79c 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -97,6 +97,8 @@ traversalSourceSpawnMethod
| traversalSourceSpawnMethod_addV
| traversalSourceSpawnMethod_E
| traversalSourceSpawnMethod_V
+ | traversalSourceSpawnMethod_mergeE
+ | traversalSourceSpawnMethod_mergeV
| traversalSourceSpawnMethod_inject
| traversalSourceSpawnMethod_io
;
@@ -128,6 +130,16 @@ traversalSourceSpawnMethod_io
: 'io' LPAREN stringLiteral RPAREN
;
+traversalSourceSpawnMethod_mergeV
+ : 'mergeV' LPAREN genericLiteralMap RPAREN #traversalSourceSpawnMethod_mergeV_Map
+ | 'mergeV' LPAREN nestedTraversal RPAREN #traversalSourceSpawnMethod_mergeV_Traversal
+ ;
+
+traversalSourceSpawnMethod_mergeE
+ : 'mergeE' LPAREN genericLiteralMap RPAREN #traversalSourceSpawnMethod_mergeE_Map
+ | 'mergeE' LPAREN nestedTraversal RPAREN #traversalSourceSpawnMethod_mergeE_Traversal
+ ;
+
chainedTraversal
: traversalMethod
| chainedTraversal DOT traversalMethod
@@ -157,6 +169,8 @@ traversalMethod
: traversalMethod_V
| traversalMethod_addE
| traversalMethod_addV
+ | traversalMethod_mergeE
+ | traversalMethod_mergeV
| traversalMethod_aggregate
| traversalMethod_and
| traversalMethod_as
@@ -272,6 +286,17 @@ traversalMethod_addV
| 'addV' LPAREN nestedTraversal RPAREN #traversalMethod_addV_Traversal
;
+traversalMethod_mergeV
+ : 'mergeV' LPAREN genericLiteralMap RPAREN #traversalMethod_mergeV_Map
+ | 'mergeV' LPAREN nestedTraversal RPAREN #traversalMethod_mergeV_Traversal
+ ;
+
+traversalMethod_mergeE
+ : 'mergeE' LPAREN genericLiteralMap RPAREN #traversalMethod_mergeE_Map
+ | 'mergeE' LPAREN nestedTraversal RPAREN #traversalMethod_mergeE_Traversal
+ ;
+
+
traversalMethod_aggregate
: 'aggregate' LPAREN traversalScope COMMA stringLiteral RPAREN #traversalMethod_aggregate_Scope_String
| 'aggregate' LPAREN stringLiteral RPAREN #traversalMethod_aggregate_String
@@ -531,6 +556,8 @@ traversalMethod_not
traversalMethod_option
: 'option' LPAREN traversalPredicate COMMA nestedTraversal RPAREN #traversalMethod_option_Predicate_Traversal
+ | 'option' LPAREN traversalMerge COMMA genericLiteralMap RPAREN #traversalMethod_option_Merge_Map
+ | 'option' LPAREN traversalMerge COMMA nestedTraversal RPAREN #traversalMethod_option_Merge_Traversal
| 'option' LPAREN genericLiteral COMMA nestedTraversal RPAREN #traversalMethod_option_Object_Traversal
| 'option' LPAREN nestedTraversal RPAREN #traversalMethod_option_Traversal
;
@@ -837,6 +864,11 @@ traversalToken
| 'value' | 'T.value'
;
+traversalMerge
+ : 'onCreate' | 'Merge.onCreate'
+ | 'onMatch' | 'Merge.onMatch'
+ ;
+
traversalOrder
: 'incr' | 'Order.incr'
| 'decr' | 'Order.decr'
@@ -1310,6 +1342,7 @@ genericLiteral
| traversalCardinality
| traversalDirection
| traversalOptionParent
+ | structureVertex
| genericLiteralCollection
| genericLiteralRange
| nestedTraversal
diff --git a/gremlin-language/src/main/java/org/apache/tinkerpop/gremlin/language/corpus/DocumentationReader.java b/gremlin-language/src/main/java/org/apache/tinkerpop/gremlin/language/corpus/DocumentationReader.java
index ba7c749..6c091e5 100644
--- a/gremlin-language/src/main/java/org/apache/tinkerpop/gremlin/language/corpus/DocumentationReader.java
+++ b/gremlin-language/src/main/java/org/apache/tinkerpop/gremlin/language/corpus/DocumentationReader.java
@@ -105,6 +105,8 @@ public class DocumentationReader {
replace("vMarko", "\"marko\"").
replace("vPeter", "\"peter\"").
replace("vStephen", "\"stephen\"").
+ replace("v1", "new Vertex(1,\"vertex\")").
+ replace("v2", "new Vertex(2,\"vertex\")").
replace("input.head()", "\"head\"").
replace("input.tail().size()", "6").
replace("input.tail()", "\"tail\"").
diff --git a/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ReferenceGrammarTest.java b/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ReferenceGrammarTest.java
index b3d3f29..a0e91fe 100644
--- a/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ReferenceGrammarTest.java
+++ b/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ReferenceGrammarTest.java
@@ -49,10 +49,20 @@ public class ReferenceGrammarTest extends AbstractGrammarTest {
private static final String featureDir = Paths.get("..", "gremlin-test", "features").toString();
private static final String docsDir = Paths.get("..", "docs", "src").toString();
- private static final Pattern vertexPattern = Pattern.compile(".*v\\d.*");
private static final Pattern edgePattern = Pattern.compile(".*e\\d.*");
private static final List<Pair<Pattern, BiFunction<String,String,String>>> stringMatcherConverters = new ArrayList<Pair<Pattern, BiFunction<String,String,String>>>() {{
+ add(Pair.with(Pattern.compile("m\\[(.*)\\]"), (k,v) -> {
+ // can't handled embedded maps because of the string replace below on the curly braces
+ final String[] items = v.replace("{", "").replace("}", "").split(",");
+ final String listItems = Stream.of(items).map(String::trim).map(x -> {
+ final String[] pair = x.split(":");
+ final String convertedKey = String.format("%s", pair[0]);
+ final String convertedValue = String.format("%s", pair[1]);
+ return String.format("%s:%s", convertedKey, convertedValue);
+ }).collect(Collectors.joining(","));
+ return String.format("[%s]", listItems);
+ }));
add(Pair.with(Pattern.compile("l\\[\\]"), (k,v) -> "[]"));
add(Pair.with(Pattern.compile("l\\[(.*)\\]"), (k,v) -> {
final String[] items = v.split(",");
@@ -60,6 +70,7 @@ public class ReferenceGrammarTest extends AbstractGrammarTest {
return String.format("[%s]", listItems);
}));
add(Pair.with(Pattern.compile("v\\[(.+)\\]"), (k,v) -> "\"1\""));
+ add(Pair.with(Pattern.compile("v(\\d)"), (k,v) -> String.format("new Vertex(%s)", v)));
add(Pair.with(Pattern.compile("e\\[(.+)\\]"), (k,v) -> "\"1\""));
add(Pair.with(Pattern.compile("d\\[(.*)\\]\\.?.*"), (k,v) -> v));
add(Pair.with(Pattern.compile("m\\[(.*)\\]"), (k,v) -> v.replace('{','[').replace('}', ']')));
@@ -89,11 +100,12 @@ public class ReferenceGrammarTest extends AbstractGrammarTest {
@Test
public void test_parse() {
+ // can't handle maps with complex embedded types at the moment - basically one Gremlin statement at this point
+ assumeThat("Complex embedded types are not supported", query.contains("l[\"666\"]"), is(false));
assumeThat("Lambdas are not supported", query.contains("Lambda.function("), is(false));
// start of a closure
assumeThat("Lambdas are not supported", query.contains("{"), is(false));
assumeThat("withComputer() step is not supported", query.startsWith("g.withComputer("), is(false));
- assumeThat("Vertex instances are not supported", vertexPattern.matcher(query).matches(), is(false));
assumeThat("Edge instances are not supported", edgePattern.matcher(query).matches(), is(false));
assumeThat("fill() terminator is not supported", query.contains("fill("), is(false));
assumeThat("withoutStrategies() is not supported", query.contains("withoutStrategies("), is(false));
diff --git a/gremlin-python/build/generate.groovy b/gremlin-python/build/generate.groovy
index f0a3d45..a633cd7 100644
--- a/gremlin-python/build/generate.groovy
+++ b/gremlin-python/build/generate.groovy
@@ -31,6 +31,16 @@ import java.nio.file.Paths
import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal
+// getting an exception like:
+// > InvocationTargetException: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of
+// > method: org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal.mergeE() is applicable for
+// > argument types: (String) values: [4ffdea36-4a0e-4681-acba-e76875d1b25b]
+// usually means bindings are not being extracted properly by VarAsBindingASTTransformation which typically happens
+// when a step is taking an argument that cannot properly resolve to the type required by the step itself. there are
+// special cases in that VarAsBindingASTTransformation class which might need to be adjusted. Editing the
+// GremlinGroovyScriptEngineTest#shouldProduceBindingsForVars() with the failing step and argument can typically make
+// this issue relatively easy to debug and enforce.
+
// file is overwritten on each generation
radishGremlinFile = new File("${projectBaseDir}/gremlin-python/src/main/python/radish/gremlin.py")
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 5ec3509..7f4a084 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -473,6 +473,30 @@ world.gremlins = {
'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').unfold().mean())],
'g_injectXnull_10_20_nullX_mean': [(lambda g, xx1=None,xx2=None:g.inject(None,xx1,xx2,None).mean())],
'g_injectXlistXnull_10_20_nullXX_meanXlocalX': [(lambda g, xx1=None:g.inject(xx1).mean(Scope.local))],
+ 'g_V_mergeEXlabel_self_weight_05X': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29)), (lambda g, xx1=None:g.V().mergeE(xx1)), (lambda g, xx1=None:g.E().hasLabel('self').has('weight',float(0.5)))],
+ 'g_mergeEXlabel_knows_out_marko_in_vadasX': [(lambda g, xx1=None:g.addV('person').property(T.id_,100).property('name','marko').addV('person').property(T.id_,101).property('name','vadas')), (lambda g, xx1=None:g.mergeE(xx1)), (lambda g, xx1=None:g.V().has('person','name','marko').out('knows').has('person','name','vadas'))],
+ 'g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists': [(lambda g, xx1=None:g.addV('person').property(T.id_,100).property('name','marko').as_('a').addV('person').property(T.id_,101).property('name','vadas').as_('b').addE('knows').from_('a').to('b')), (lambda g, xx1=None:g.mergeE(xx1)), (lambda g, xx1=None:g.V().has('person','name','marko').outE('knows').has('weight',float(0.5)).inV().has('person','name','vadas')), (lambda g, xx1=None:g.V().has('person','name','marko').outE('kno [...]
+ 'g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X': [(lambda g, xx1=None:g.mergeE(xx1)), (lambda g, xx1=None:g.V()), (lambda g, xx1=None:g.E().hasLabel('knows').has('weight',float(0.5)))],
+ 'g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX': [(lambda g, xx1=None,xx3=None,xx2=None:g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3)), (lambda g, xx1=None,xx3=None,xx2=None:g.V()), (lambda g, xx1=None,xx3=None,xx2=None:g.E().hasLabel('knows').has('created','Y')), (lambda g, xx1=None,xx3=None,xx2=None:g.E().hasLabel('knows').has('created','N'))],
+ 'g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists': [(lambda g, xx1=None,xx3=None,xx2=None:g.addV('person').property(T.id_,100).property('name','marko').as_('a').addV('person').property(T.id_,101).property('name','vadas').as_('b').addE('knows').from_('a').to('b')), (lambda g, xx1=None,xx3=None,xx2=None:g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3)), (lambda g, xx1=None,xx3=None,xx2=None:g.V()), (lambda g, xx1=No [...]
+ 'g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated': [(lambda g, xx1=None,xx3=None,xx2=None:g.addV('person').property(T.id_,100).property('name','marko').as_('a').addV('person').property(T.id_,101).property('name','vadas').as_('b').addE('knows').from_('a').to('b').property('created','Y')), (lambda g, xx1=None,xx3=None,xx2=None:g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3)), (lambda g, xx1=None,xx3=None,xx [...]
+ 'g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated': [(lambda g, xx1=None,xx3=None,xx2=None:g.addV('person').property(T.id_,100).property('name','marko').as_('a').addV('person').property(T.id_,101).property('name','vadas').as_('b').addE('knows').from_('a').to('b').property('created','Y').addE('knows').from_('a').to('b')), (lambda g, xx1=None,xx3=None,xx2=None:g.V().has('person','name','marko').mergeE(xx1).option(Merge. [...]
+ 'g_VX100X_VX101X_mergeEXlabel_knows_out_marko_in_vadasX': [(lambda g, xx1=None:g.addV('person').property(T.id_,100).property('name','marko')), (lambda g, xx1=None:g.V(100).V(101).mergeE(xx1)), (lambda g, xx1=None:g.V()), (lambda g, xx1=None:g.E())],
+ 'g_mergeVXlabel_person_name_stephenX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.mergeV(xx1)), (lambda g, xx1=None:g.V().has('person','name','stephen'))],
+ 'g_mergeVXlabel_person_name_markoX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.mergeV(xx1)), (lambda g, xx1=None:g.V().has('person','name','marko'))],
+ 'g_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.mergeV(xx1).option(Merge.onCreate,xx2)), (lambda g, xx1=None,xx2=None:g.V().has('person','name','stephen').has('age',19))],
+ 'g_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.mergeV(xx1).option(Merge.onMatch,xx2)), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko').has('age',19))],
+ 'g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.withSideEffect('c',xx1).withSideEffect('m',xx2).mergeV(__.select('c')).option(Merge.onCreate,__.select('m'))), (lambda g, xx1=None,xx2=None:g.V().has('person','name','stephen').has('age',19))],
+ 'g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.withSideEffect('c',xx1).withSideEffect('m',xx2).mergeV(__.select('c')).option(Merge.onMatch,__.select('m'))), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko').has('age',19))],
+ 'g_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.mergeV(xx1).property('name','vadas','acl','public')), (lambda g, xx1=None:g.V().properties('name').hasValue('vadas').has('acl','public'))],
+ 'g_injectX0X_mergeVXlabel_person_name_stephenX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.inject(0).mergeV(xx1)), (lambda g, xx1=None:g.V().has('person','name','stephen'))],
+ 'g_injectX0X_mergeVXlabel_person_name_markoX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.inject(0).mergeV(xx1)), (lambda g, xx1=None:g.V().has('person','name','marko'))],
+ 'g_injectX0X_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.inject(0).mergeV(xx1).option(Merge.onCreate,xx2)), (lambda g, xx1=None,xx2=None:g.V().has('person','name','stephen').has('age',19))],
+ 'g_injectX0X_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.inject(0).mergeV(xx1).option(Merge.onMatch,xx2)), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko').has('age',19))],
+ 'g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_injectX0X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.withSideEffect('c',xx1).withSideEffect('m',xx2).inject(0).mergeV(__.select('c')).option(Merge.onCreate,__.select('m'))), (lambda g, xx1=None,xx2=None:g.V().has('person','name','stephen').ha [...]
+ 'g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.withSideEffect('c',xx1).withSideEffect('m',xx2).inject(0).mergeV(__.select('c')).option(Merge.onMatch,__.select('m'))), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko').has('age',19))],
+ 'g_injectX0X_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.inject(0).mergeV(xx1).property('name','vadas','acl','public')), (lambda g, xx1=None:g.V().properties('name').hasValue('vadas').has('acl','public'))],
+ 'g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.inject(xx1,xx2).mergeV(__.identity())), (lambda g, xx1=None,xx2=None:g.V().has('person','name','stephen')), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko')), (lambda g, xx1=None,xx2=None:g.V())],
'g_V_age_min': [(lambda g:g.V().age.min_())],
'g_V_foo_min': [(lambda g:g.V().foo.min_())],
'g_V_name_min': [(lambda g:g.V().name.min_())],
diff --git a/gremlin-test/features/map/MergeEdge.feature b/gremlin-test/features/map/MergeEdge.feature
new file mode 100644
index 0000000..65173ce
--- /dev/null
+++ b/gremlin-test/features/map/MergeEdge.feature
@@ -0,0 +1,184 @@
+# 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.
+
+@StepClassMap @StepMergeE
+Feature: Step - mergeE()
+
+ Scenario: g_V_mergeEXlabel_self_weight_05X
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29)
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"self\", \"weight\":\"d[0.5].d\"}]"
+ And the traversal of
+ """
+ g.V().mergeE(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.E().hasLabel(\"self\").has(\"weight\",0.5)"
+
+ @AllowUserSuppliedIds
+ Scenario: g_mergeEXlabel_knows_out_marko_in_vadasX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property(T.id, 100).property("name", "marko").
+ addV("person").property(T.id, 101).property("name", "vadas")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\"}]"
+ And the traversal of
+ """
+ g.mergeE(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").out(\"knows\").has(\"person\",\"name\",\"vadas\")"
+
+ @AllowUserSuppliedIds
+ Scenario: g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property(T.id, 100).property("name", "marko").as("a").
+ addV("person").property(T.id, 101).property("name", "vadas").as("b").
+ addE("knows").from("a").to("b")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\", \"weight\":\"d[0.5].d\"}]"
+ And the traversal of
+ """
+ g.mergeE(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").outE(\"knows\").has(\"weight\",0.5).inV().has(\"person\",\"name\",\"vadas\")"
+ And the graph should return 2 for count of "g.V().has(\"person\",\"name\",\"marko\").outE(\"knows\").inV().has(\"person\",\"name\",\"vadas\")"
+
+ @AllowUserSuppliedIds
+ Scenario: g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X
+ Given the empty graph
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\", \"weight\":\"d[0.5].d\"}]"
+ And the traversal of
+ """
+ g.mergeE(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 2 for count of "g.V()"
+ And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"weight\",0.5)"
+
+ @AllowUserSuppliedIds
+ Scenario: g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX
+ Given the empty graph
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\", \"created\":\"Y\"}]"
+ And using the parameter xx3 defined as "m[{\"t[label]\": \"knows\", \"created\":\"N\"}]"
+ And the traversal of
+ """
+ g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 2 for count of "g.V()"
+ And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"Y\")"
+ And the graph should return 0 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"N\")"
+
+ @AllowUserSuppliedIds
+ Scenario: g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property(T.id, 100).property("name", "marko").as("a").
+ addV("person").property(T.id, 101).property("name", "vadas").as("b").
+ addE("knows").from("a").to("b")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\",\"created\":\"Y\"}]"
+ And using the parameter xx3 defined as "m[{\"created\":\"N\"}]"
+ And the traversal of
+ """
+ g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 2 for count of "g.V()"
+ And the graph should return 0 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"Y\")"
+ And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"N\")"
+
+ @AllowUserSuppliedIds
+ Scenario: g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property(T.id, 100).property("name", "marko").as("a").
+ addV("person").property(T.id, 101).property("name", "vadas").as("b").
+ addE("knows").from("a").to("b").property("created","Y")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\",\"created\":\"Y\"}]"
+ And using the parameter xx3 defined as "m[{\"created\":\"N\"}]"
+ And the traversal of
+ """
+ g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 2 for count of "g.V()"
+ And the graph should return 0 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"Y\")"
+ And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"N\")"
+
+ @AllowUserSuppliedIds
+ Scenario: g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property(T.id, 100).property("name", "marko").as("a").
+ addV("person").property(T.id, 101).property("name", "vadas").as("b").
+ addE("knows").from("a").to("b").property("created","Y").
+ addE("knows").from("a").to("b")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\",\"created\":\"Y\"}]"
+ And using the parameter xx3 defined as "m[{\"created\":\"N\"}]"
+ And the traversal of
+ """
+ g.V().has("person","name","marko").mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3)
+ """
+ When iterated to list
+ Then the result should have a count of 2
+ And the graph should return 2 for count of "g.V()"
+ And the graph should return 2 for count of "g.E()"
+ And the graph should return 0 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"Y\")"
+ And the graph should return 2 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"N\")"
+
+ @AllowUserSuppliedIds
+ Scenario: g_VX100X_VX101X_mergeEXlabel_knows_out_marko_in_vadasX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property(T.id, 100).property("name", "marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\"}]"
+ And the traversal of
+ """
+ g.V(100).V(101).mergeE(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 0
+ And the graph should return 1 for count of "g.V()"
+ And the graph should return 0 for count of "g.E()"
\ No newline at end of file
diff --git a/gremlin-test/features/map/MergeVertex.feature b/gremlin-test/features/map/MergeVertex.feature
new file mode 100644
index 0000000..385f2f8
--- /dev/null
+++ b/gremlin-test/features/map/MergeVertex.feature
@@ -0,0 +1,265 @@
+# 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.
+
+@StepClassMap @StepMergeV
+Feature: Step - mergeV()
+
+ Scenario: g_mergeVXlabel_person_name_stephenX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\"}]"
+ And the traversal of
+ """
+ g.mergeV(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\")"
+
+ Scenario: g_mergeVXlabel_person_name_markoX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And the traversal of
+ """
+ g.mergeV(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\")"
+
+ Scenario: g_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\", \"age\": \"d[19].i\"}]"
+ And the traversal of
+ """
+ g.mergeV(xx1).option(Merge.onCreate,xx2)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\").has(\"age\", 19)"
+
+ Scenario: g_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And using the parameter xx2 defined as "m[{\"age\": \"d[19].i\"}]"
+ And the traversal of
+ """
+ g.mergeV(xx1).option(Merge.onMatch,xx2)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\", 19)"
+
+ Scenario: g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\", \"age\": \"d[19].i\"}]"
+ And the traversal of
+ """
+ g.withSideEffect("c", xx1).
+ withSideEffect("m", xx2).
+ mergeV(__.select("c")).option(Merge.onCreate, __.select("m"))
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\").has(\"age\", 19)"
+
+ Scenario: g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And using the parameter xx2 defined as "m[{\"age\": \"d[19].i\"}]"
+ And the traversal of
+ """
+ g.withSideEffect("c", xx1).
+ withSideEffect("m", xx2).
+ mergeV(__.select("c")).option(Merge.onMatch, __.select("m"))
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\", 19)"
+
+ @MultiMetaProperties
+ Scenario: g_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And the traversal of
+ """
+ g.mergeV(xx1).property("name","vadas","acl","public")
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().properties(\"name\").hasValue(\"vadas\").has(\"acl\",\"public\")"
+
+ Scenario: g_injectX0X_mergeVXlabel_person_name_stephenX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\"}]"
+ And the traversal of
+ """
+ g.inject(0).mergeV(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\")"
+
+ Scenario: g_injectX0X_mergeVXlabel_person_name_markoX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And the traversal of
+ """
+ g.inject(0).mergeV(xx1)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\")"
+
+ Scenario: g_injectX0X_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\", \"age\": \"d[19].i\"}]"
+ And the traversal of
+ """
+ g.inject(0).mergeV(xx1).option(Merge.onCreate,xx2)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\").has(\"age\", 19)"
+
+ Scenario: g_injectX0X_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And using the parameter xx2 defined as "m[{\"age\": \"d[19].i\"}]"
+ And the traversal of
+ """
+ g.inject(0).mergeV(xx1).option(Merge.onMatch,xx2)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\", 19)"
+
+ Scenario: g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_injectX0X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\", \"age\": \"d[19].i\"}]"
+ And the traversal of
+ """
+ g.withSideEffect("c", xx1).
+ withSideEffect("m", xx2).
+ inject(0).mergeV(__.select("c")).option(Merge.onCreate, __.select("m"))
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\").has(\"age\", 19)"
+
+ Scenario: g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And using the parameter xx2 defined as "m[{\"age\": \"d[19].i\"}]"
+ And the traversal of
+ """
+ g.withSideEffect("c", xx1).
+ withSideEffect("m", xx2).
+ inject(0).mergeV(__.select("c")).option(Merge.onMatch, __.select("m"))
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\", 19)"
+
+ @MultiMetaProperties
+ Scenario: g_injectX0X_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And the traversal of
+ """
+ g.inject(0).mergeV(xx1).property("name","vadas","acl","public")
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().properties(\"name\").hasValue(\"vadas\").has(\"acl\",\"public\")"
+
+ Scenario: g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property("age", 29).as("marko")
+ """
+ And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+ And using the parameter xx2 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\"}]"
+ And the traversal of
+ """
+ g.inject(xx1, xx2).mergeV(__.identity())
+ """
+ When iterated to list
+ Then the result should have a count of 2
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\")"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\")"
+ And the graph should return 2 for count of "g.V()"
diff --git a/gremlin-test/features/sideEffect/Group.feature b/gremlin-test/features/sideEffect/Group.feature
index d4d3867..b8e7b5c 100644
--- a/gremlin-test/features/sideEffect/Group.feature
+++ b/gremlin-test/features/sideEffect/Group.feature
@@ -266,7 +266,7 @@ Feature: Step - group()
# The post-ordering really isn't really right but works around TINKERPOP-2600
Scenario: g_withSideEffectXa__marko_666_noone_blahX_V_groupXaX_byXnameX_byXoutE_label_foldX_capXaX
Given the modern graph
- And using the parameter xx1 defined as "m[{\"marko\":[\"666\"], \"noone\":[\"blah\"]}]"
+ And using the parameter xx1 defined as "m[{\"marko\":l[\"666\"], \"noone\":l[\"blah\"]}]"
And the traversal of
"""
g.withSideEffect("a", xx1).V().group("a").by("name").by(__.outE().label().fold()).cap("a").unfold().group().by(keys).by(select(values).order(Scope.local).by(Order.asc))
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 b8021c1..ea5eed3 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
@@ -38,6 +38,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSo
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import org.apache.tinkerpop.shaded.jackson.databind.JsonNode;
@@ -64,10 +65,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -88,10 +91,21 @@ public final class StepDefinition {
private static final Pattern edgeTripletPattern = Pattern.compile("(.+)-(.+)->(.+)");
private static final Pattern ioPattern = Pattern.compile("g\\.io\\(\"(.*)\"\\).*");
private List<Pair<Pattern, Function<String,String>>> stringMatcherConverters = new ArrayList<Pair<Pattern, Function<String,String>>>() {{
- // expects json so that should port to the Gremlin script form - replace curly json braces with square ones
- // for Gremlin sake.
- add(Pair.with(Pattern.compile("m\\[(.*)\\]"), s -> s.replace('{','[').replace('}', ']')));
-
+ // expects json so that should port to the Gremlin script form
+ add(Pair.with(Pattern.compile("m\\[(.*)\\]"), s -> {
+ // can't handled embedded maps because of the string replace below on the curly braces
+ final String[] items = s.replace("{", "").replace("}", "").split(",");
+ final String listItems = Stream.of(items).map(String::trim).map(x -> {
+ final String[] pair = x.split(":");
+
+ // if wrapping double quotes aren't removed they end up re-wrapping again for pure string values
+ // on conversion
+ final String convertedKey = convertToString(pair[0].trim().replace("\"", ""));
+ final String convertedValue = convertToString(pair[1].trim().replace("\"", ""));
+ return String.format("%s:%s", convertedKey, convertedValue);
+ }).collect(Collectors.joining(","));
+ return String.format("[%s]", listItems);
+ }));
add(Pair.with(Pattern.compile("l\\[\\]"), s -> "[]"));
add(Pair.with(Pattern.compile("l\\[(.*)\\]"), s -> {
final String[] items = s.split(",");
@@ -109,9 +123,12 @@ public final class StepDefinition {
add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.id"), s -> g.V().has("name", s).id().next().toString()));
add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.sid"), s -> g.V().has("name", s).id().next().toString()));
+ add(Pair.with(Pattern.compile("v\\[(.+)\\]"), s -> {
+ final Iterator<Object> itty = g.V().has("name", s).id();
+ return String.format("new Vertex(\"%s\",\"%s\")", itty.hasNext() ? itty.next() : s, Vertex.DEFAULT_LABEL);
+ }));
add(Pair.with(Pattern.compile("e\\[(.+)\\]\\.id"), s -> getEdgeIdString(g, s)));
add(Pair.with(Pattern.compile("e\\[(.+)\\]\\.sid"), s -> getEdgeIdString(g, s)));
-
add(Pair.with(Pattern.compile("t\\[(.*)\\]"), s -> String.format("T.%s", s)));
add(Pair.with(Pattern.compile("D\\[(.*)\\]"), s -> String.format("Direction.%s", s)));
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedStandardSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedStandardSuite.java
index e9c4602..e55d7bb 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedStandardSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedStandardSuite.java
@@ -23,65 +23,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.CoreTraversalTest;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.ComplexTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.OrderabilityTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.branch.BranchTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.branch.ChooseTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.branch.OptionalTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.branch.RepeatTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.AndTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.CoinTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.CyclicPathTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DropTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.FilterTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.IsTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.OrTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.SampleTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.SimplePathTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TailTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WhereTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.CoalesceTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConstantTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.ElementMapTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.FlatMapTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.FoldTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.IndexTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.LoopsTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MapTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MathTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.MinTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.OrderTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.PathTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.ProfileTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.ProjectTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.UnfoldTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.ValueMapTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.WriteTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AggregateTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.ExplainTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupCountTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SackTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SideEffectCapTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SideEffectTest;
-import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.StoreTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TreeTest;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategyProcessTest;
@@ -93,8 +39,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.Transl
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategyProcessTest;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IncidentToAdjacentStrategyProcessTest;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategyProcessTest;
-import org.apache.tinkerpop.gremlin.structure.Graph;
-import org.apache.tinkerpop.gremlin.structure.StructureStandardSuite;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchTest.java
index ed4b2e7..642cda4 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchTest.java
@@ -40,8 +40,8 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.identi
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.label;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.values;
-import static org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick.any;
-import static org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick.none;
+import static org.apache.tinkerpop.gremlin.process.traversal.Pick.any;
+import static org.apache.tinkerpop.gremlin.process.traversal.Pick.none;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java
index 28f3abe..d1f4933 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java
@@ -22,9 +22,9 @@ import org.apache.tinkerpop.gremlin.LoadGraphWith;
import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.MapHelper;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.junit.Test;
@@ -195,7 +195,7 @@ public abstract class ChooseTest extends AbstractGremlinProcessTest {
return g.V().choose(label())
.option("blah", out("knows"))
.option("bleep", out("created"))
- .option(TraversalOptionParent.Pick.none, identity()).values("name");
+ .option(Pick.none, identity()).values("name");
}
@Override
@@ -212,7 +212,7 @@ public abstract class ChooseTest extends AbstractGremlinProcessTest {
public Traversal<Vertex, Map<String, Long>> get_g_V_hasLabelXpersonX_chooseXageX__optionX27L__constantXyoungXX_optionXnone__constantXoldXX_groupCount() {
return g.V().hasLabel("person").choose(values("age"))
.option(27L, __.constant("young"))
- .option(TraversalOptionParent.Pick.none, __.constant("old"))
+ .option(Pick.none, __.constant("old"))
.groupCount();
}
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
index 5aa9823..ed1e6a2 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
import org.apache.tinkerpop.gremlin.FeatureRequirement;
import org.apache.tinkerpop.gremlin.FeatureRequirementSet;
import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.MutationListener;
import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -40,7 +41,9 @@ import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import org.junit.Test;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -83,6 +86,31 @@ public class EventStrategyProcessTest extends AbstractGremlinProcessTest {
@Test
@FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+ public void shouldTriggerAddVertexViaMergeV() {
+ final StubMutationListener listener1 = new StubMutationListener();
+ final StubMutationListener listener2 = new StubMutationListener();
+ final EventStrategy.Builder builder = EventStrategy.build()
+ .addListener(listener1)
+ .addListener(listener2);
+
+ if (graph.features().graph().supportsTransactions())
+ builder.eventQueue(new EventStrategy.TransactionalEventQueue(graph));
+
+ final EventStrategy eventStrategy = builder.create();
+
+ graph.addVertex("some", "thing");
+ final GraphTraversalSource gts = create(eventStrategy);
+ final Map<Object,Object> m = new HashMap<>();
+ m.put("any", "thing");
+ gts.V().mergeV(m).property("any", "thing").next();
+
+ tryCommit(graph, g -> assertEquals(1, IteratorUtils.count(gts.V().has("any", "thing"))));
+ assertEquals(1, listener1.addVertexEventRecorded());
+ assertEquals(1, listener2.addVertexEventRecorded());
+ }
+
+ @Test
+ @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
public void shouldTriggerAddVertexFromStart() {
final StubMutationListener listener1 = new StubMutationListener();
final StubMutationListener listener2 = new StubMutationListener();
@@ -246,6 +274,40 @@ public class EventStrategyProcessTest extends AbstractGremlinProcessTest {
@Test
@FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+ public void shouldTriggerAddVertexPropertyChangedViaMergeV() {
+ final StubMutationListener listener1 = new StubMutationListener();
+ final StubMutationListener listener2 = new StubMutationListener();
+ final EventStrategy.Builder builder = EventStrategy.build()
+ .addListener(listener1)
+ .addListener(listener2);
+
+ if (graph.features().graph().supportsTransactions())
+ builder.eventQueue(new EventStrategy.TransactionalEventQueue(graph));
+
+ final EventStrategy eventStrategy = builder.create();
+
+ final Vertex vSome = graph.addVertex("some", "thing");
+ vSome.property(VertexProperty.Cardinality.single, "that", "thing");
+ final GraphTraversalSource gts = create(eventStrategy);
+
+ final Map<Object,Object> m1 = new HashMap<>();
+ m1.put("any", "thing");
+ gts.mergeV(m1).iterate();
+
+ final Map<Object,Object> m2 = new HashMap<>();
+ m2.put("any", "thing else");
+ gts.mergeV(m1).option(Merge.onMatch, m2).iterate();
+
+ tryCommit(graph, g -> assertEquals(1, IteratorUtils.count(gts.V().has("any", "thing else"))));
+
+ assertEquals(1, listener1.addVertexEventRecorded());
+ assertEquals(1, listener2.addVertexEventRecorded());
+ assertEquals(1, listener2.vertexPropertyChangedEventRecorded());
+ assertEquals(1, listener1.vertexPropertyChangedEventRecorded());
+ }
+
+ @Test
+ @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
@FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
public void shouldTriggerAddVertexPropertyPropertyChanged() {
final StubMutationListener listener1 = new StubMutationListener();
diff --git a/gremlin-tools/gremlin-io-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Model.java b/gremlin-tools/gremlin-io-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
index 87e5f86..98f3f35 100644
--- a/gremlin-tools/gremlin-io-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
+++ b/gremlin-tools/gremlin-io-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
@@ -26,12 +26,12 @@ import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalMetrics;
import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics;
@@ -178,7 +178,7 @@ public class Model {
addGraphProcessEntry(Direction.OUT, "Direction", "", Compatibilities.UNTYPED_GRAPHSON.matchToArray());
addGraphProcessEntry(Operator.sum, "Operator", "", Compatibilities.UNTYPED_GRAPHSON.matchToArray());
addGraphProcessEntry(Order.shuffle, "Order", "", before3_5_0);
- addGraphProcessEntry(TraversalOptionParent.Pick.any, "Pick", "", Compatibilities.UNTYPED_GRAPHSON.matchToArray());
+ addGraphProcessEntry(Pick.any, "Pick", "", Compatibilities.UNTYPED_GRAPHSON.matchToArray());
addGraphProcessEntry(Pop.all, "Pop", "", Compatibilities.UNTYPED_GRAPHSON.matchToArray());
addGraphProcessEntry(org.apache.tinkerpop.gremlin.util.function.Lambda.function("{ it.get() }"), "Lambda", "", Compatibilities.UNTYPED_GRAPHSON.matchToArray());
final TraversalMetrics tm = createStaticTraversalMetrics();
diff --git a/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/_3_2_3/manual-graphson-generator.groovy b/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/_3_2_3/manual-graphson-generator.groovy
index 878f6a3..3806884 100644
--- a/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/_3_2_3/manual-graphson-generator.groovy
+++ b/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/_3_2_3/manual-graphson-generator.groovy
@@ -22,16 +22,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalMetri
import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics
import java.time.*
-import java.nio.file.*
+
import org.apache.tinkerpop.gremlin.driver.ser.*
import org.apache.tinkerpop.gremlin.process.traversal.*
import org.apache.tinkerpop.gremlin.tinkergraph.structure.*
import org.apache.tinkerpop.gremlin.structure.*
import org.apache.tinkerpop.gremlin.structure.io.graphson.*
import org.apache.tinkerpop.gremlin.driver.message.*
-import org.apache.tinkerpop.gremlin.process.traversal.step.*
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick
-import org.apache.tinkerpop.gremlin.structure.io.gryo.*
import org.apache.commons.configuration.BaseConfiguration
import java.util.concurrent.TimeUnit
diff --git a/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/_3_2_4/manual-graphson-generator.groovy b/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/_3_2_4/manual-graphson-generator.groovy
index e1ff14d..6b497b7 100644
--- a/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/_3_2_4/manual-graphson-generator.groovy
+++ b/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/_3_2_4/manual-graphson-generator.groovy
@@ -22,16 +22,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalMetri
import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics
import java.time.*
-import java.nio.file.*
+
import org.apache.tinkerpop.gremlin.driver.ser.*
import org.apache.tinkerpop.gremlin.process.traversal.*
import org.apache.tinkerpop.gremlin.tinkergraph.structure.*
import org.apache.tinkerpop.gremlin.structure.*
import org.apache.tinkerpop.gremlin.structure.io.graphson.*
import org.apache.tinkerpop.gremlin.driver.message.*
-import org.apache.tinkerpop.gremlin.process.traversal.step.*
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick
-import org.apache.tinkerpop.gremlin.structure.io.gryo.*
import org.apache.commons.configuration.BaseConfiguration
import java.util.concurrent.TimeUnit
diff --git a/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/_3_2_3/manual-gryo-generator.groovy b/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/_3_2_3/manual-gryo-generator.groovy
index d231aa7..4f0dfac 100644
--- a/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/_3_2_3/manual-gryo-generator.groovy
+++ b/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/_3_2_3/manual-gryo-generator.groovy
@@ -21,19 +21,13 @@
import org.apache.commons.configuration.BaseConfiguration
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalMetrics
import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics
-import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics
import org.apache.tinkerpop.shaded.kryo.io.Output
import java.time.*
-import java.nio.file.*
-import org.apache.tinkerpop.gremlin.driver.ser.*
+
import org.apache.tinkerpop.gremlin.process.traversal.*
import org.apache.tinkerpop.gremlin.tinkergraph.structure.*
import org.apache.tinkerpop.gremlin.structure.*
-import org.apache.tinkerpop.gremlin.structure.io.graphson.*
-import org.apache.tinkerpop.gremlin.driver.message.*
-import org.apache.tinkerpop.gremlin.process.traversal.step.*
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick
import org.apache.tinkerpop.gremlin.structure.io.gryo.*
import java.util.concurrent.TimeUnit
diff --git a/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/_3_2_4/manual-gryo-generator.groovy b/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/_3_2_4/manual-gryo-generator.groovy
index 6e66f36..d67eec4 100644
--- a/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/_3_2_4/manual-gryo-generator.groovy
+++ b/gremlin-tools/gremlin-io-test/src/test/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/_3_2_4/manual-gryo-generator.groovy
@@ -21,19 +21,13 @@
import org.apache.commons.configuration.BaseConfiguration
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalMetrics
import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics
-import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics
import org.apache.tinkerpop.shaded.kryo.io.Output
import java.time.*
-import java.nio.file.*
-import org.apache.tinkerpop.gremlin.driver.ser.*
+
import org.apache.tinkerpop.gremlin.process.traversal.*
import org.apache.tinkerpop.gremlin.tinkergraph.structure.*
import org.apache.tinkerpop.gremlin.structure.*
-import org.apache.tinkerpop.gremlin.structure.io.graphson.*
-import org.apache.tinkerpop.gremlin.driver.message.*
-import org.apache.tinkerpop.gremlin.process.traversal.step.*
-import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick
import org.apache.tinkerpop.gremlin.structure.io.gryo.*
import java.util.concurrent.TimeUnit
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/TinkerMergeVertexStep.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/TinkerMergeVertexStep.java
new file mode 100644
index 0000000..5876879
--- /dev/null
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/TinkerMergeVertexStep.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tinkergraph.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerHelper;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+
+/**
+ * Optimizes {@code mergeV()} searches by attempting to use an index where possible.
+ */
+public class TinkerMergeVertexStep<S> extends MergeVertexStep<S> {
+ public TinkerMergeVertexStep(final MergeVertexStep step) {
+ super(step.getTraversal(), step.isStart(), step.getSearchCreateTraversal());
+ if (step.getOnMatchTraversal() != null) this.addChildOption(Merge.onMatch, step.getOnMatchTraversal());
+ if (step.getOnCreateTraversal() != null) this.addChildOption(Merge.onCreate, step.getOnCreateTraversal());
+ }
+
+ @Override
+ protected Stream<Vertex> createSearchStream(final Map<Object, Object> search) {
+ final TinkerGraph graph = (TinkerGraph) this.getTraversal().getGraph().get();
+ Optional<String> firstIndex = Optional.empty();
+
+ Stream<Vertex> stream;
+ // prioritize lookup by id but otherwise attempt an index lookup
+ if (search.containsKey(T.id)) {
+ stream = IteratorUtils.stream(graph.vertices(search.get(T.id)));
+ } else {
+ // look for the first index we can find - that's the lucky winner. may or may not be the most selective
+ final Set<String> indexedKeys = graph.getIndexedKeys(Vertex.class);
+ firstIndex = search.keySet().stream().
+ filter(k -> k instanceof String).
+ map(k -> (String) k).
+ filter(indexedKeys::contains).findFirst();
+
+ // use the index if possible otherwise just in memory filter
+ stream = firstIndex.map(s -> TinkerHelper.queryVertexIndex(graph, s, search.get(s)).stream().map(v -> (Vertex) v)).
+ orElseGet(() -> IteratorUtils.stream(graph.vertices()));
+ }
+
+ final Optional<String> indexUsed = firstIndex;
+ stream = stream.filter(v -> {
+ // try to match on all search criteria skipping T.id as it was handled above
+ return search.entrySet().stream().filter(kv -> {
+ final Object k = kv.getKey();
+ return k != T.id && !(indexUsed.isPresent() && indexUsed.get().equals(k));
+ }).allMatch(kv -> {
+ if (kv.getKey() == T.label) {
+ return v.label().equals(kv.getValue());
+ } else {
+ final VertexProperty<Object> vp = v.property(kv.getKey().toString());
+ return vp.isPresent() && kv.getValue().equals(vp.value());
+ }
+ });
+ });
+
+ return stream;
+ }
+}
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/strategy/optimization/TinkerMergeVertexStepStrategy.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/strategy/optimization/TinkerMergeVertexStepStrategy.java
new file mode 100644
index 0000000..e5806ea
--- /dev/null
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/strategy/optimization/TinkerMergeVertexStepStrategy.java
@@ -0,0 +1,53 @@
+/*
+ * 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.tinkergraph.process.traversal.strategy.optimization;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.apache.tinkerpop.gremlin.tinkergraph.process.traversal.step.map.TinkerMergeVertexStep;
+
+/**
+ * Optimizes {@code mergeV()} search lookups by using {@link TinkerMergeVertexStep}.
+ */
+public final class TinkerMergeVertexStepStrategy extends AbstractTraversalStrategy<TraversalStrategy.ProviderOptimizationStrategy>
+ implements TraversalStrategy.ProviderOptimizationStrategy {
+
+ private static final TinkerMergeVertexStepStrategy INSTANCE = new TinkerMergeVertexStepStrategy();
+
+ private TinkerMergeVertexStepStrategy() {
+ }
+
+ @Override
+ public void apply(final Traversal.Admin<?, ?> traversal) {
+ if (TraversalHelper.onGraphComputer(traversal))
+ return;
+
+ for (final MergeVertexStep originalMergeVertexStep : TraversalHelper.getStepsOfClass(MergeVertexStep.class, traversal)) {
+ final TinkerMergeVertexStep tinkerMergeVertexStep = new TinkerMergeVertexStep(originalMergeVertexStep);
+ TraversalHelper.replaceStep(originalMergeVertexStep, tinkerMergeVertexStep, traversal);
+ }
+ }
+
+ public static TinkerMergeVertexStepStrategy instance() {
+ return INSTANCE;
+ }
+}
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
index b9dda16..514d008 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
@@ -39,6 +39,7 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComputerView;
import org.apache.tinkerpop.gremlin.tinkergraph.process.traversal.strategy.optimization.TinkerGraphCountStrategy;
import org.apache.tinkerpop.gremlin.tinkergraph.process.traversal.strategy.optimization.TinkerGraphStepStrategy;
+import org.apache.tinkerpop.gremlin.tinkergraph.process.traversal.strategy.optimization.TinkerMergeVertexStepStrategy;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import java.io.File;
@@ -72,7 +73,8 @@ public final class TinkerGraph implements Graph {
static {
TraversalStrategies.GlobalCache.registerStrategies(TinkerGraph.class, TraversalStrategies.GlobalCache.getStrategies(Graph.class).clone().addStrategies(
TinkerGraphStepStrategy.instance(),
- TinkerGraphCountStrategy.instance()));
+ TinkerGraphCountStrategy.instance(),
+ TinkerMergeVertexStepStrategy.instance()));
}
private static final Configuration EMPTY_CONFIGURATION = new BaseConfiguration() {{