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 2023/06/21 19:43:28 UTC
[tinkerpop] 02/02: TINKERPOP-2957 allow cardinality to be specified for mergeV()
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch TINKERPOP-2957
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit df1297b12aecf0fbfcea67589d69167a0021eb8a
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Wed Jun 21 11:49:52 2023 -0400
TINKERPOP-2957 allow cardinality to be specified for mergeV()
Provides a nicer way to allow users to control cardinality of property values when using mergeV(). A bit tough to get this in without changes to the serializers but by treating the CardinalityValue as a special form of Bytecode it allowed this change to keep the serializers sacred.
---
CHANGELOG.asciidoc | 1 +
docs/src/upgrade/release-3.6.x.asciidoc | 36 ++++++
.../tinkerpop/gremlin/jsr223/JavaTranslator.java | 34 ++++--
.../grammar/DefaultGremlinBaseVisitor.java | 2 +-
.../language/grammar/GenericLiteralVisitor.java | 38 +++---
.../language/grammar/TraversalMethodVisitor.java | 10 ++
.../gremlin/process/traversal/Bytecode.java | 4 +-
.../gremlin/process/traversal/Translator.java | 14 ++-
.../traversal/dsl/graph/GraphTraversal.java | 24 ++++
.../CardinalityValueTraversal.java} | 47 ++++----
.../traversal/step/map/MergeVertexStep.java | 17 ++-
.../process/traversal/step/util/Parameters.java | 1 -
.../traversal/translator/DotNetTranslator.java | 10 ++
.../traversal/translator/GolangTranslator.java | 10 ++
.../traversal/translator/GroovyTranslator.java | 9 ++
.../traversal/translator/JavascriptTranslator.java | 9 ++
.../traversal/translator/PythonTranslator.java | 9 ++
.../gremlin/structure/VertexProperty.java | 18 ++-
.../structure/io/binary/GraphBinaryWriter.java | 4 +-
.../io/binary/types/TransformSerializer.java | 2 +-
.../grammar/GeneralLiteralVisitorTest.java | 34 +++---
.../gremlin/process/traversal/CardinalityTest.java | 68 +++++++++++
.../process/traversal/CardinalityValueTest.java | 64 ----------
.../traversal/translator/DotNetTranslatorTest.java | 5 +
.../traversal/translator/GolangTranslatorTest.java | 6 +
.../traversal/translator/GroovyTranslatorTest.java | 6 +
.../translator/JavascriptTranslatorTest.java | 6 +
.../traversal/translator/PythonTranslatorTest.java | 14 +++
.../Process/Traversal/CardinalityValue.cs | 88 ++++++++++++++
.../Process/Traversal/GraphTraversal.cs | 9 ++
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 6 +
.../Process/Traversal/CardinalityValueTests.cs | 55 +++++++++
gremlin-go/driver/cucumber/gremlin.go | 6 +
gremlin-go/driver/traversal.go | 34 +++++-
gremlin-javascript/build/generate.groovy | 1 +
.../lib/process/graph-traversal.js | 42 ++++++-
.../gremlin-javascript/test/cucumber/gremlin.js | 7 ++
gremlin-language/src/main/antlr4/Gremlin.g4 | 16 +--
gremlin-python/build/generate.groovy | 2 +-
.../python/gremlin_python/process/traversal.py | 18 +++
gremlin-python/src/main/python/radish/gremlin.py | 8 +-
.../gremlin/test/features/map/MergeVertex.feature | 129 +++++++++++++++++++++
42 files changed, 770 insertions(+), 153 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index de83cce8cf..4dc866639e 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
This release also includes changes from <<release-3-5-7, 3.5.7>>.
+* Allowed `mergeV()` to more easily define `Cardinality` values for properties for `onMatch` and `onCreate` options.
* Added `text/plain` MIME type to the HTTP endpoint to return a Gremlin Console-like representation of the data.
* Added GraphBinary serialization option to the HTTP endpoint.
* Fixed bug with `fail` step not working with a `VertexProgram` running on the server.
diff --git a/docs/src/upgrade/release-3.6.x.asciidoc b/docs/src/upgrade/release-3.6.x.asciidoc
index d54ad7a38c..2154a3be6d 100644
--- a/docs/src/upgrade/release-3.6.x.asciidoc
+++ b/docs/src/upgrade/release-3.6.x.asciidoc
@@ -48,6 +48,42 @@ $ curl -H "Accept:text/plain" -X POST -d "{\"gremlin\":\"g.V()\"}" "http://local
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2947[TINKERPOP-2947]
+==== mergeV() and Cardinality
+
+The `mergeV()` step makes it much easier to write upsert-like traversals. Of course, if you had a graph that required
+the use of multi-properties, some of the ease of `mergeV()` was lost. It typically meant falling back to traversals
+using `sideEffect()` or similar direct uses of `property()` to allow it to work properly:
+
+[source,groovy]
+----
+g.mergeV([(T.id): '1234']).
+ option(onMatch, sideEffect(property(single,'age', 20).
+ property(set,'city','miami')).constant([:]))
+----
+
+For this version, `mergeV()` gets two new bits of syntax. First, it is possible to individually define the cardinality
+for each property value in the `Map` for `onCreate` or `onMerge` events. Therefore, the above example could be written
+as:
+
+[source,groovy]
+----
+g.mergeV([(T.id): '1234']).
+ option(onMatch, ['age': single(20), 'city': set('miami')])
+----
+
+The other option available is to provide a default `Cardinality` to the `option()` as follows:
+
+[source,groovy]
+----
+g.mergeV([(T.id): '1234']).
+ option(onMatch, ['age': 20, 'city': set('miami')], single)
+----
+
+In the above example, any property value that does not have its cardinality explicitly defined, will be assumed to be
+the cardinality of the argument specified.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2957[TINKERPOP-2957]
+
== TinkerPop 3.6.4
*Release Date: May 12, 2023*
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
index 91bc19cc0f..830ca2093e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
@@ -26,6 +26,7 @@ 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.lambda.CardinalityValueTraversal;
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;
@@ -111,14 +112,33 @@ public final class JavaTranslator<S extends TraversalSource, T extends Traversal
if (object instanceof Bytecode.Binding)
return translateObject(((Bytecode.Binding) object).value());
else if (object instanceof Bytecode) {
- try {
- final Traversal.Admin<?, ?> traversal = (Traversal.Admin) this.anonymousTraversalStart.invoke(null);
- for (final Bytecode.Instruction instruction : ((Bytecode) object).getStepInstructions()) {
- invokeMethod(traversal, Traversal.class, instruction.getOperator(), instruction.getArguments());
+ // source based bytecode at this stage of translation could have special meaning, but generally this is
+ // going to spawn a new anonymous traversal.
+ final Bytecode bc = (Bytecode) object;
+ if (!bc.getSourceInstructions().isEmpty()) {
+ // currently, valid source instructions will be singly defined. would be odd to get this error. could
+ // be just bad construction from a language variant if it appears. maybe better as an assertion but
+ // third-party variants might benefit from this error
+ if (bc.getSourceInstructions().size() != 1) {
+ throw new IllegalStateException("More than one source instruction defined in bytecode");
+ }
+
+ final Bytecode.Instruction inst = bc.getSourceInstructions().get(0);
+ if (inst.getOperator().equals(CardinalityValueTraversal.class.getSimpleName())) {
+ return CardinalityValueTraversal.from(inst);
+ } else {
+ throw new IllegalStateException(String.format("Unknown source instruction for %s", inst.getOperator()));
+ }
+ } else {
+ try {
+ final Traversal.Admin<?, ?> traversal = (Traversal.Admin) this.anonymousTraversalStart.invoke(null);
+ for (final Bytecode.Instruction instruction : bc.getStepInstructions()) {
+ invokeMethod(traversal, Traversal.class, instruction.getOperator(), instruction.getArguments());
+ }
+ return traversal;
+ } catch (final Throwable e) {
+ throw new IllegalStateException(e.getMessage());
}
- return traversal;
- } catch (final Throwable e) {
- throw new IllegalStateException(e.getMessage());
}
} else if (object instanceof TraversalStrategyProxy) {
final Map<String, Object> map = new HashMap<>();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
index e24640eeb1..bbb79de7cd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
@@ -1436,5 +1436,5 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im
* {@inheritDoc}
*/
@Override
- public T visitTraversalCardinalityValue(final GremlinParser.TraversalCardinalityValueContext ctx) { notImplemented(ctx); return null; }
+ public T visitTraversalMethod_option_Merge_Map_Cardinality(final GremlinParser.TraversalMethod_option_Merge_Map_CardinalityContext ctx) { notImplemented(ctx); return null; }
}
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 7e604efe80..61863b2ae9 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
@@ -21,7 +21,6 @@ package org.apache.tinkerpop.gremlin.language.grammar;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.text.StringEscapeUtils;
-import org.apache.tinkerpop.gremlin.process.traversal.CardinalityValue;
import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -509,7 +508,23 @@ public class GenericLiteralVisitor extends DefaultGremlinBaseVisitor<Object> {
*/
@Override
public Object visitTraversalCardinality(final GremlinParser.TraversalCardinalityContext ctx) {
- return TraversalEnumParser.parseTraversalEnumFromContext(VertexProperty.Cardinality.class, ctx);
+ // could be Cardinality.single() the method or single the enum so grab the right child index based on
+ // number of children
+ if (ctx.getChildCount() == 1) {
+ return TraversalEnumParser.parseTraversalEnumFromContext(VertexProperty.Cardinality.class, ctx);
+ } else {
+ final int idx = ctx.getChildCount() == 5 ? 1 : 0;
+ final String specifiedCard = ctx.children.get(idx).getText();
+ if (specifiedCard.endsWith(VertexProperty.Cardinality.single.name()))
+ return VertexProperty.Cardinality.single(visitGenericLiteral(ctx.genericLiteral()));
+ else if (specifiedCard.endsWith(VertexProperty.Cardinality.list.name()))
+ return VertexProperty.Cardinality.list(visitGenericLiteral(ctx.genericLiteral()));
+ else if (specifiedCard.endsWith(VertexProperty.Cardinality.set.name()))
+ return VertexProperty.Cardinality.set(visitGenericLiteral(ctx.genericLiteral()));
+ else
+ throw new GremlinParserException(String.format(
+ "A Cardinality value not recognized: %s", specifiedCard));
+ }
}
/**
@@ -612,23 +627,4 @@ public class GenericLiteralVisitor extends DefaultGremlinBaseVisitor<Object> {
}
return result;
}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object visitTraversalCardinalityValue(final GremlinParser.TraversalCardinalityValueContext ctx) {
- // could be CardinalityValue.single or single so grab the right child index based on number of children
- final int idx = ctx.getChildCount() == 5 ? 1 : 0;
- final String specifiedCard = ctx.children.get(idx).getText();
- if (specifiedCard.equals(VertexProperty.Cardinality.single.name()))
- return CardinalityValue.single(visitGenericLiteral(ctx.genericLiteral()));
- else if (specifiedCard.equals(VertexProperty.Cardinality.list.name()))
- return CardinalityValue.list(visitGenericLiteral(ctx.genericLiteral()));
- else if (specifiedCard.equals(VertexProperty.Cardinality.set.name()))
- return CardinalityValue.set(visitGenericLiteral(ctx.genericLiteral()));
- else
- throw new GremlinParserException(String.format(
- "A CardinalityValue must be defined as one of the available Cardinality values, not %s", specifiedCard));
- }
}
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 111732cd07..adde508c96 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
@@ -1703,6 +1703,16 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()));
}
+ @Override
+ public Traversal visitTraversalMethod_option_Merge_Map_Cardinality(final GremlinParser.TraversalMethod_option_Merge_Map_CardinalityContext ctx) {
+ if (ctx.nullLiteral() != null) {
+ return this.graphTraversal.option(TraversalEnumParser.parseTraversalEnumFromContext(Merge.class, ctx.traversalMerge()), (Map) null);
+ }
+
+ return graphTraversal.option(TraversalEnumParser.parseTraversalEnumFromContext(Merge.class, ctx.traversalMerge()),
+ (Map) new GenericLiteralVisitor(antlr).visitGenericLiteralMap(ctx.genericLiteralMap()));
+ }
+
public GraphTraversal[] getNestedTraversalList(final GremlinParser.NestedTraversalListContext ctx) {
return ctx.nestedTraversalExpr().nestedTraversal()
.stream()
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
index dc8eca3add..5b88cea5fc 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
@@ -47,7 +47,7 @@ import java.util.Set;
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class Bytecode implements Cloneable, Serializable {
+public class Bytecode implements Cloneable, Serializable {
private static final Object[] EMPTY_ARRAY = new Object[]{};
@@ -56,7 +56,7 @@ public final class Bytecode implements Cloneable, Serializable {
public Bytecode() {}
- Bytecode(final String sourceName, final Object... arguments) {
+ public Bytecode(final String sourceName, final Object... arguments) {
this.sourceInstructions.add(new Instruction(sourceName, flattenArguments(arguments)));
}
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 0be7555797..120176821a 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,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -231,6 +232,11 @@ public interface Translator<S, T> {
*/
protected abstract Script produceScript(final P<?> p);
+ /**
+ * Take the {@link Bytecode} and write the syntax for it directly to the member {@link #script} variable.
+ */
+ protected abstract Script produceCardinalityValue(final Bytecode o);
+
/**
* For each operator argument, if withParameters set true, try parametrization as follows:
*
@@ -272,7 +278,13 @@ public interface Translator<S, T> {
if (object instanceof Bytecode.Binding) {
return script.getBoundKeyOrAssign(withParameters, ((Bytecode.Binding) object).variable());
} else if (object instanceof Bytecode) {
- return produceScript(getAnonymousTraversalPrefix(), (Bytecode) object);
+ final Bytecode bc = (Bytecode) object;
+ if (bc.getSourceInstructions().size() == 1 &&
+ bc.getSourceInstructions().get(0).getOperator().equals(CardinalityValueTraversal.class.getSimpleName())) {
+ return produceCardinalityValue(bc);
+ } else {
+ return produceScript(getAnonymousTraversalPrefix(), bc);
+ }
} else if (object instanceof Traversal) {
return convertToScript(((Traversal) object).asAdmin().getBytecode());
} else if (object instanceof String) {
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 7fc1b3995a..c6bd6ea811 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
@@ -36,6 +36,7 @@ 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.CardinalityValueTraversal;
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;
@@ -3271,6 +3272,29 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
return this;
}
+ /**
+ * This is a step modulator to a {@link TraversalOptionParent} like {@code choose()} or {@code mergeV()} where the
+ * provided argument associated to the {@code token} is applied according to the semantics of the step. Please see
+ * the documentation of such steps to understand the usage context.
+ *
+ * @param m Provides a {@code Map} as the option which is the same as doing {@code constant(m)}.
+ * @return the traversal with the modulated step
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergev-step" target="_blank">Reference Documentation - MergeV Step</a>
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergee-step" target="_blank">Reference Documentation - MergeE Step</a>
+ * @since 3.6.0
+ */
+ public default <M, E2> GraphTraversal<S, E> option(final Merge merge, final Map<Object, Object> m, final VertexProperty.Cardinality cardinality) {
+ this.asAdmin().getBytecode().addStep(Symbols.option, merge, m, cardinality);
+ // do explicit cardinality for every single pair in the map
+ for (Object k : m.keySet()) {
+ final Object o = m.get(k);
+ if (!(o instanceof CardinalityValueTraversal))
+ m.put(k, new CardinalityValueTraversal(cardinality, o));
+ }
+ ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption((M) merge, (Traversal.Admin<E, E2>) new ConstantTraversal<>(m).asAdmin());
+ return this;
+ }
+
/**
* This step modifies {@link #choose(Function)} to specifies the available choices that might be executed.
*
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityValue.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/CardinalityValueTraversal.java
similarity index 58%
rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityValue.java
rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/CardinalityValueTraversal.java
index a2461060ca..8c13e0649b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityValue.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/CardinalityValueTraversal.java
@@ -16,36 +16,35 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.tinkerpop.gremlin.process.traversal;
+package org.apache.tinkerpop.gremlin.process.traversal.lambda;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import java.util.Objects;
-/**
- * Wraps a {@link VertexProperty.Cardinality} with the value it is associated.
- */
-public class CardinalityValue<T> {
+public final class CardinalityValueTraversal extends AbstractLambdaTraversal {
private final VertexProperty.Cardinality cardinality;
- private final T value;
+ private final Object value;
+
+ private final Bytecode bytecode;
- private CardinalityValue(final VertexProperty.Cardinality cardinality, final T value) {
+ public CardinalityValueTraversal(final VertexProperty.Cardinality cardinality, final Object value) {
this.cardinality = cardinality;
this.value = value;
+ this.bytecode = new Bytecode(CardinalityValueTraversal.class.getSimpleName(), cardinality.name(), value);
}
- public static <T> CardinalityValue<T> single(final T value) {
- return new CardinalityValue<T>(VertexProperty.Cardinality.single, value);
- }
-
- public static <T> CardinalityValue<T> list(final T value) {
- return new CardinalityValue<T>(VertexProperty.Cardinality.list, value);
+ public static CardinalityValueTraversal from(final Bytecode.Instruction inst) {
+ return new CardinalityValueTraversal(VertexProperty.Cardinality.valueOf(inst.getArguments()[0].toString()),
+ inst.getArguments()[1]);
}
- public static <T> CardinalityValue<T> set(final T value) {
- return new CardinalityValue<T>(VertexProperty.Cardinality.set, value);
+ @Override
+ public Bytecode getBytecode() {
+ return this.bytecode;
}
public VertexProperty.Cardinality getCardinality() {
@@ -56,12 +55,18 @@ public class CardinalityValue<T> {
return value;
}
+ @Override
+ public String toString() {
+ return "[" + cardinality + ", " + value + "]";
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) return true;
- if (!(o instanceof CardinalityValue)) return false;
+ if (!(o instanceof CardinalityValueTraversal)) return false;
+ if (!super.equals(o)) return false;
- CardinalityValue<?> that = (CardinalityValue<?>) o;
+ final CardinalityValueTraversal that = (CardinalityValueTraversal) o;
if (cardinality != that.cardinality) return false;
return Objects.equals(value, that.value);
@@ -69,13 +74,9 @@ public class CardinalityValue<T> {
@Override
public int hashCode() {
- int result = cardinality.hashCode();
+ int result = super.hashCode();
+ result = 31 * result + cardinality.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
-
- @Override
- public String toString() {
- return "cv[" + cardinality + ", " + Objects.toString(value) + "]";
- }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
index 1408cfbfec..ed1e26a34a 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
@@ -29,12 +29,15 @@ import java.util.stream.Stream;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
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.CloseableIterator;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
@@ -96,18 +99,28 @@ public class MergeVertexStep<S> extends MergeStep<S, Vertex, Map> {
validateMapInput(onMatchMap, true);
onMatchMap.forEach((key, value) -> {
+ Object val = value;
+ VertexProperty.Cardinality card = graph.features().vertex().getCardinality(key);
+
+ // a value can be a traversal in the case where the user specifies the cardinality for the value.
+ if (value instanceof CardinalityValueTraversal) {
+ final CardinalityValueTraversal cardinalityValueTraversal = (CardinalityValueTraversal) value;
+ card = cardinalityValueTraversal.getCardinality();
+ val = cardinalityValueTraversal.getValue();
+ }
+
// 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<?> p = v.property(key);
final Property<Object> oldValue = p.isPresent() ? eventStrategy.detach(v.property(key)) : null;
- final Event.VertexPropertyChangedEvent vpce = new Event.VertexPropertyChangedEvent(eventStrategy.detach(v), oldValue, value);
+ final Event.VertexPropertyChangedEvent vpce = new Event.VertexPropertyChangedEvent(eventStrategy.detach(v), oldValue, val);
this.callbackRegistry.getCallbacks().forEach(c -> c.accept(vpce));
}
// try to detect proper cardinality for the key according to the graph
- v.property(graph.features().vertex().getCardinality(key), key, value);
+ v.property(card, key, val);
});
});
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
index 55d69367df..d1654a344e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
@@ -135,7 +135,6 @@ public class Parameters implements Cloneable, Serializable {
public <E> List<E> get(final Object key, final Supplier<E> defaultValue) {
final List<E> list = (List<E>) this.parameters.get(key);
return (null == list) ? (null == defaultValue ? Collections.emptyList() : Collections.singletonList(defaultValue.get())) : list;
-
}
/**
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 20ee8ec65c..5e37147b25 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
@@ -192,6 +192,16 @@ public final class DotNetTranslator implements Translator.ScriptTranslator {
return o.toString();
}
+ @Override
+ protected Script produceCardinalityValue(final Bytecode o) {
+ final Bytecode.Instruction inst = o.getSourceInstructions().get(0);
+ final String card = inst.getArguments()[0].toString();
+ script.append("CardinalityValue." + card.substring(0, 1).toUpperCase() + card.substring(1) + "(");
+ convertToScript(inst.getArguments()[1]);
+ script.append(")");
+ return script;
+ }
+
@Override
protected Script produceScript(final Set<?> o) {
final Iterator<?> iterator = o.iterator();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GolangTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GolangTranslator.java
index ee1c38cbc0..dece576e72 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GolangTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GolangTranslator.java
@@ -181,6 +181,16 @@ public final class GolangTranslator implements Translator.ScriptTranslator {
return GO_PACKAGE_NAME + "Pick." + resolveSymbol(o.toString());
}
+ @Override
+ protected Script produceCardinalityValue(final Bytecode o) {
+ final Bytecode.Instruction inst = o.getSourceInstructions().get(0);
+ final String card = inst.getArguments()[0].toString();
+ script.append(GO_PACKAGE_NAME + "CardinalityValue." + card.substring(0, 1).toUpperCase() + card.substring(1) + "(");
+ convertToScript(inst.getArguments()[1]);
+ script.append(")");
+ return script;
+ }
+
@Override
protected Script produceScript(final Set<?> o) {
final Iterator<?> iterator = o.iterator();
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 7fea7eb829..ad5498b003 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
@@ -206,6 +206,15 @@ public final class GroovyTranslator implements Translator.ScriptTranslator {
return o.toString();
}
+ @Override
+ protected Script produceCardinalityValue(final Bytecode o) {
+ final Bytecode.Instruction inst = o.getSourceInstructions().get(0);
+ script.append("VertexProperty.Cardinality." + inst.getArguments()[0] + "(");
+ convertToScript(inst.getArguments()[1]);
+ script.append(")");
+ return script;
+ }
+
@Override
protected Script produceScript(final Set<?> o) {
return produceScript(new ArrayList<>(o)).append(" as Set");
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 c03fd01ebb..1ebf25a34c 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
@@ -187,6 +187,15 @@ public final class JavascriptTranslator implements Translator.ScriptTranslator {
return o.toString();
}
+ @Override
+ protected Script produceCardinalityValue(final Bytecode o) {
+ final Bytecode.Instruction inst = o.getSourceInstructions().get(0);
+ script.append("CardinalityValue." + inst.getArguments()[0] + "(");
+ convertToScript(inst.getArguments()[1]);
+ script.append(")");
+ return script;
+ }
+
@Override
protected Script produceScript(final Set<?> o) {
return produceScript(new ArrayList<>(o));
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 41f8382643..e26b02f8ad 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
@@ -362,6 +362,15 @@ public final class PythonTranslator implements Translator.ScriptTranslator {
return script;
}
+ @Override
+ protected Script produceCardinalityValue(final Bytecode o) {
+ final Bytecode.Instruction inst = o.getSourceInstructions().get(0);
+ script.append("CardinalityValue." + resolveSymbol(inst.getArguments()[0].toString()) + "(");
+ convertToScript(inst.getArguments()[1]);
+ script.append(")");
+ return script;
+ }
+
protected String resolveSymbol(final String methodName) {
return SymbolHelper.toPython(methodName);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
index 4460f7d050..a1f45c145d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
@@ -18,6 +18,10 @@
*/
package org.apache.tinkerpop.gremlin.structure;
+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.lambda.CardinalityValueTraversal;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyVertexProperty;
import java.util.Iterator;
@@ -40,7 +44,19 @@ public interface VertexProperty<V> extends Property<V>, Element {
public static final String DEFAULT_LABEL = "vertexProperty";
public enum Cardinality {
- single, list, set
+ single, list, set;
+
+ public static CardinalityValueTraversal single(final Object value) {
+ return new CardinalityValueTraversal(single, value);
+ }
+
+ public static CardinalityValueTraversal list(final Object value) {
+ return new CardinalityValueTraversal(list, value);
+ }
+
+ public static CardinalityValueTraversal set(final Object value) {
+ return new CardinalityValueTraversal(set, value);
+ }
}
/**
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java
index ab0b094329..80b1df0b1b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java
@@ -102,7 +102,7 @@ public class GraphBinaryWriter {
if (serializer instanceof TransformSerializer) {
// For historical reasons, there are types that need to be transformed into another type
// before serialization, e.g., Map.Entry
- TransformSerializer<T> transformSerializer = (TransformSerializer<T>) serializer;
+ final TransformSerializer<T> transformSerializer = (TransformSerializer<T>) serializer;
write(transformSerializer.transform(value), buffer);
return;
}
@@ -118,7 +118,7 @@ public class GraphBinaryWriter {
* <p>Note that for simple types, the provided information will be <code>null</code>.</p>
*/
public <T> void writeFullyQualifiedNull(final Class<T> objectClass, Buffer buffer, final Object information) throws IOException {
- TypeSerializer<T> serializer = registry.getSerializer(objectClass);
+ final TypeSerializer<T> serializer = registry.getSerializer(objectClass);
serializer.write(null, buffer, this);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TransformSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TransformSerializer.java
index 97ccbbaea3..7275b1cce6 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TransformSerializer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TransformSerializer.java
@@ -24,5 +24,5 @@ import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializer;
* Represents a special TypeSerializer placeholder that transforms the value into another before serializing it.
*/
public interface TransformSerializer<T> extends TypeSerializer<T> {
- Object transform(T value);
+ Object transform(final T value);
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java
index 7e066adc88..89b6fca15a 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java
@@ -20,7 +20,7 @@ package org.apache.tinkerpop.gremlin.language.grammar;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
-import org.apache.tinkerpop.gremlin.process.traversal.CardinalityValue;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.junit.Assert;
import org.junit.Test;
@@ -677,25 +677,31 @@ public class GeneralLiteralVisitorTest {
}
@RunWith(Parameterized.class)
- public static class CardinalityValueTest {
+ public static class CardinalityTest {
@Parameterized.Parameter(value = 0)
public String script;
@Parameterized.Parameter(value = 1)
- public CardinalityValue expected;
+ public Object expected;
@Parameterized.Parameters()
public static Iterable<Object[]> generateTestParameters() {
return Arrays.asList(new Object[][]{
- {"single(\"test\")", CardinalityValue.single("test")},
- {"list(\"test\")", CardinalityValue.list("test")},
- {"set(\"test\")", CardinalityValue.set("test")},
- {"CardinalityValue.single(\"test\")", CardinalityValue.single("test")},
- {"CardinalityValue.list(\"test\")", CardinalityValue.list("test")},
- {"CardinalityValue.set(\"test\")", CardinalityValue.set("test")},
- {"single(1l)", CardinalityValue.single(1L)},
- {"list(1l)", CardinalityValue.list(1L)},
- {"set(1l)", CardinalityValue.set(1L)},
+ {"single(\"test\")", VertexProperty.Cardinality.single("test")},
+ {"list(\"test\")", VertexProperty.Cardinality.list("test")},
+ {"set(\"test\")", VertexProperty.Cardinality.set("test")},
+ {"Cardinality.single(\"test\")", VertexProperty.Cardinality.single("test")},
+ {"Cardinality.list(\"test\")", VertexProperty.Cardinality.list("test")},
+ {"Cardinality.set(\"test\")", VertexProperty.Cardinality.set("test")},
+ {"single(1l)", VertexProperty.Cardinality.single(1L)},
+ {"list(1l)", VertexProperty.Cardinality.list(1L)},
+ {"set(1l)", VertexProperty.Cardinality.set(1L)},
+ {"Cardinality.single", VertexProperty.Cardinality.single},
+ {"Cardinality.list", VertexProperty.Cardinality.list},
+ {"Cardinality.set", VertexProperty.Cardinality.set},
+ {"single", VertexProperty.Cardinality.single},
+ {"list", VertexProperty.Cardinality.list},
+ {"set", VertexProperty.Cardinality.set},
});
}
@@ -703,8 +709,8 @@ public class GeneralLiteralVisitorTest {
public void shouldParse() {
final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString(script));
final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer));
- final GremlinParser.TraversalCardinalityValueContext ctx = parser.traversalCardinalityValue();
- assertEquals(expected, GenericLiteralVisitor.instance().visitTraversalCardinalityValue(ctx));
+ final GremlinParser.TraversalCardinalityContext ctx = parser.traversalCardinality();
+ assertEquals(expected, GenericLiteralVisitor.instance().visitTraversalCardinality(ctx));
}
}
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityTest.java
new file mode 100644
index 0000000000..b5c578e2f4
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+public class CardinalityTest {
+
+ @Test
+ public void shouldCreateSingle() {
+ final CardinalityValueTraversal t = VertexProperty.Cardinality.single("test");
+ assertEquals("test", t.getValue());
+ assertEquals(VertexProperty.Cardinality.single, t.getCardinality());
+ }
+
+ @Test
+ public void shouldCreateSet() {
+ final CardinalityValueTraversal t = VertexProperty.Cardinality.set("test");
+ assertEquals("test", t.getValue());
+ assertEquals(VertexProperty.Cardinality.set, t.getCardinality());
+ }
+
+ @Test
+ public void shouldCreateList() {
+ final CardinalityValueTraversal t = VertexProperty.Cardinality.list("test");
+ assertEquals("test", t.getValue());
+ assertEquals(VertexProperty.Cardinality.list, t.getCardinality());
+ }
+
+ @Test
+ public void shouldBeEqual() {
+ assertEquals(VertexProperty.Cardinality.single("test"), VertexProperty.Cardinality.single("test"));
+ assertEquals(VertexProperty.Cardinality.single(1), VertexProperty.Cardinality.single(1));
+ assertEquals(VertexProperty.Cardinality.single(null), VertexProperty.Cardinality.single(null));
+ }
+
+ @Test
+ public void shouldNotBeEqual() {
+ assertNotEquals(VertexProperty.Cardinality.single(100), VertexProperty.Cardinality.single("testing"));
+ assertNotEquals(VertexProperty.Cardinality.single("test"), VertexProperty.Cardinality.single("testing"));
+ assertNotEquals(VertexProperty.Cardinality.single(100), VertexProperty.Cardinality.single(1));
+ assertNotEquals(VertexProperty.Cardinality.single("null"), VertexProperty.Cardinality.single(null));
+ }
+}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityValueTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityValueTest.java
deleted file mode 100644
index 221b13f085..0000000000
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CardinalityValueTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.structure.VertexProperty;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
-public class CardinalityValueTest {
-
- @Test
- public void shouldCreateSingle() {
- final CardinalityValue<String> cv = CardinalityValue.single("test");
- assertEquals("test", cv.getValue());
- assertEquals(VertexProperty.Cardinality.single, cv.getCardinality());
- }
-
- @Test
- public void shouldCreateSet() {
- final CardinalityValue<String> cv = CardinalityValue.set("test");
- assertEquals("test", cv.getValue());
- assertEquals(VertexProperty.Cardinality.set, cv.getCardinality());
- }
-
- @Test
- public void shouldCreateList() {
- final CardinalityValue<String> cv = CardinalityValue.list("test");
- assertEquals("test", cv.getValue());
- assertEquals(VertexProperty.Cardinality.list, cv.getCardinality());
- }
-
- @Test
- public void shouldBeEqual() {
- assertEquals(CardinalityValue.single("test"), CardinalityValue.single("test"));
- assertEquals(CardinalityValue.single(1), CardinalityValue.single(1));
- assertEquals(CardinalityValue.single(null), CardinalityValue.single(null));
- }
-
- @Test
- public void shouldNotBeEqual() {
- assertNotEquals(CardinalityValue.single(100), CardinalityValue.single("testing"));
- assertNotEquals(CardinalityValue.single("test"), CardinalityValue.single("testing"));
- assertNotEquals(CardinalityValue.single(100), CardinalityValue.single(1));
- assertNotEquals(CardinalityValue.single("null"), CardinalityValue.single(null));
- }
-}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslatorTest.java
index 28256f79fe..b3b7f1a855 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslatorTest.java
@@ -199,6 +199,11 @@ public class DotNetTranslatorTest {
g.V().hasLabel("person").property(VertexProperty.Cardinality.single, "name", null)).getScript());
}
+ @Test
+ public void shouldTranslateCardinalityValue() {
+ assertTranslation("CardinalityValue.Set(\"test\")", VertexProperty.Cardinality.set("test"));
+ }
+
@Test
public void shouldTranslateHasNull() {
String script = translator.translate(g.V().has("k", (Object) null).asAdmin().getBytecode()).getScript();
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GolangTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GolangTranslatorTest.java
index 19b37b79eb..6ad0a95671 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GolangTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GolangTranslatorTest.java
@@ -63,6 +63,12 @@ public class GolangTranslatorTest {
assertEquals("g.AddV(\"person\").Property(gremlingo.Cardinality.List, \"name\", \"marko\")", gremlinAsGo);
}
+ @Test
+ public void shouldTranslateCardinalityValue() {
+ assertEquals("g.Inject(gremlingo.CardinalityValue.Set(\"test\"))", translator.translate(
+ g.inject(VertexProperty.Cardinality.set("test")).asAdmin().getBytecode()).getScript());
+ }
+
@Test
public void shouldTranslateMultilineStrings() {
final String gremlinAsGo = translator.translate(
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslatorTest.java
index b9910fbbc8..499e76b4b4 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslatorTest.java
@@ -34,6 +34,7 @@ import org.apache.tinkerpop.gremlin.structure.Column;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
@@ -160,6 +161,11 @@ public class GroovyTranslatorTest {
assertTranslation("Column.keys", Column.keys);
}
+ @Test
+ public void shouldTranslateCardinalityValue() {
+ assertTranslation("VertexProperty.Cardinality.set(\"test\")", VertexProperty.Cardinality.set("test"));
+ }
+
@Test
public void shouldTranslateDateUsingLanguageTypeTranslator() {
final Translator.ScriptTranslator t = GroovyTranslator.of("g",
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
index 98de2f988c..ba99d44969 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
@@ -32,6 +32,7 @@ import org.apache.tinkerpop.gremlin.structure.Column;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
@@ -120,6 +121,11 @@ public class JavascriptTranslatorTest {
assertTranslation("Scope.local", Scope.local);
}
+ @Test
+ public void shouldTranslateCardinalityValue() {
+ assertTranslation("CardinalityValue.set(\"test\")", VertexProperty.Cardinality.set("test"));
+ }
+
@Test
public void shouldHaveValidToString() {
assertEquals("translator[h:gremlin-javascript]", JavascriptTranslator.of("h").toString());
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslatorTest.java
index 9fee3ed4a9..ebbca186f8 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/PythonTranslatorTest.java
@@ -19,11 +19,13 @@
package org.apache.tinkerpop.gremlin.process.traversal.translator;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
@@ -32,6 +34,9 @@ import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
import org.apache.tinkerpop.gremlin.util.function.Lambda;
import org.junit.Test;
+import java.util.HashMap;
+import java.util.Map;
+
import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.hasLabel;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.inE;
@@ -62,6 +67,15 @@ public class PythonTranslatorTest {
assertEquals("g.addV('person').property(Cardinality.list_,'name','marko')", gremlinAsPython);
}
+ @Test
+ public void shouldTranslateCardinalityValue() {
+ final Map<Object, Object> m = new HashMap<>();
+ m.put("name", VertexProperty.Cardinality.set("marko"));
+ final String gremlinAsPython = translator.translate(
+ g.mergeV(new HashMap<>()).option(Merge.onMatch, m).asAdmin().getBytecode()).getScript();
+ assertEquals("g.merge_v({}).option(Merge.on_match,{'name':CardinalityValue.set_('marko')})", gremlinAsPython);
+ }
+
@Test
public void shouldTranslateMultilineStrings() {
final String gremlinAsPython = translator.translate(
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/CardinalityValue.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/CardinalityValue.cs
new file mode 100644
index 0000000000..efecabddae
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/CardinalityValue.cs
@@ -0,0 +1,88 @@
+#region License
+
+/*
+ * 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.
+ */
+
+#endregion
+
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Gremlin.Net.Process.Traversal
+{
+ /// <summary>
+ /// Holds a property value with the associated <see cref="Traversal.Cardinality" />.
+ /// </summary>
+ public class CardinalityValue : Bytecode
+ {
+ /// <summary>
+ /// Gets the <see cref="Traversal.Cardinality" /> for the value.
+ /// </summary>
+ public Cardinality Cardinality
+ {
+ get
+ {
+ return this.SourceInstructions[0].Arguments[0];
+ }
+ }
+
+ /// <summary>
+ /// Gets the value.
+ /// </summary>
+ public object Value
+ {
+ get
+ {
+ return this.SourceInstructions[0].Arguments[1];
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CardinalityValue" /> class.
+ /// </summary>
+ public CardinalityValue(Cardinality card, object val)
+ {
+ this.AddSource("CardinalityValueTraversal", card, val);
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="CardinalityValue" /> with a particular value and Single <see cref="Cardinality" />.
+ /// </summary>
+ public static CardinalityValue Single(object value)
+ {
+ return new CardinalityValue(Cardinality.Single, value);
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="CardinalityValue" /> with a particular value and Set <see cref="Cardinality" />.
+ /// </summary>
+ public static CardinalityValue Set(object value)
+ {
+ return new CardinalityValue(Cardinality.Set, value);
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="CardinalityValue" /> with a particular value and List <see cref="Cardinality" />.
+ /// </summary>
+ public static CardinalityValue List(object value)
+ {
+ return new CardinalityValue(Cardinality.List, value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index c6cf798f9a..82aa656ced 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -1301,6 +1301,15 @@ namespace Gremlin.Net.Process.Traversal
return Wrap<S, E>(this);
}
+ /// <summary>
+ /// Adds the option step to this <see cref="GraphTraversal{SType, EType}" />.
+ /// </summary>
+ public GraphTraversal<S, E> Option (object pickToken, IDictionary<object,object> traversalOption, Cardinality cardinality)
+ {
+ Bytecode.AddStep("option", pickToken, traversalOption, cardinality);
+ return Wrap<S, E>(this);
+ }
+
/// <summary>
/// Adds the option step to this <see cref="GraphTraversal{SType, EType}" />.
/// </summary>
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 6580b1908c..667630c571 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -707,6 +707,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_mergeV_hidden_id_key_onMatch_matched_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("vertex"), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {}).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx1"])}},
{"g_mergeV_hidden_label_key_matched_onMatch_matched_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("vertex"), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {}).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx1"])}},
{"g_mergeV_hidden_label_value_onMatch_matched_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("vertex"), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {}).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx1"])}},
+ {"g_mergeVXname_markoX_optionXonMatch_age_listX33XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch, (IDictionary<object,object>) new Dictionary<object,object> {{"age" [...]
+ {"g_mergeVXname_markoX_optionXonMatch_age_setX33XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch, (IDictionary<object,object>) new Dictionary<object,object> {{"age", [...]
+ {"g_mergeVXname_markoX_optionXonMatch_age_setX31XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch, (IDictionary<object,object>) new Dictionary<object,object> {{"age", [...]
+ {"g_mergeVXname_markoX_optionXonMatch_age_singleX33XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch, (IDictionary<object,object>) new Dictionary<object,object> {{"ag [...]
+ {"g_mergeVXname_markoX_optionXonMatch_age_33_singleX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch,new Dictionary<object,object> {{"age", 33}},Cardinality.Single), ( [...]
+ {"g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch,new Dictionary<object,object> {{"name", "allen"}, [...]
{"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-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/CardinalityValueTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/CardinalityValueTests.cs
new file mode 100644
index 0000000000..c30aaf3950
--- /dev/null
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/CardinalityValueTests.cs
@@ -0,0 +1,55 @@
+#region License
+
+/*
+ * 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.
+ */
+
+#endregion
+
+using Gremlin.Net.Process.Traversal;
+using Xunit;
+
+namespace Gremlin.Net.UnitTest.Process.Traversal
+{
+ public class CardinalityValueTests
+ {
+ [Fact]
+ public void ShouldProduceSingleValue()
+ {
+ CardinalityValue val = CardinalityValue.Single("test");
+ Assert.Equal("test", val.Value);
+ Assert.Equal(Cardinality.Single, val.Cardinality);
+ }
+
+ [Fact]
+ public void ShouldProduceListValue()
+ {
+ CardinalityValue val = CardinalityValue.List("test");
+ Assert.Equal("test", val.Value);
+ Assert.Equal(Cardinality.List, val.Cardinality);
+ }
+
+ [Fact]
+ public void ShouldProduceSetValue()
+ {
+ CardinalityValue val = CardinalityValue.Set("test");
+ Assert.Equal("test", val.Value);
+ Assert.Equal(Cardinality.Set, val.Cardinality);
+ }
+ }
+}
\ No newline at end of file
diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go
index 1444206a4b..84b752bf0f 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -678,6 +678,12 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
"g_mergeV_hidden_id_key_onMatch_matched_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("vertex")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{}).Option(gremlingo.Merge.OnMatch, p["xx1"])}},
"g_mergeV_hidden_label_key_matched_onMatch_matched_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("vertex")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{}).Option(gremlingo.Merge.OnMatch, p["xx1"])}},
"g_mergeV_hidden_label_value_onMatch_matched_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("vertex")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{}).Option(gremlingo.Merge.OnMatch, p["xx1"])}},
+ "g_mergeVXname_markoX_optionXonMatch_age_listX33XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name": [...]
+ "g_mergeVXname_markoX_optionXonMatch_age_setX33XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name": " [...]
+ "g_mergeVXname_markoX_optionXonMatch_age_setX31XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name": " [...]
+ "g_mergeVXname_markoX_optionXonMatch_age_singleX33XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name" [...]
+ "g_mergeVXname_markoX_optionXonMatch_age_33_singleX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name": [...]
+ "g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]int [...]
"g_V_age_min": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Min()}},
"g_V_foo_min": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("foo").Min()}},
"g_V_name_min": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Min()}},
diff --git a/gremlin-go/driver/traversal.go b/gremlin-go/driver/traversal.go
index c6a72a6f43..1529fbacb0 100644
--- a/gremlin-go/driver/traversal.go
+++ b/gremlin-go/driver/traversal.go
@@ -153,6 +153,36 @@ var Cardinality = cardinalities{
Set: "set",
}
+type cv struct {
+ Bytecode *Bytecode
+}
+
+type CardValue interface {
+ Single(val interface{}) Bytecode
+ Set(val interface{}) Bytecode
+ List(val interface{}) Bytecode
+}
+
+var CardinalityValue CardValue = &cv{}
+
+func (*cv) Single(val interface{}) Bytecode {
+ bc := Bytecode{}
+ bc.AddSource("CardinalityValueTraversal", Cardinality.Single, val)
+ return bc
+}
+
+func (*cv) Set(val interface{}) Bytecode {
+ bc := Bytecode{}
+ bc.AddSource("CardinalityValueTraversal", Cardinality.Set, val)
+ return bc
+}
+
+func (*cv) List(val interface{}) Bytecode {
+ bc := Bytecode{}
+ bc.AddSource("CardinalityValueTraversal", Cardinality.List, val)
+ return bc
+}
+
type column string
type columns struct {
@@ -286,8 +316,8 @@ type merges struct {
var Merge = merges{
OnCreate: "onCreate",
OnMatch: "onMatch",
- OutV: "outV",
- InV: "inV",
+ OutV: "outV",
+ InV: "inV",
}
type operator string
diff --git a/gremlin-javascript/build/generate.groovy b/gremlin-javascript/build/generate.groovy
index c726d0e2ff..c7d92e0b29 100644
--- a/gremlin-javascript/build/generate.groovy
+++ b/gremlin-javascript/build/generate.groovy
@@ -95,6 +95,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
'const __ = graphTraversalModule.statics;\n' +
'const Barrier = traversalModule.barrier\n' +
'const Cardinality = traversalModule.cardinality\n' +
+ 'const CardinalityValue = graphTraversalModule.CardinalityValue;\n' +
'const Column = traversalModule.column\n' +
'const Direction = {\n' +
' BOTH: traversalModule.direction.both,\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 292439ee6c..3884639d55 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
@@ -22,7 +22,7 @@
*/
'use strict';
-const { Traversal } = require('./traversal');
+const { Traversal, cardinality } = require('./traversal');
const { Transaction } = require('./transaction');
const remote = require('../driver/remote-connection');
const Bytecode = require('./bytecode');
@@ -1433,6 +1433,45 @@ class GraphTraversal extends Traversal {
}
}
+class CardinalityValue extends Bytecode {
+ /**
+ * Creates a new instance of {@link CardinalityValue}.
+ * @param {String} card
+ * @param {Object} value
+ */
+ constructor(card, value) {
+ super();
+ this.addSource('CardinalityValueTraversal', [card, value]);
+ }
+
+ /**
+ * Create a value with single cardinality.
+ * @param {Array} value
+ * @returns {CardinalityValue}
+ */
+ static single(value) {
+ return new CardinalityValue(cardinality.single, value);
+ }
+
+ /**
+ * Create a value with list cardinality.
+ * @param {Array} value
+ * @returns {CardinalityValue}
+ */
+ static list(value) {
+ return new CardinalityValue(cardinality.list, value);
+ }
+
+ /**
+ * Create a value with set cardinality.
+ * @param {Array} value
+ * @returns {CardinalityValue}
+ */
+ static set(value) {
+ return new CardinalityValue(cardinality.set, value);
+ }
+}
+
function callOnEmptyTraversal(fnName, args) {
const g = new GraphTraversal(null, null, new Bytecode());
return g[fnName].apply(g, args);
@@ -1543,5 +1582,6 @@ const statics = {
module.exports = {
GraphTraversal,
GraphTraversalSource,
+ CardinalityValue,
statics,
};
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 513042b2cb..5ef4ba4999 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
@@ -30,6 +30,7 @@ const { TraversalStrategies, VertexProgramStrategy, OptionsStrategy, ReadOnlyStr
const __ = graphTraversalModule.statics;
const Barrier = traversalModule.barrier
const Cardinality = traversalModule.cardinality
+const CardinalityValue = graphTraversalModule.CardinalityValue;
const Column = traversalModule.column
const Direction = {
BOTH: traversalModule.direction.both,
@@ -697,6 +698,12 @@ const gremlins = {
g_mergeV_hidden_id_key_onMatch_matched_prohibited: [function({g, xx1}) { return g.addV("vertex") }, function({g, xx1}) { return g.mergeV(new Map([])).option(Merge.onMatch,xx1) }],
g_mergeV_hidden_label_key_matched_onMatch_matched_prohibited: [function({g, xx1}) { return g.addV("vertex") }, function({g, xx1}) { return g.mergeV(new Map([])).option(Merge.onMatch,xx1) }],
g_mergeV_hidden_label_value_onMatch_matched_prohibited: [function({g, xx1}) { return g.addV("vertex") }, function({g, xx1}) { return g.mergeV(new Map([])).option(Merge.onMatch,xx1) }],
+ g_mergeVXname_markoX_optionXonMatch_age_listX33XX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["age",CardinalityValue.list(33)]])) }, function({g}) { return g.V().has("person","name","marko").has("age",33) }, function({g}) { return g.V().has("person","name","mark [...]
+ g_mergeVXname_markoX_optionXonMatch_age_setX33XX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["age",CardinalityValue.set(33)]])) }, function({g}) { return g.V().has("person","name","marko").has("age",33) }, function({g}) { return g.V().has("person","name","marko" [...]
+ g_mergeVXname_markoX_optionXonMatch_age_setX31XX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["age",CardinalityValue.set(31)]])) }, function({g}) { return g.V().has("person","name","marko").has("age",31) }, function({g}) { return g.V().has("person","name","marko" [...]
+ g_mergeVXname_markoX_optionXonMatch_age_singleX33XX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["age",CardinalityValue.single(33)]])) }, function({g}) { return g.V().has("person","name","marko").has("age",33) }, function({g}) { return g.V().has("person","name"," [...]
+ g_mergeVXname_markoX_optionXonMatch_age_33_singleX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["age",33]]),Cardinality.single) }, function({g}) { return g.V().has("person","name","marko").has("age",33) }, function({g}) { return g.V().has("person","name","marko") [...]
+ g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["name","allen"],["age",CardinalityValue.set(31)]]),Cardinality.single) }, function({g}) { return g.V().has("person","name","marko") }, function({g}) { [...]
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 3f9d7afaf3..5f37c5013f 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -569,6 +569,7 @@ traversalMethod_not
traversalMethod_option
: 'option' LPAREN traversalPredicate COMMA nestedTraversal RPAREN #traversalMethod_option_Predicate_Traversal
| 'option' LPAREN traversalMerge COMMA (genericLiteralMap | nullLiteral) RPAREN #traversalMethod_option_Merge_Map
+ | 'option' LPAREN traversalMerge COMMA (genericLiteralMap | nullLiteral) COMMA traversalCardinality RPAREN #traversalMethod_option_Merge_Map_Cardinality
| '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
@@ -910,17 +911,17 @@ traversalDirection
;
traversalCardinality
- : 'single' | 'Cardinality.single'
+ : 'Cardinality.single' LPAREN genericLiteral RPAREN
+ | 'Cardinality.set' LPAREN genericLiteral RPAREN
+ | 'Cardinality.list' LPAREN genericLiteral RPAREN
+ | 'single' LPAREN genericLiteral RPAREN
+ | 'set' LPAREN genericLiteral RPAREN
+ | 'list' LPAREN genericLiteral RPAREN
+ | 'single' | 'Cardinality.single'
| 'set' | 'Cardinality.set'
| 'list' | 'Cardinality.list'
;
-traversalCardinalityValue
- : 'CardinalityValue.'? 'single' LPAREN genericLiteral RPAREN
- | 'CardinalityValue.'? 'set' LPAREN genericLiteral RPAREN
- | 'CardinalityValue.'? 'list' LPAREN genericLiteral RPAREN
- ;
-
traversalColumn
: 'keys' | 'Column.keys'
| 'values' | 'Column.values'
@@ -1382,7 +1383,6 @@ genericLiteral
| infLiteral
// Allow the generic literal to match specific gremlin tokens also
| traversalToken
- | traversalCardinalityValue
| traversalCardinality
| traversalDirection
| traversalMerge
diff --git a/gremlin-python/build/generate.groovy b/gremlin-python/build/generate.groovy
index 6ccfa9ee86..78a065499b 100644
--- a/gremlin-python/build/generate.groovy
+++ b/gremlin-python/build/generate.groovy
@@ -93,7 +93,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
'from gremlin_python.process.traversal import TraversalStrategy\n' +
'from gremlin_python.process.graph_traversal import __\n' +
'from gremlin_python.structure.graph import Graph\n' +
- 'from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, Merge, T, Pick, Operator, IO, WithOptions\n')
+ 'from gremlin_python.process.traversal import Barrier, Cardinality, CardinalityValue, P, TextP, Pop, Scope, Column, Order, Direction, Merge, T, Pick, Operator, IO, WithOptions\n')
// Groovy can't process certain null oriented calls because it gets confused with the right overload to call
// at runtime. using this approach for now as these are the only such situations encountered so far. a better
diff --git a/gremlin-python/src/main/python/gremlin_python/process/traversal.py b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
index dc259d3d6f..a83048e3b0 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
@@ -817,6 +817,24 @@ class Bytecode(object):
return Bytecode._create_graph_op("tx", "rollback")
+class CardinalityValue(Bytecode):
+ def __init__(self, cardinality, val):
+ super().__init__()
+ self.add_source("CardinalityValueTraversal", cardinality, val)
+
+ @classmethod
+ def single(cls, val):
+ return CardinalityValue(Cardinality.single, val)
+
+ @classmethod
+ def list_(cls, val):
+ return CardinalityValue(Cardinality.list_, val)
+
+ @classmethod
+ def set_(cls, val):
+ return CardinalityValue(Cardinality.set_, val)
+
+
'''
BINDINGS
'''
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 998289bbc9..8075e8d134 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -29,7 +29,7 @@ from gremlin_python.process.anonymous_traversal import traversal
from gremlin_python.process.traversal import TraversalStrategy
from gremlin_python.process.graph_traversal import __
from gremlin_python.structure.graph import Graph
-from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, Merge, T, Pick, Operator, IO, WithOptions
+from gremlin_python.process.traversal import Barrier, Cardinality, CardinalityValue, P, TextP, Pop, Scope, Column, Order, Direction, Merge, T, Pick, Operator, IO, WithOptions
world.gremlins = {
'g_V_branchXlabel_eq_person__a_bX_optionXa__ageX_optionXb__langX_optionXb__nameX': [(lambda g, l1=None:g.V().branch(l1).option('a',__.age).option('b',__.lang).option('b',__.name))],
@@ -679,6 +679,12 @@ world.gremlins = {
'g_mergeV_hidden_id_key_onMatch_matched_prohibited': [(lambda g, xx1=None:g.addV('vertex')), (lambda g, xx1=None:g.merge_v({}).option(Merge.on_match,xx1))],
'g_mergeV_hidden_label_key_matched_onMatch_matched_prohibited': [(lambda g, xx1=None:g.addV('vertex')), (lambda g, xx1=None:g.merge_v({}).option(Merge.on_match,xx1))],
'g_mergeV_hidden_label_value_onMatch_matched_prohibited': [(lambda g, xx1=None:g.addV('vertex')), (lambda g, xx1=None:g.merge_v({}).option(Merge.on_match,xx1))],
+ 'g_mergeVXname_markoX_optionXonMatch_age_listX33XX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'age':CardinalityValue.list_(33)})), (lambda g:g.V().has('person','name','marko').has('age',33)), (lambda g:g.V().has('person','name','marko').has('age')), (lambda g:g.V().has('person','name','marko').propert [...]
+ 'g_mergeVXname_markoX_optionXonMatch_age_setX33XX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'age':CardinalityValue.set_(33)})), (lambda g:g.V().has('person','name','marko').has('age',33)), (lambda g:g.V().has('person','name','marko').has('age')), (lambda g:g.V().has('person','name','marko').propertie [...]
+ 'g_mergeVXname_markoX_optionXonMatch_age_setX31XX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'age':CardinalityValue.set_(31)})), (lambda g:g.V().has('person','name','marko').has('age',31)), (lambda g:g.V().has('person','name','marko').has('age')), (lambda g:g.V().has('person','name','marko').propertie [...]
+ 'g_mergeVXname_markoX_optionXonMatch_age_singleX33XX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'age':CardinalityValue.single(33)})), (lambda g:g.V().has('person','name','marko').has('age',33)), (lambda g:g.V().has('person','name','marko').has('age')), (lambda g:g.V().has('person','name','marko').prop [...]
+ 'g_mergeVXname_markoX_optionXonMatch_age_33_singleX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'age':33},Cardinality.single)), (lambda g:g.V().has('person','name','marko').has('age',33)), (lambda g:g.V().has('person','name','marko').has('age')), (lambda g:g.V().has('person','name','marko').properties( [...]
+ 'g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'name':'allen','age':CardinalityValue.set_(31)},Cardinality.single)), (lambda g:g.V().has('person','name','marko')), (lambda g:g.V().has('person','name','allen').has('age',31)), (lambda g: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/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature
index 91de838781..a71737e4fb 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature
@@ -842,3 +842,132 @@ Feature: Step - mergeV()
"""
When iterated to list
Then the traversal will raise an error
+
+ @MultiMetaProperties
+ Scenario: g_mergeVXname_markoX_optionXonMatch_age_listX33XX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property(Cardinality.list, "age", 29).property(Cardinality.list, "age", 31).property(Cardinality.list, "age", 32)
+ """
+ And the traversal of
+ """
+ g.mergeV([name: "marko"]).
+ option(Merge.onMatch, [age: Cardinality.list(33)])
+ """
+ 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\", 33)"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\")"
+ And the graph should return 4 for count of "g.V().has(\"person\",\"name\",\"marko\").properties(\"age\")"
+
+ @MultiMetaProperties
+ Scenario: g_mergeVXname_markoX_optionXonMatch_age_setX33XX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property(Cardinality.list, "age", 29).property(Cardinality.list, "age", 31).property(Cardinality.list, "age", 32)
+ """
+ And the traversal of
+ """
+ g.mergeV([name: "marko"]).
+ option(Merge.onMatch, [age: Cardinality.set(33)])
+ """
+ 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\", 33)"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\")"
+ And the graph should return 4 for count of "g.V().has(\"person\",\"name\",\"marko\").properties(\"age\")"
+
+ @MultiMetaProperties
+ Scenario: g_mergeVXname_markoX_optionXonMatch_age_setX31XX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property(Cardinality.list, "age", 29).property(Cardinality.list, "age", 31).property(Cardinality.list, "age", 32)
+ """
+ And the traversal of
+ """
+ g.mergeV([name: "marko"]).
+ option(Merge.onMatch, [age: Cardinality.set(31)])
+ """
+ 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\", 31)"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\")"
+ And the graph should return 3 for count of "g.V().has(\"person\",\"name\",\"marko\").properties(\"age\")"
+
+ @MultiMetaProperties
+ Scenario: g_mergeVXname_markoX_optionXonMatch_age_singleX33XX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property(Cardinality.list, "age", 29).property(Cardinality.list, "age", 31).property(Cardinality.list, "age", 32)
+ """
+ And the traversal of
+ """
+ g.mergeV([name: "marko"]).
+ option(Merge.onMatch, [age: Cardinality.single(33)])
+ """
+ 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\", 33)"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\")"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").properties(\"age\")"
+
+ @MultiMetaProperties
+ Scenario: g_mergeVXname_markoX_optionXonMatch_age_33_singleX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property(Cardinality.list, "age", 29).property(Cardinality.list, "age", 31).property(Cardinality.list, "age", 32)
+ """
+ And the traversal of
+ """
+ g.mergeV([name: "marko"]).
+ option(Merge.onMatch, [age: 33], Cardinality.single)
+ """
+ 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\", 33)"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").has(\"age\")"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").properties(\"age\")"
+
+ @MultiMetaProperties
+ Scenario: g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property(Cardinality.list, "age", 29).property(Cardinality.list, "age", 31).property(Cardinality.list, "age", 32)
+ """
+ And the traversal of
+ """
+ g.mergeV([name: "marko"]).
+ option(Merge.onMatch, [name: "allen", age: Cardinality.set(31)], single)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 0 for count of "g.V().has(\"person\",\"name\",\"marko\")"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"allen\").has(\"age\", 31)"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"allen\").has(\"age\")"
+ And the graph should return 3 for count of "g.V().has(\"person\",\"name\",\"allen\").properties(\"age\")"
+
+ @MultiMetaProperties
+ Scenario: g_mergeVXname_markoX_optionXonMatch_name_allen_age_singleX31X_singleX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("person").property("name", "marko").property(Cardinality.list, "age", 29).property(Cardinality.list, "age", 31).property(Cardinality.list, "age", 32)
+ """
+ And the traversal of
+ """
+ g.mergeV([name: "marko"]).
+ option(Merge.onMatch, [name: "allen", age: Cardinality.single(31)], single)
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 0 for count of "g.V().has(\"person\",\"name\",\"marko\")"
+ And the graph should return 0 for count of "g.V().has(\"person\",\"name\",\"allen\").has(\"age\", 33)"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"allen\").has(\"age\", 31)"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"allen\").has(\"age\")"
+ And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"allen\").properties(\"age\")"
\ No newline at end of file