You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by xi...@apache.org on 2026/06/24 15:54:52 UTC
(tinkerpop) 01/03: Add cardinality
This is an automated email from the ASF dual-hosted git repository.
xiazcy pushed a commit to branch multi-label-experiment
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 27da3669c7d134d4c46e4bbb4deb856542389bf7
Author: Yang Xia <xi...@gmail.com>
AuthorDate: Wed Jun 17 07:49:19 2026 -0700
Add cardinality
---
.../grammar/DefaultGremlinBaseVisitor.java | 4 -
.../language/grammar/TraversalMethodVisitor.java | 19 ---
.../grammar/TraversalSourceSpawnMethodVisitor.java | 26 +---
.../process/traversal/step/map/AddVertexStep.java | 15 +-
.../apache/tinkerpop/gremlin/structure/Graph.java | 44 ++++++
.../gremlin/structure/LabelCardinality.java | 153 +++++++++++++++++++
.../structure/io/binary/types/EdgeSerializer.java | 15 +-
.../structure/io/binary/types/GraphSerializer.java | 6 +-
.../io/binary/types/VertexSerializer.java | 6 +-
.../structure/io/graphml/GraphMLWriter.java | 6 +-
.../io/graphson/GraphSONSerializersV1.java | 8 +-
.../io/graphson/GraphSONSerializersV2.java | 8 +-
.../io/graphson/GraphSONSerializersV3.java | 8 +-
.../structure/io/gryo/GryoSerializersV3.java | 10 +-
.../gremlin/structure/util/star/StarGraph.java | 14 +-
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 8 +-
gremlin-go/driver/cucumber/gremlin.go | 8 +-
.../gremlin-javascript/test/cucumber/gremlin.js | 8 +-
gremlin-language/src/main/antlr4/Gremlin.g4 | 2 -
.../src/main/python/tests/feature/gremlin.py | 8 +-
.../gremlin/language/translator/translations.json | 170 +++------------------
.../gremlin/test/features/map/AddEdge.feature | 32 ----
.../gremlin/test/features/map/MergeVertex.feature | 2 +-
.../test/features/sideEffect/AddLabel.feature | 4 +-
.../test/features/sideEffect/DropLabel.feature | 18 +--
.../ser/binary/TypeSerializerFailureTests.java | 7 +-
.../tinkergraph/structure/AbstractTinkerGraph.java | 27 ++++
.../gremlin/tinkergraph/structure/TinkerEdge.java | 38 +++--
.../gremlin/tinkergraph/structure/TinkerGraph.java | 10 +-
.../structure/TinkerTransactionGraph.java | 10 +-
.../tinkergraph/structure/TinkerVertex.java | 65 ++++++--
.../traversal/step/map/MergeVMultiLabelTest.java | 5 +-
.../step/sideEffect/LabelMutationPropertyTest.java | 7 +-
.../step/sideEffect/LabelMutationStepTest.java | 13 +-
.../structure/TinkerVertexMultiLabelTest.java | 24 +--
35 files changed, 438 insertions(+), 370 deletions(-)
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 5257be977a..a3cc037253 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
@@ -167,10 +167,6 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im
* {@inheritDoc}
*/
@Override public T visitTraversalMethod_addE_String(final GremlinParser.TraversalMethod_addE_StringContext ctx) { notImplemented(ctx); return null; }
- /**
- * {@inheritDoc}
- */
- @Override public T visitTraversalMethod_addE_StringVarargs(final GremlinParser.TraversalMethod_addE_StringVarargsContext ctx) { notImplemented(ctx); return null; }
/**
* {@inheritDoc}
*/
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 cc65794ed6..cf99aeb3e5 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
@@ -205,25 +205,6 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
}
}
- /**
- * {@inheritDoc}
- */
- @Override
- public GraphTraversal visitTraversalMethod_addE_StringVarargs(final GremlinParser.TraversalMethod_addE_StringVarargsContext ctx) {
- final List<GremlinParser.StringArgumentContext> args = ctx.stringArgument();
- final Object firstLiteralOrVar = antlr.argumentVisitor.visitStringArgument(args.get(0));
- final String firstLabel = firstLiteralOrVar instanceof String ? (String) firstLiteralOrVar : ((GValue<String>) firstLiteralOrVar).get();
- final Object secondLiteralOrVar = antlr.argumentVisitor.visitStringArgument(args.get(1));
- final String secondLabel = secondLiteralOrVar instanceof String ? (String) secondLiteralOrVar : ((GValue<String>) secondLiteralOrVar).get();
-
- final String[] moreLabels = new String[args.size() - 2];
- for (int i = 2; i < args.size(); i++) {
- final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(args.get(i));
- moreLabels[i - 2] = literalOrVar instanceof String ? (String) literalOrVar : ((GValue<String>) literalOrVar).get();
- }
- return this.graphTraversal.addE(firstLabel, secondLabel, moreLabels);
- }
-
/**
* {@inheritDoc}
*/
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
index 5ce9b41c09..5ce311697f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
@@ -57,27 +57,13 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor
*/
@Override
public GraphTraversal visitTraversalSourceSpawnMethod_addE(final GremlinParser.TraversalSourceSpawnMethod_addEContext ctx) {
- final List<GremlinParser.StringArgumentContext> stringArgs = ctx.stringArgument();
- if (stringArgs != null && !stringArgs.isEmpty()) {
- if (stringArgs.size() == 1) {
- final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(stringArgs.get(0));
- if (GValue.valueInstanceOf(literalOrVar, String.class)) {
- return this.traversalSource.addE((GValue<String>) literalOrVar);
- } else {
- return this.traversalSource.addE((String) literalOrVar);
- }
+ final GremlinParser.StringArgumentContext stringArg = ctx.stringArgument();
+ if (stringArg != null) {
+ final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(stringArg);
+ if (GValue.valueInstanceOf(literalOrVar, String.class)) {
+ return this.traversalSource.addE((GValue<String>) literalOrVar);
} else {
- // Multi-label: addE("a", "b", ...)
- final Object firstLiteralOrVar = antlr.argumentVisitor.visitStringArgument(stringArgs.get(0));
- final String firstLabel = firstLiteralOrVar instanceof String ? (String) firstLiteralOrVar : ((GValue<String>) firstLiteralOrVar).get();
- final Object secondLiteralOrVar = antlr.argumentVisitor.visitStringArgument(stringArgs.get(1));
- final String secondLabel = secondLiteralOrVar instanceof String ? (String) secondLiteralOrVar : ((GValue<String>) secondLiteralOrVar).get();
- final String[] moreLabels = new String[stringArgs.size() - 2];
- for (int i = 2; i < stringArgs.size(); i++) {
- final Object literalOrVar = antlr.argumentVisitor.visitStringArgument(stringArgs.get(i));
- moreLabels[i - 2] = literalOrVar instanceof String ? (String) literalOrVar : ((GValue<String>) literalOrVar).get();
- }
- return this.traversalSource.addE(firstLabel, secondLabel, moreLabels);
+ return this.traversalSource.addE((String) literalOrVar);
}
} else if (ctx.nestedTraversal() != null) {
return this.traversalSource.addE(anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal()));
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index 4c2b505b53..8a48d1728b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -49,24 +49,27 @@ public class AddVertexStep<S> extends ScalarMapStep<S, Vertex> implements AddVer
public AddVertexStep(final Traversal.Admin traversal, final String label) {
super(traversal);
- this.internalParameters.set(this, T.label, null == label ? Vertex.DEFAULT_LABEL : label);
+ if (label != null) {
+ this.internalParameters.set(this, T.label, label);
+ }
userProvidedLabel = label != null;
}
public AddVertexStep(final Traversal.Admin traversal, final Traversal.Admin<S,String> vertexLabelTraversal) {
super(traversal);
- this.internalParameters.set(this, T.label, null == vertexLabelTraversal ? Vertex.DEFAULT_LABEL : vertexLabelTraversal);
+ if (vertexLabelTraversal != null) {
+ this.internalParameters.set(this, T.label, vertexLabelTraversal);
+ }
userProvidedLabel = vertexLabelTraversal != null;
}
public AddVertexStep(final Traversal.Admin traversal, final Set<String> labels) {
super(traversal);
- if (labels == null || labels.isEmpty()) {
- this.internalParameters.set(this, T.label, Vertex.DEFAULT_LABEL);
- userProvidedLabel = false;
- } else {
+ if (labels != null && !labels.isEmpty()) {
this.internalParameters.set(this, T.label, labels);
userProvidedLabel = true;
+ } else {
+ userProvidedLabel = false;
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
index 3f9f246297..fe6291e616 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
@@ -655,6 +655,28 @@ public interface Graph extends AutoCloseable, Host {
return new VertexPropertyFeatures() {
};
}
+
+ /**
+ * Gets the {@link LabelCardinality} for vertices in this graph. Defines how many labels
+ * a vertex can have and whether labels are mutable.
+ *
+ * @return the label cardinality for vertices, defaulting to {@link LabelCardinality#ZERO_OR_MORE}
+ * @since 4.0.0
+ */
+ default LabelCardinality getLabelCardinality() {
+ return LabelCardinality.ZERO_OR_MORE;
+ }
+
+ /**
+ * Gets the default label returned for vertices with no explicit labels when the cardinality
+ * requires at least one label ({@link LabelCardinality#ONE} or {@link LabelCardinality#ONE_OR_MORE}).
+ *
+ * @return the default vertex label, typically {@link Vertex#DEFAULT_LABEL}
+ * @since 4.0.0
+ */
+ default String getDefaultLabel() {
+ return Vertex.DEFAULT_LABEL;
+ }
}
/**
@@ -703,6 +725,28 @@ public interface Graph extends AutoCloseable, Host {
return new EdgePropertyFeatures() {
};
}
+
+ /**
+ * Gets the {@link LabelCardinality} for edges in this graph. Defines how many labels
+ * an edge can have and whether labels are mutable.
+ *
+ * @return the label cardinality for edges, defaulting to {@link LabelCardinality#ZERO_OR_ONE}
+ * @since 4.0.0
+ */
+ default LabelCardinality getLabelCardinality() {
+ return LabelCardinality.ZERO_OR_ONE;
+ }
+
+ /**
+ * Gets the default label returned for edges with no explicit labels when the cardinality
+ * requires at least one label ({@link LabelCardinality#ONE} or {@link LabelCardinality#ONE_OR_MORE}).
+ *
+ * @return the default edge label, typically {@link Edge#DEFAULT_LABEL}
+ * @since 4.0.0
+ */
+ default String getDefaultLabel() {
+ return Edge.DEFAULT_LABEL;
+ }
}
/**
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/LabelCardinality.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/LabelCardinality.java
new file mode 100644
index 0000000000..690fa75cef
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/LabelCardinality.java
@@ -0,0 +1,153 @@
+/*
+ * 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.structure;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Defines the label cardinality for graph elements (vertices and edges). Providers declare their
+ * supported cardinality via {@link Graph.Features} and use the validation methods to enforce
+ * label mutation constraints.
+ * <p>
+ * The four cardinality modes are:
+ * <ul>
+ * <li>{@link #ONE} — Exactly one label, immutable (classic TinkerPop 3.x behavior)</li>
+ * <li>{@link #ZERO_OR_ONE} — Zero or one label, mutable, droppable to zero</li>
+ * <li>{@link #ONE_OR_MORE} — One or more labels, must always have at least one (virtual default fills when empty)</li>
+ * <li>{@link #ZERO_OR_MORE} — Zero or more labels, fully flexible</li>
+ * </ul>
+ *
+ * @since 4.0.0
+ */
+public enum LabelCardinality {
+
+ /**
+ * Exactly one label, immutable. All mutation operations throw.
+ * This provides backward compatibility with TinkerPop 3.x single-label semantics.
+ */
+ ONE,
+
+ /**
+ * Zero or one label, mutable. The element can have at most one label at a time.
+ * To change the label, the existing one must be dropped first.
+ */
+ ZERO_OR_ONE,
+
+ /**
+ * One or more labels, mutable. The element must always have at least one label.
+ * When all explicit labels are removed, the provider's default label (e.g., "vertex" or "edge")
+ * is returned at read time as a virtual floor.
+ */
+ ONE_OR_MORE,
+
+ /**
+ * Zero or more labels, fully flexible. No constraints on the number of labels.
+ * Elements can have any number of labels including zero.
+ */
+ ZERO_OR_MORE;
+
+ /**
+ * Whether this cardinality allows multiple labels on an element simultaneously.
+ *
+ * @return {@code true} for {@link #ONE_OR_MORE} and {@link #ZERO_OR_MORE}
+ */
+ public boolean supportsMultiLabel() {
+ return this == ONE_OR_MORE || this == ZERO_OR_MORE;
+ }
+
+ /**
+ * Whether this cardinality allows an element to have zero labels.
+ *
+ * @return {@code true} for {@link #ZERO_OR_ONE} and {@link #ZERO_OR_MORE}
+ */
+ public boolean supportsZeroLabels() {
+ return this == ZERO_OR_ONE || this == ZERO_OR_MORE;
+ }
+
+ /**
+ * Whether this cardinality allows label mutation (addLabel/dropLabel).
+ *
+ * @return {@code true} for all modes except {@link #ONE}
+ */
+ public boolean supportsMutation() {
+ return this != ONE;
+ }
+
+ /**
+ * Validates that adding the specified labels would not violate this cardinality's constraints.
+ * Must be called BEFORE any mutation to ensure no invalid intermediate states.
+ *
+ * @param currentLabels the element's current label set
+ * @param label the first label to add
+ * @param moreLabels additional labels to add (varargs)
+ * @throws IllegalStateException if the addition would violate cardinality constraints
+ */
+ public void validateAdd(final Set<String> currentLabels, final String label, final String... moreLabels) {
+ if (!supportsMutation())
+ throw new IllegalStateException("Label mutation is not supported with cardinality " + this +
+ ". Labels are immutable once assigned.");
+
+ if (!supportsMultiLabel()) {
+ // ZERO_OR_ONE: resulting set must have at most 1 element
+ // Fast path: single label + empty set → always valid
+ if (moreLabels.length == 0 && currentLabels.isEmpty())
+ return;
+
+ // Compute the resulting set size
+ final Set<String> resultSet = new HashSet<>(currentLabels);
+ resultSet.add(label);
+ for (final String l : moreLabels) {
+ resultSet.add(l);
+ }
+ if (resultSet.size() > 1)
+ throw new IllegalStateException("Cannot add label: would result in " + resultSet.size() +
+ " labels but cardinality is " + this + ". Drop the existing label first.");
+ }
+ }
+
+ /**
+ * Validates that dropping a specific label would not violate this cardinality's constraints.
+ * For mutable cardinalities, dropping always succeeds (floor is applied at read time).
+ *
+ * @param currentLabels the element's current label set
+ * @param label the label to drop
+ * @throws IllegalStateException if mutation is not supported (ONE mode)
+ */
+ public void validateDrop(final Set<String> currentLabels, final String label) {
+ if (!supportsMutation())
+ throw new IllegalStateException("Label mutation is not supported with cardinality " + this +
+ ". Labels are immutable once assigned.");
+ // No floor validation needed — floor is applied at read time by the element's labels() method
+ }
+
+ /**
+ * Validates that dropping all labels would not violate this cardinality's constraints.
+ * For mutable cardinalities, dropping all always succeeds (floor is applied at read time).
+ *
+ * @param currentLabels the element's current label set
+ * @throws IllegalStateException if mutation is not supported (ONE mode)
+ */
+ public void validateDropAll(final Set<String> currentLabels) {
+ if (!supportsMutation())
+ throw new IllegalStateException("Label mutation is not supported with cardinality " + this +
+ ". Labels are immutable once assigned.");
+ // Always succeeds for mutable cardinalities — floor applied at read time
+ }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EdgeSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EdgeSerializer.java
index fe48d6f6ae..2f63384fcf 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EdgeSerializer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EdgeSerializer.java
@@ -100,26 +100,17 @@ public class EdgeSerializer extends SimpleTypeSerializer<Edge> {
context.write(value.id(), buffer);
// Write all edge labels as List<String> for multi-label support
final Set<String> edgeLabels = value.labels();
- if (edgeLabels == null || edgeLabels.isEmpty()) {
- throw new IOException("Unexpected null or empty labels when nullable is false");
- }
- context.writeValue(new ArrayList<>(edgeLabels), buffer, false);
+ context.writeValue(edgeLabels == null ? new ArrayList<>() : new ArrayList<>(edgeLabels), buffer, false);
context.write(value.inVertex().id(), buffer);
// Write all inVertex labels as List<String> for multi-label support
final Set<String> inVLabels = value.inVertex().labels();
- if (inVLabels == null || inVLabels.isEmpty()) {
- throw new IOException("Unexpected null or empty labels when nullable is false");
- }
- context.writeValue(new ArrayList<>(inVLabels), buffer, false);
+ context.writeValue(inVLabels == null ? new ArrayList<>() : new ArrayList<>(inVLabels), buffer, false);
context.write(value.outVertex().id(), buffer);
// Write all outVertex labels as List<String> for multi-label support
final Set<String> outVLabels = value.outVertex().labels();
- if (outVLabels == null || outVLabels.isEmpty()) {
- throw new IOException("Unexpected null or empty labels when nullable is false");
- }
- context.writeValue(new ArrayList<>(outVLabels), buffer, false);
+ context.writeValue(outVLabels == null ? new ArrayList<>() : new ArrayList<>(outVLabels), buffer, false);
// we don't serialize the parent Vertex for edges.
context.write(null, buffer);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/GraphSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/GraphSerializer.java
index 4138555dc1..62f5e2546c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/GraphSerializer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/GraphSerializer.java
@@ -135,7 +135,8 @@ public class GraphSerializer extends SimpleTypeSerializer<Graph> {
context.write(vertex.id(), buffer);
// serializing label as list here for now according to GraphBinaryV4
- context.writeValue(Collections.singletonList(vertex.label()), buffer, false);
+ final String vLabel = vertex.label();
+ context.writeValue(vLabel == null ? Collections.emptyList() : Collections.singletonList(vLabel), buffer, false);
context.writeValue(vertexProperties.size(), buffer, false);
for (VertexProperty<Object> vp : vertexProperties) {
@@ -154,7 +155,8 @@ public class GraphSerializer extends SimpleTypeSerializer<Graph> {
private void writeEdge(Buffer buffer, GraphBinaryWriter context, Edge edge) throws IOException {
context.write(edge.id(), buffer);
// serializing label as list here for now according to GraphBinaryV4
- context.writeValue(Collections.singletonList(edge.label()), buffer, false);
+ final String eLabel = edge.label();
+ context.writeValue(eLabel == null ? Collections.emptyList() : Collections.singletonList(eLabel), buffer, false);
context.write(edge.inVertex().id(), buffer);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/VertexSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/VertexSerializer.java
index ce6befecba..f5074cc643 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/VertexSerializer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/VertexSerializer.java
@@ -72,11 +72,9 @@ public class VertexSerializer extends SimpleTypeSerializer<Vertex> {
protected void writeValue(final Vertex value, final Buffer buffer, final GraphBinaryWriter context) throws IOException {
context.write(value.id(), buffer);
// Write all labels as List<String> for multi-label support.
+ // Empty list is valid for zero-label elements (ZERO_OR_MORE / ZERO_OR_ONE cardinality).
final java.util.Set<String> labels = value.labels();
- if (labels == null || labels.isEmpty()) {
- throw new IOException("Unexpected null or empty labels when nullable is false");
- }
- context.writeValue(new ArrayList<>(labels), buffer, false);
+ context.writeValue(labels == null ? new ArrayList<>() : new ArrayList<>(labels), buffer, false);
if (value instanceof ReferenceVertex) {
context.write(null, buffer);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
index cae0f9c987..0c75805f3a 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
@@ -266,7 +266,7 @@ public final class GraphMLWriter implements GraphWriter {
writer.writeStartElement(GraphMLTokens.DATA);
writer.writeAttribute(GraphMLTokens.KEY, this.edgeLabelKey);
- writer.writeCharacters(edge.label());
+ writer.writeCharacters(edge.label() == null ? "" : edge.label());
writer.writeEndElement();
final List<String> keys = new ArrayList<>(edge.keys());
@@ -296,7 +296,7 @@ public final class GraphMLWriter implements GraphWriter {
writer.writeStartElement(GraphMLTokens.DATA);
writer.writeAttribute(GraphMLTokens.KEY, this.edgeLabelKey);
- writer.writeCharacters(edge.label());
+ writer.writeCharacters(edge.label() == null ? "" : edge.label());
writer.writeEndElement();
for (String key : edge.keys()) {
@@ -328,7 +328,7 @@ public final class GraphMLWriter implements GraphWriter {
writer.writeStartElement(GraphMLTokens.DATA);
writer.writeAttribute(GraphMLTokens.KEY, this.vertexLabelKey);
- writer.writeCharacters(vertex.label());
+ writer.writeCharacters(vertex.label() == null ? "" : vertex.label());
writer.writeEndElement();
for (String key : keys) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1.java
index 4841b482b4..32a49a34c2 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1.java
@@ -140,10 +140,10 @@ final class GraphSONSerializersV1 {
if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName());
GraphSONUtil.writeWithType(GraphSONTokens.ID, edge.id(), jsonGenerator, serializerProvider, typeSerializer);
- jsonGenerator.writeStringField(GraphSONTokens.LABEL, edge.label());
+ jsonGenerator.writeStringField(GraphSONTokens.LABEL, edge.label() == null ? "" : edge.label());
jsonGenerator.writeStringField(GraphSONTokens.TYPE, GraphSONTokens.EDGE);
- jsonGenerator.writeStringField(GraphSONTokens.IN_LABEL, edge.inVertex().label());
- jsonGenerator.writeStringField(GraphSONTokens.OUT_LABEL, edge.outVertex().label());
+ jsonGenerator.writeStringField(GraphSONTokens.IN_LABEL, edge.inVertex().label() == null ? "" : edge.inVertex().label());
+ jsonGenerator.writeStringField(GraphSONTokens.OUT_LABEL, edge.outVertex().label() == null ? "" : edge.outVertex().label());
GraphSONUtil.writeWithType(GraphSONTokens.IN, edge.inVertex().id(), jsonGenerator, serializerProvider, typeSerializer);
GraphSONUtil.writeWithType(GraphSONTokens.OUT, edge.outVertex().id(), jsonGenerator, serializerProvider, typeSerializer);
writeProperties(edge, jsonGenerator, serializerProvider, typeSerializer);
@@ -196,7 +196,7 @@ final class GraphSONSerializersV1 {
jsonGenerator.writeStartObject();
if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName());
GraphSONUtil.writeWithType(GraphSONTokens.ID, vertex.id(), jsonGenerator, serializerProvider, typeSerializer);
- jsonGenerator.writeStringField(GraphSONTokens.LABEL, vertex.label());
+ jsonGenerator.writeStringField(GraphSONTokens.LABEL, vertex.label() == null ? "" : vertex.label());
jsonGenerator.writeStringField(GraphSONTokens.TYPE, GraphSONTokens.VERTEX);
writeProperties(vertex, jsonGenerator, serializerProvider, typeSerializer);
jsonGenerator.writeEndObject();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2.java
index 8b52323c94..871569d2a4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2.java
@@ -101,7 +101,7 @@ class GraphSONSerializersV2 {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField(GraphSONTokens.ID, vertex.id());
- jsonGenerator.writeStringField(GraphSONTokens.LABEL, vertex.label());
+ jsonGenerator.writeStringField(GraphSONTokens.LABEL, vertex.label() == null ? "" : vertex.label());
writeProperties(vertex, jsonGenerator);
jsonGenerator.writeEndObject();
@@ -150,9 +150,9 @@ class GraphSONSerializersV2 {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField(GraphSONTokens.ID, edge.id());
- jsonGenerator.writeStringField(GraphSONTokens.LABEL, edge.label());
- jsonGenerator.writeStringField(GraphSONTokens.IN_LABEL, edge.inVertex().label());
- jsonGenerator.writeStringField(GraphSONTokens.OUT_LABEL, edge.outVertex().label());
+ jsonGenerator.writeStringField(GraphSONTokens.LABEL, edge.label() == null ? "" : edge.label());
+ jsonGenerator.writeStringField(GraphSONTokens.IN_LABEL, edge.inVertex().label() == null ? "" : edge.inVertex().label());
+ jsonGenerator.writeStringField(GraphSONTokens.OUT_LABEL, edge.outVertex().label() == null ? "" : edge.outVertex().label());
jsonGenerator.writeObjectField(GraphSONTokens.IN, edge.inVertex().id());
jsonGenerator.writeObjectField(GraphSONTokens.OUT, edge.outVertex().id());
writeProperties(edge, jsonGenerator);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3.java
index 6df9141f5b..c9f2520556 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3.java
@@ -101,7 +101,7 @@ class GraphSONSerializersV3 {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField(GraphSONTokens.ID, vertex.id());
- jsonGenerator.writeStringField(GraphSONTokens.LABEL, vertex.label());
+ jsonGenerator.writeStringField(GraphSONTokens.LABEL, vertex.label() == null ? "" : vertex.label());
writeTypeForGraphObjectIfUntyped(jsonGenerator, typeInfo, GraphSONTokens.VERTEX);
writeProperties(vertex, jsonGenerator, serializerProvider);
@@ -161,10 +161,10 @@ class GraphSONSerializersV3 {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField(GraphSONTokens.ID, edge.id());
- jsonGenerator.writeStringField(GraphSONTokens.LABEL, edge.label());
+ jsonGenerator.writeStringField(GraphSONTokens.LABEL, edge.label() == null ? "" : edge.label());
writeTypeForGraphObjectIfUntyped(jsonGenerator, typeInfo, GraphSONTokens.EDGE);
- jsonGenerator.writeStringField(GraphSONTokens.IN_LABEL, edge.inVertex().label());
- jsonGenerator.writeStringField(GraphSONTokens.OUT_LABEL, edge.outVertex().label());
+ jsonGenerator.writeStringField(GraphSONTokens.IN_LABEL, edge.inVertex().label() == null ? "" : edge.inVertex().label());
+ jsonGenerator.writeStringField(GraphSONTokens.OUT_LABEL, edge.outVertex().label() == null ? "" : edge.outVertex().label());
jsonGenerator.writeObjectField(GraphSONTokens.IN, edge.inVertex().id());
jsonGenerator.writeObjectField(GraphSONTokens.OUT, edge.outVertex().id());
writeProperties(edge, jsonGenerator);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3.java
index 5a0beec2ae..e53f1aacef 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3.java
@@ -74,7 +74,7 @@ public final class GryoSerializersV3 {
@Override
public <O extends OutputShim> void write(final KryoShim<?, O> kryo, final O output, final Edge edge) {
kryo.writeClassAndObject(output, edge.id());
- output.writeString(edge.label());
+ output.writeString(edge.label() == null ? "" : edge.label());
kryo.writeClassAndObject(output, edge.inVertex().id());
// temporary try/catch perhaps? need this to get SparkSingleIterationStrategyTest to work. Trying to grab
@@ -84,7 +84,8 @@ public final class GryoSerializersV3 {
//
// ghetto
try {
- output.writeString(edge.inVertex().label());
+ final String inLabel = edge.inVertex().label();
+ output.writeString(inLabel == null ? Vertex.DEFAULT_LABEL : inLabel);
} catch (Exception ex) {
output.writeString(Vertex.DEFAULT_LABEL);
}
@@ -93,7 +94,8 @@ public final class GryoSerializersV3 {
// same nonsense as above for a default label
try {
- output.writeString(edge.outVertex().label());
+ final String outLabel = edge.outVertex().label();
+ output.writeString(outLabel == null ? Vertex.DEFAULT_LABEL : outLabel);
} catch (Exception ex) {
output.writeString(Vertex.DEFAULT_LABEL);
}
@@ -132,7 +134,7 @@ public final class GryoSerializersV3 {
@Override
public <O extends OutputShim> void write(final KryoShim<?, O> kryo, final O output, final Vertex vertex) {
kryo.writeClassAndObject(output, vertex.id());
- output.writeString(vertex.label());
+ output.writeString(vertex.label() == null ? "" : vertex.label());
final Iterator<? extends VertexProperty> properties = vertex.properties();
output.writeBoolean(properties.hasNext());
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java
index 1981a3257b..30c5cb9066 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java
@@ -205,7 +205,13 @@ public final class StarGraph implements Graph, Serializable {
if (vertex instanceof StarVertex) return (StarGraph) vertex.graph();
// else convert to a star graph
final StarGraph starGraph = new StarGraph();
- final StarVertex starVertex = (StarVertex) starGraph.addVertex(T.id, vertex.id(), T.label, vertex.label());
+ final String vertexLabel = vertex.label();
+ final StarVertex starVertex;
+ if (vertexLabel != null) {
+ starVertex = (StarVertex) starGraph.addVertex(T.id, vertex.id(), T.label, vertexLabel);
+ } else {
+ starVertex = (StarVertex) starGraph.addVertex(T.id, vertex.id());
+ }
// Copy multi-labels from source vertex
final Set<String> srcLabels = vertex.labels();
@@ -221,7 +227,8 @@ public final class StarGraph implements Graph, Serializable {
vp.properties().forEachRemaining(p -> starVertexProperty.property(p.key(), p.value()));
});
vertex.edges(Direction.IN).forEachRemaining(edge -> {
- final Edge starEdge = starVertex.addInEdge(edge.label(), starGraph.addVertex(T.id, edge.outVertex().id()), T.id, edge.id());
+ final String edgeLabel = edge.label() == null ? Edge.DEFAULT_LABEL : edge.label();
+ final Edge starEdge = starVertex.addInEdge(edgeLabel, starGraph.addVertex(T.id, edge.outVertex().id()), T.id, edge.id());
edge.properties().forEachRemaining(p -> starEdge.property(p.key(), p.value()));
final Set<String> edgeSrcLabels = edge.labels();
if (edgeSrcLabels.size() > 1) {
@@ -230,7 +237,8 @@ public final class StarGraph implements Graph, Serializable {
});
vertex.edges(Direction.OUT).forEachRemaining(edge -> {
- final Edge starEdge = starVertex.addOutEdge(edge.label(), starGraph.addVertex(T.id, edge.inVertex().id()), T.id, edge.id());
+ final String edgeLabel = edge.label() == null ? Edge.DEFAULT_LABEL : edge.label();
+ final Edge starEdge = starVertex.addOutEdge(edgeLabel, starGraph.addVertex(T.id, edge.inVertex().id()), T.id, edge.id());
edge.properties().forEachRemaining(p -> starEdge.property(p.key(), p.value()));
final Set<String> edgeSrcLabels = edge.labels();
if (edgeSrcLabels.size() > 1) {
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 6e19a2d5e1..e2b0f2b16b 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -921,8 +921,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_unionXaddEXknowsvarXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").Property("age", 29).AddV((string) "person").Property("name", "vadas").Property("age", 27), (g,p) =>g.Union<object>(__.AddE((string) p["xx1"]).Property("weight", 1).From(__.V().Has("name", "marko")).To(__.V().Has("name", "vadas"))), (g,p) =>g.E( [...]
{"g_addEXedgeX_fromXV_hasXname_markoXX_toXV_hasXname_vadasXX_propertyXweight_0_5X_withXkey_valueX_valuesXweight_keyX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").Property("age", 29).AddV((string) "person").Property("name", "vadas").Property("age", 27), (g,p) =>g.AddE((string) "edge").From(__.V().Has("name", "marko")).To(__.V().Has("name", "vadas")).Property("weight", 0.5).With(" [...]
{"g_addEXknowsX_fromXV_hasXname_markoXX_toXV_hasXname_vadasXX_propertyXweight_0_5X_addEXknowsX_fromXV_hasXname_markoXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").Property("age", 29).AddV((string) "person").Property("name", "vadas").Property("age", 27), (g,p) =>g.AddE((string) "knows").From(__.V().Has("name", "marko")).To(__.V().Has("name", "vadas")).Property("weight", 0.5).Add [...]
- {"g_addEXa_bX_from_V_to_V_labels_fold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").AddV((string) "person").Property("name", "josh"), (g,p) =>g.AddE((string) "knows", (string) "trusts").From(__.V().Has("name", "marko")).To(__.V().Has("name", "josh")).Labels().Fold(), (g,p) =>g.E().HasLabel("knows"), (g,p) =>g.E().HasLabel("trusts")}},
- {"g_addEXa_b_cX_from_V_to_V_labels_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").AddV((string) "person").Property("name", "josh"), (g,p) =>g.AddE((string) "a", (string) "b", (string) "c").From(__.V().Has("name", "marko")).To(__.V().Has("name", "josh")).Labels().Count()}},
{"g_VX1X_addVXanimalX_propertyXage_selectXaX_byXageXX_propertyXname_puppyX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").Property("age", 29).As("marko").AddV((string) "person").Property("name", "vadas").Property("age", 27).As("vadas").AddV((string) "software").Property("name", "lop").Property("lang", "java").As("lop").AddV((string) "person").Property("name", "josh").Property("age [...]
{"g_V_addVXanimalX_propertyXage_0X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").Property("age", 29).As("marko").AddV((string) "person").Property("name", "vadas").Property("age", 27).As("vadas").AddV((string) "software").Property("name", "lop").Property("lang", "java").As("lop").AddV((string) "person").Property("name", "josh").Property("age", 32).As("josh").AddV((string) "softwar [...]
{"g_V_addVXanimalvarX_propertyXage_0varX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").Property("age", 29).As("marko").AddV((string) "person").Property("name", "vadas").Property("age", 27).As("vadas").AddV((string) "software").Property("name", "lop").Property("lang", "java").As("lop").AddV((string) "person").Property("name", "josh").Property("age", 32).As("josh").AddV((string) "s [...]
@@ -1996,7 +1994,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_hasLabelXpersonX_hasXname_markoX_addLabelXemployeeX_labels", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko"), (g,p) =>g.V().HasLabel("person").Has("name", "marko").AddLabel("employee").Labels().Fold(), (g,p) =>g.V().HasLabel("person").HasLabel("employee")}},
{"g_V_addLabelXa_bX_labels_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person"), (g,p) =>g.V().AddLabel("a", "b").Labels().Count()}},
{"g_V_addLabelXexistingX_labels_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person"), (g,p) =>g.V().AddLabel("person").Labels().Count()}},
- {"g_E_addLabelXfriendX_labels_fold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b"), (g,p) =>g.E().AddLabel("friend").Labels().Fold(), (g,p) =>g.E().HasLabel("knows"), (g,p) =>g.E().HasLabel("friend")}},
+ {"g_E_addLabelXfriendX_labels_fold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b"), (g,p) =>g.E().AddLabel("friend").Labels().Fold()}},
{"g_V_valueXnameX_aggregateXxX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Aggregate("x").Cap<object>("x")}},
{"g_V_aggregateXxX_byXnameX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("x").By("name").Cap<object>("x")}},
{"g_V_out_aggregateXaX_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out().Aggregate("a").Path()}},
@@ -2058,8 +2056,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_dropLabels_labels", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b"), (g,p) =>g.V().DropLabels().Labels()}},
{"g_V_dropLabelXa_bX_labels", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b", (string) "c"), (g,p) =>g.V().DropLabel("a", "b").Labels()}},
{"g_V_dropLabels_defaultLabel", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person"), (g,p) =>g.V().DropLabels().Labels()}},
- {"g_E_dropLabelXknowsX_labels", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").As("a").AddV((string) "person").As("b").AddE("knows", "trusts").From("a").To("b"), (g,p) =>g.E().DropLabel("knows").Labels().Fold(), (g,p) =>g.E().HasLabel("knows"), (g,p) =>g.E().HasLabel("trusts")}},
- {"g_E_dropLabels_labels", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").As("a").AddV((string) "person").As("b").AddE("knows", "trusts").From("a").To("b"), (g,p) =>g.E().DropLabels().Labels()}},
+ {"g_E_dropLabelXknowsX_labels", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").As("a").AddV((string) "person").As("b").AddE((string) "knows").From("a").To("b"), (g,p) =>g.E().DropLabel("knows").Labels().Fold()}},
+ {"g_E_dropLabels_labels", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV((string) "person").As("a").AddV((string) "person").As("b").AddE((string) "knows").From("a").To("b"), (g,p) =>g.E().DropLabels().Labels()}},
{"g_V_fail", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fail()}},
{"g_V_failXmsgX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fail("msg")}},
{"g_V_unionXout_failX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Union<object>(__.Out(), __.Fail())}},
diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go
index e44a411b01..5f5a2ea179 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -891,8 +891,6 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
"g_unionXaddEXknowsvarXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).AddV("person").Property("name", "vadas").Property("age", 27)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Union(gremlingo.T__.AddE(p["xx1"]).Property("weight", 1).From(grem [...]
"g_addEXedgeX_fromXV_hasXname_markoXX_toXV_hasXname_vadasXX_propertyXweight_0_5X_withXkey_valueX_valuesXweight_keyX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).AddV("person").Property("name", "vadas").Property("age", 27)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddE("edge").From(gremlingo.T__.V().Has(" [...]
"g_addEXknowsX_fromXV_hasXname_markoXX_toXV_hasXname_vadasXX_propertyXweight_0_5X_addEXknowsX_fromXV_hasXname_markoXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).AddV("person").Property("name", "vadas").Property("age", 27)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddE("knows").From(gremlingo.T__.V().Ha [...]
- "g_addEXa_bX_from_V_to_V_labels_fold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "josh")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddE("knows", "trusts").From(gremlingo.T__.V().Has("name", "marko")).To(gremlingo.T__.V().Has("name", "josh")).Labels().Fold()}, func(g *gremlingo.GraphTraversal [...]
- "g_addEXa_b_cX_from_V_to_V_labels_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "josh")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddE("a", "b", "c").From(gremlingo.T__.V().Has("name", "marko")).To(gremlingo.T__.V().Has("name", "josh")).Labels().Count()}},
"g_VX1X_addVXanimalX_propertyXage_selectXaX_byXageXX_propertyXname_puppyX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software") [...]
"g_V_addVXanimalX_propertyXage_0X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("name", "ripple").Property("la [...]
"g_V_addVXanimalvarX_propertyXage_0varX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("name", "ripple").Proper [...]
@@ -1966,7 +1964,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
"g_V_hasLabelXpersonX_hasXname_markoX_addLabelXemployeeX_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Has("name", "marko").AddLabel("employee").Labels().Fold()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal [...]
"g_V_addLabelXa_bX_labels_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().AddLabel("a", "b").Labels().Count()}},
"g_V_addLabelXexistingX_labels_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().AddLabel("person").Labels().Count()}},
- "g_E_addLabelXfriendX_labels_fold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "josh").As("b").AddE("knows").From("a").To("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().AddLabel("friend").Labels().Fold()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremling [...]
+ "g_E_addLabelXfriendX_labels_fold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "josh").As("b").AddE("knows").From("a").To("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().AddLabel("friend").Labels().Fold()}},
"g_V_valueXnameX_aggregateXxX_capXxX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Aggregate("x").Cap("x")}},
"g_V_aggregateXxX_byXnameX_capXxX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Aggregate("x").By("name").Cap("x")}},
"g_V_out_aggregateXaX_path": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Out().Aggregate("a").Path()}},
@@ -2028,8 +2026,8 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
"g_V_dropLabels_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("a", "b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().DropLabels().Labels()}},
"g_V_dropLabelXa_bX_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("a", "b", "c")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().DropLabel("a", "b").Labels()}},
"g_V_dropLabels_defaultLabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().DropLabels().Labels()}},
- "g_E_dropLabelXknowsX_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").As("a").AddV("person").As("b").AddE("knows", "trusts").From("a").To("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().DropLabel("knows").Labels().Fold()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().HasLabel("knows [...]
- "g_E_dropLabels_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").As("a").AddV("person").As("b").AddE("knows", "trusts").From("a").To("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().DropLabels().Labels()}},
+ "g_E_dropLabelXknowsX_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").As("a").AddV("person").As("b").AddE("knows").From("a").To("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().DropLabel("knows").Labels().Fold()}},
+ "g_E_dropLabels_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").As("a").AddV("person").As("b").AddE("knows").From("a").To("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().DropLabels().Labels()}},
"g_V_fail": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Fail()}},
"g_V_failXmsgX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Fail("msg")}},
"g_V_unionXout_failX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Union(gremlingo.T__.Out(), gremlingo.T__.Fail())}},
diff --git a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
index b0ff76c57d..9893904988 100644
--- a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
@@ -922,8 +922,6 @@ const gremlins = {
g_unionXaddEXknowsvarXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXXX: [function({g, xx1}) { return g.addV("person").property("name", "marko").property("age", 29).addV("person").property("name", "vadas").property("age", 27) }, function({g, xx1}) { return g.union(__.addE(xx1).property("weight", 1).from_(__.V().has("name", "marko")).to(__.V().has("name", "vadas"))) }, function({g, xx1}) { return g.E().has("knows", "weight", 1) }],
g_addEXedgeX_fromXV_hasXname_markoXX_toXV_hasXname_vadasXX_propertyXweight_0_5X_withXkey_valueX_valuesXweight_keyX: [function({g}) { return g.addV("person").property("name", "marko").property("age", 29).addV("person").property("name", "vadas").property("age", 27) }, function({g}) { return g.addE("edge").from_(__.V().has("name", "marko")).to(__.V().has("name", "vadas")).property("weight", 0.5).with_("key", "value").values("weight", "key") }],
g_addEXknowsX_fromXV_hasXname_markoXX_toXV_hasXname_vadasXX_propertyXweight_0_5X_addEXknowsX_fromXV_hasXname_markoXX: [function({g}) { return g.addV("person").property("name", "marko").property("age", 29).addV("person").property("name", "vadas").property("age", 27) }, function({g}) { return g.addE("knows").from_(__.V().has("name", "marko")).to(__.V().has("name", "vadas")).property("weight", 0.5).addE("knows").from_(__.V().has("name", "marko")) }],
- g_addEXa_bX_from_V_to_V_labels_fold: [function({g}) { return g.addV("person").property("name", "marko").addV("person").property("name", "josh") }, function({g}) { return g.addE("knows", "trusts").from_(__.V().has("name", "marko")).to(__.V().has("name", "josh")).labels().fold() }, function({g}) { return g.E().hasLabel("knows") }, function({g}) { return g.E().hasLabel("trusts") }],
- g_addEXa_b_cX_from_V_to_V_labels_count: [function({g}) { return g.addV("person").property("name", "marko").addV("person").property("name", "josh") }, function({g}) { return g.addE("a", "b", "c").from_(__.V().has("name", "marko")).to(__.V().has("name", "josh")).labels().count() }],
g_VX1X_addVXanimalX_propertyXage_selectXaX_byXageXX_propertyXname_puppyX: [function({g, vid1}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple").addV("p [...]
g_V_addVXanimalX_propertyXage_0X: [function({g}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple").addV("person").property("name", "peter").property("ag [...]
g_V_addVXanimalvarX_propertyXage_0varX: [function({g, xx1, xx2}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple").addV("person").property("name", "pete [...]
@@ -1997,7 +1995,7 @@ const gremlins = {
g_V_hasLabelXpersonX_hasXname_markoX_addLabelXemployeeX_labels: [function({g}) { return g.addV("person").property("name", "marko") }, function({g}) { return g.V().hasLabel("person").has("name", "marko").addLabel("employee").labels().fold() }, function({g}) { return g.V().hasLabel("person").hasLabel("employee") }],
g_V_addLabelXa_bX_labels_count: [function({g}) { return g.addV("person") }, function({g}) { return g.V().addLabel("a", "b").labels().count() }],
g_V_addLabelXexistingX_labels_count: [function({g}) { return g.addV("person") }, function({g}) { return g.V().addLabel("person").labels().count() }],
- g_E_addLabelXfriendX_labels_fold: [function({g}) { return g.addV("person").property("name", "marko").as("a").addV("person").property("name", "josh").as("b").addE("knows").from_("a").to("b") }, function({g}) { return g.E().addLabel("friend").labels().fold() }, function({g}) { return g.E().hasLabel("knows") }, function({g}) { return g.E().hasLabel("friend") }],
+ g_E_addLabelXfriendX_labels_fold: [function({g}) { return g.addV("person").property("name", "marko").as("a").addV("person").property("name", "josh").as("b").addE("knows").from_("a").to("b") }, function({g}) { return g.E().addLabel("friend").labels().fold() }],
g_V_valueXnameX_aggregateXxX_capXxX: [function({g}) { return g.V().values("name").aggregate("x").cap("x") }],
g_V_aggregateXxX_byXnameX_capXxX: [function({g}) { return g.V().aggregate("x").by("name").cap("x") }],
g_V_out_aggregateXaX_path: [function({g}) { return g.V().out().aggregate("a").path() }],
@@ -2059,8 +2057,8 @@ const gremlins = {
g_V_dropLabels_labels: [function({g}) { return g.addV("a", "b") }, function({g}) { return g.V().dropLabels().labels() }],
g_V_dropLabelXa_bX_labels: [function({g}) { return g.addV("a", "b", "c") }, function({g}) { return g.V().dropLabel("a", "b").labels() }],
g_V_dropLabels_defaultLabel: [function({g}) { return g.addV("person") }, function({g}) { return g.V().dropLabels().labels() }],
- g_E_dropLabelXknowsX_labels: [function({g}) { return g.addV("person").as("a").addV("person").as("b").addE("knows", "trusts").from_("a").to("b") }, function({g}) { return g.E().dropLabel("knows").labels().fold() }, function({g}) { return g.E().hasLabel("knows") }, function({g}) { return g.E().hasLabel("trusts") }],
- g_E_dropLabels_labels: [function({g}) { return g.addV("person").as("a").addV("person").as("b").addE("knows", "trusts").from_("a").to("b") }, function({g}) { return g.E().dropLabels().labels() }],
+ g_E_dropLabelXknowsX_labels: [function({g}) { return g.addV("person").as("a").addV("person").as("b").addE("knows").from_("a").to("b") }, function({g}) { return g.E().dropLabel("knows").labels().fold() }],
+ g_E_dropLabels_labels: [function({g}) { return g.addV("person").as("a").addV("person").as("b").addE("knows").from_("a").to("b") }, function({g}) { return g.E().dropLabels().labels() }],
g_V_fail: [function({g}) { return g.V().fail() }],
g_V_failXmsgX: [function({g}) { return g.V().fail("msg") }],
g_V_unionXout_failX: [function({g}) { return g.V().union(__.out(), __.fail()) }],
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4
index 2d7fa316a6..b60c62c6e8 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -113,7 +113,6 @@ traversalSourceSpawnMethod
traversalSourceSpawnMethod_addE
: K_ADDE LPAREN RPAREN
| K_ADDE LPAREN stringArgument RPAREN
- | K_ADDE LPAREN stringArgument COMMA stringArgument (COMMA stringArgument)* RPAREN
| K_ADDE LPAREN nestedTraversal RPAREN
;
@@ -329,7 +328,6 @@ traversalMethod_E
traversalMethod_addE
: K_ADDE LPAREN RPAREN #traversalMethod_addE_Empty
| K_ADDE LPAREN stringArgument RPAREN #traversalMethod_addE_String
- | K_ADDE LPAREN stringArgument (COMMA stringArgument)+ RPAREN #traversalMethod_addE_StringVarargs
| K_ADDE LPAREN nestedTraversal RPAREN #traversalMethod_addE_Traversal
;
diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py b/gremlin-python/src/main/python/tests/feature/gremlin.py
index 3c9709c755..61c4af5360 100644
--- a/gremlin-python/src/main/python/tests/feature/gremlin.py
+++ b/gremlin-python/src/main/python/tests/feature/gremlin.py
@@ -896,8 +896,6 @@ world.gremlins = {
'g_unionXaddEXknowsvarXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXXX': [(lambda g, xx1=None:g.add_v('person').property('name', 'marko').property('age', 29).add_v('person').property('name', 'vadas').property('age', 27)), (lambda g, xx1=None:g.union(__.add_e(xx1).property('weight', 1).from_(__.V().has('name', 'marko')).to(__.V().has('name', 'vadas')))), (lambda g, xx1=None:g.E().has('knows', 'weight', 1))],
'g_addEXedgeX_fromXV_hasXname_markoXX_toXV_hasXname_vadasXX_propertyXweight_0_5X_withXkey_valueX_valuesXweight_keyX': [(lambda g:g.add_v('person').property('name', 'marko').property('age', 29).add_v('person').property('name', 'vadas').property('age', 27)), (lambda g:g.add_e('edge').from_(__.V().has('name', 'marko')).to(__.V().has('name', 'vadas')).property('weight', 0.5).with_('key', 'value').values('weight', 'key'))],
'g_addEXknowsX_fromXV_hasXname_markoXX_toXV_hasXname_vadasXX_propertyXweight_0_5X_addEXknowsX_fromXV_hasXname_markoXX': [(lambda g:g.add_v('person').property('name', 'marko').property('age', 29).add_v('person').property('name', 'vadas').property('age', 27)), (lambda g:g.add_e('knows').from_(__.V().has('name', 'marko')).to(__.V().has('name', 'vadas')).property('weight', 0.5).add_e('knows').from_(__.V().has('name', 'marko')))],
- 'g_addEXa_bX_from_V_to_V_labels_fold': [(lambda g:g.add_v('person').property('name', 'marko').add_v('person').property('name', 'josh')), (lambda g:g.add_e('knows', 'trusts').from_(__.V().has('name', 'marko')).to(__.V().has('name', 'josh')).labels().fold()), (lambda g:g.E().has_label('knows')), (lambda g:g.E().has_label('trusts'))],
- 'g_addEXa_b_cX_from_V_to_V_labels_count': [(lambda g:g.add_v('person').property('name', 'marko').add_v('person').property('name', 'josh')), (lambda g:g.add_e('a', 'b', 'c').from_(__.V().has('name', 'marko')).to(__.V().has('name', 'josh')).labels().count())],
'g_VX1X_addVXanimalX_propertyXage_selectXaX_byXageXX_propertyXname_puppyX': [(lambda g, vid1=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java').as_('ripple').add [...]
'g_V_addVXanimalX_propertyXage_0X': [(lambda g:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java').as_('ripple').add_v('person').property('name', 'peter').property('ag [...]
'g_V_addVXanimalvarX_propertyXage_0varX': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java').as_('ripple').add_v('person').property('nam [...]
@@ -1971,7 +1969,7 @@ world.gremlins = {
'g_V_hasLabelXpersonX_hasXname_markoX_addLabelXemployeeX_labels': [(lambda g:g.add_v('person').property('name', 'marko')), (lambda g:g.V().has_label('person').has('name', 'marko').add_label('employee').labels().fold()), (lambda g:g.V().has_label('person').has_label('employee'))],
'g_V_addLabelXa_bX_labels_count': [(lambda g:g.add_v('person')), (lambda g:g.V().add_label('a', 'b').labels().count())],
'g_V_addLabelXexistingX_labels_count': [(lambda g:g.add_v('person')), (lambda g:g.V().add_label('person').labels().count())],
- 'g_E_addLabelXfriendX_labels_fold': [(lambda g:g.add_v('person').property('name', 'marko').as_('a').add_v('person').property('name', 'josh').as_('b').add_e('knows').from_('a').to('b')), (lambda g:g.E().add_label('friend').labels().fold()), (lambda g:g.E().has_label('knows')), (lambda g:g.E().has_label('friend'))],
+ 'g_E_addLabelXfriendX_labels_fold': [(lambda g:g.add_v('person').property('name', 'marko').as_('a').add_v('person').property('name', 'josh').as_('b').add_e('knows').from_('a').to('b')), (lambda g:g.E().add_label('friend').labels().fold())],
'g_V_valueXnameX_aggregateXxX_capXxX': [(lambda g:g.V().values('name').aggregate('x').cap('x'))],
'g_V_aggregateXxX_byXnameX_capXxX': [(lambda g:g.V().aggregate('x').by('name').cap('x'))],
'g_V_out_aggregateXaX_path': [(lambda g:g.V().out().aggregate('a').path())],
@@ -2033,8 +2031,8 @@ world.gremlins = {
'g_V_dropLabels_labels': [(lambda g:g.add_v('a', 'b')), (lambda g:g.V().drop_labels().labels())],
'g_V_dropLabelXa_bX_labels': [(lambda g:g.add_v('a', 'b', 'c')), (lambda g:g.V().drop_label('a', 'b').labels())],
'g_V_dropLabels_defaultLabel': [(lambda g:g.add_v('person')), (lambda g:g.V().drop_labels().labels())],
- 'g_E_dropLabelXknowsX_labels': [(lambda g:g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows', 'trusts').from_('a').to('b')), (lambda g:g.E().drop_label('knows').labels().fold()), (lambda g:g.E().has_label('knows')), (lambda g:g.E().has_label('trusts'))],
- 'g_E_dropLabels_labels': [(lambda g:g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows', 'trusts').from_('a').to('b')), (lambda g:g.E().drop_labels().labels())],
+ 'g_E_dropLabelXknowsX_labels': [(lambda g:g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows').from_('a').to('b')), (lambda g:g.E().drop_label('knows').labels().fold())],
+ 'g_E_dropLabels_labels': [(lambda g:g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows').from_('a').to('b')), (lambda g:g.E().drop_labels().labels())],
'g_V_fail': [(lambda g:g.V().fail())],
'g_V_failXmsgX': [(lambda g:g.V().fail('msg'))],
'g_V_unionXout_failX': [(lambda g:g.V().union(__.out(), __.fail()))],
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
index 7de3961add..db9e62435f 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
@@ -17831,88 +17831,6 @@
}
]
},
- {
- "scenario": "g_addEXa_bX_from_V_to_V_labels_fold",
- "traversals": [
- {
- "original": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "language": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "canonical": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "anonymized": "g.addV(string0).property(string1, string2).addV(string0).property(string1, string3)",
- "dotnet": "g.AddV((string) \"person\").Property(\"name\", \"marko\").AddV((string) \"person\").Property(\"name\", \"josh\")",
- "go": "g.AddV(\"person\").Property(\"name\", \"marko\").AddV(\"person\").Property(\"name\", \"josh\")",
- "groovy": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "java": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "javascript": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "python": "g.add_v('person').property('name', 'marko').add_v('person').property('name', 'josh')"
- },
- {
- "original": "g.addE(\"knows\", \"trusts\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().fold()",
- "language": "g.addE(\"knows\", \"trusts\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().fold()",
- "canonical": "g.addE(\"knows\", \"trusts\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().fold()",
- "anonymized": "g.addE(string0, string1).from(__.V().has(string2, string3)).to(__.V().has(string2, string4)).labels().fold()",
- "dotnet": "g.AddE((string) \"knows\", (string) \"trusts\").From(__.V().Has(\"name\", \"marko\")).To(__.V().Has(\"name\", \"josh\")).Labels().Fold()",
- "go": "g.AddE(\"knows\", \"trusts\").From(gremlingo.T__.V().Has(\"name\", \"marko\")).To(gremlingo.T__.V().Has(\"name\", \"josh\")).Labels().Fold()",
- "groovy": "g.addE(\"knows\", \"trusts\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().fold()",
- "java": "g.addE(\"knows\", \"trusts\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().fold()",
- "javascript": "g.addE(\"knows\", \"trusts\").from_(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().fold()",
- "python": "g.add_e('knows', 'trusts').from_(__.V().has('name', 'marko')).to(__.V().has('name', 'josh')).labels().fold()"
- },
- {
- "original": "g.E().hasLabel(\"knows\")",
- "language": "g.E().hasLabel(\"knows\")",
- "canonical": "g.E().hasLabel(\"knows\")",
- "anonymized": "g.E().hasLabel(string0)",
- "dotnet": "g.E().HasLabel(\"knows\")",
- "go": "g.E().HasLabel(\"knows\")",
- "groovy": "g.E().hasLabel(\"knows\")",
- "java": "g.E().hasLabel(\"knows\")",
- "javascript": "g.E().hasLabel(\"knows\")",
- "python": "g.E().has_label('knows')"
- },
- {
- "original": "g.E().hasLabel(\"trusts\")",
- "language": "g.E().hasLabel(\"trusts\")",
- "canonical": "g.E().hasLabel(\"trusts\")",
- "anonymized": "g.E().hasLabel(string0)",
- "dotnet": "g.E().HasLabel(\"trusts\")",
- "go": "g.E().HasLabel(\"trusts\")",
- "groovy": "g.E().hasLabel(\"trusts\")",
- "java": "g.E().hasLabel(\"trusts\")",
- "javascript": "g.E().hasLabel(\"trusts\")",
- "python": "g.E().has_label('trusts')"
- }
- ]
- },
- {
- "scenario": "g_addEXa_b_cX_from_V_to_V_labels_count",
- "traversals": [
- {
- "original": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "language": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "canonical": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "anonymized": "g.addV(string0).property(string1, string2).addV(string0).property(string1, string3)",
- "dotnet": "g.AddV((string) \"person\").Property(\"name\", \"marko\").AddV((string) \"person\").Property(\"name\", \"josh\")",
- "go": "g.AddV(\"person\").Property(\"name\", \"marko\").AddV(\"person\").Property(\"name\", \"josh\")",
- "groovy": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "java": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "javascript": "g.addV(\"person\").property(\"name\", \"marko\").addV(\"person\").property(\"name\", \"josh\")",
- "python": "g.add_v('person').property('name', 'marko').add_v('person').property('name', 'josh')"
- },
- {
- "original": "g.addE(\"a\", \"b\", \"c\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().count()",
- "language": "g.addE(\"a\", \"b\", \"c\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().count()",
- "canonical": "g.addE(\"a\", \"b\", \"c\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().count()",
- "anonymized": "g.addE(string0, string1, string2).from(__.V().has(string3, string4)).to(__.V().has(string3, string5)).labels().count()",
- "dotnet": "g.AddE((string) \"a\", (string) \"b\", (string) \"c\").From(__.V().Has(\"name\", \"marko\")).To(__.V().Has(\"name\", \"josh\")).Labels().Count()",
- "go": "g.AddE(\"a\", \"b\", \"c\").From(gremlingo.T__.V().Has(\"name\", \"marko\")).To(gremlingo.T__.V().Has(\"name\", \"josh\")).Labels().Count()",
- "groovy": "g.addE(\"a\", \"b\", \"c\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().count()",
- "java": "g.addE(\"a\", \"b\", \"c\").from(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().count()",
- "javascript": "g.addE(\"a\", \"b\", \"c\").from_(__.V().has(\"name\", \"marko\")).to(__.V().has(\"name\", \"josh\")).labels().count()",
- "python": "g.add_e('a', 'b', 'c').from_(__.V().has('name', 'marko')).to(__.V().has('name', 'josh')).labels().count()"
- }
- ]
- },
{
"scenario": "g_VX1X_addVXanimalX_propertyXage_selectXaX_byXageXX_propertyXname_puppyX",
"traversals": [
@@ -41652,30 +41570,6 @@
"java": "g.E().addLabel(\"friend\").labels().fold()",
"javascript": "g.E().addLabel(\"friend\").labels().fold()",
"python": "g.E().add_label('friend').labels().fold()"
- },
- {
- "original": "g.E().hasLabel(\"knows\")",
- "language": "g.E().hasLabel(\"knows\")",
- "canonical": "g.E().hasLabel(\"knows\")",
- "anonymized": "g.E().hasLabel(string0)",
- "dotnet": "g.E().HasLabel(\"knows\")",
- "go": "g.E().HasLabel(\"knows\")",
- "groovy": "g.E().hasLabel(\"knows\")",
- "java": "g.E().hasLabel(\"knows\")",
- "javascript": "g.E().hasLabel(\"knows\")",
- "python": "g.E().has_label('knows')"
- },
- {
- "original": "g.E().hasLabel(\"friend\")",
- "language": "g.E().hasLabel(\"friend\")",
- "canonical": "g.E().hasLabel(\"friend\")",
- "anonymized": "g.E().hasLabel(string0)",
- "dotnet": "g.E().HasLabel(\"friend\")",
- "go": "g.E().HasLabel(\"friend\")",
- "groovy": "g.E().hasLabel(\"friend\")",
- "java": "g.E().hasLabel(\"friend\")",
- "javascript": "g.E().hasLabel(\"friend\")",
- "python": "g.E().has_label('friend')"
}
]
},
@@ -42792,16 +42686,16 @@
"scenario": "g_E_dropLabelXknowsX_labels",
"traversals": [
{
- "original": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "language": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "canonical": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "anonymized": "g.addV(string0).as(string1).addV(string0).as(string2).addE(string3, string4).from(string1).to(string2)",
- "dotnet": "g.AddV((string) \"person\").As(\"a\").AddV((string) \"person\").As(\"b\").AddE(\"knows\", \"trusts\").From(\"a\").To(\"b\")",
- "go": "g.AddV(\"person\").As(\"a\").AddV(\"person\").As(\"b\").AddE(\"knows\", \"trusts\").From(\"a\").To(\"b\")",
- "groovy": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "java": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "javascript": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from_(\"a\").to(\"b\")",
- "python": "g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows', 'trusts').from_('a').to('b')"
+ "original": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "language": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "canonical": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "anonymized": "g.addV(string0).as(string1).addV(string0).as(string2).addE(string3).from(string1).to(string2)",
+ "dotnet": "g.AddV((string) \"person\").As(\"a\").AddV((string) \"person\").As(\"b\").AddE((string) \"knows\").From(\"a\").To(\"b\")",
+ "go": "g.AddV(\"person\").As(\"a\").AddV(\"person\").As(\"b\").AddE(\"knows\").From(\"a\").To(\"b\")",
+ "groovy": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "java": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "javascript": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from_(\"a\").to(\"b\")",
+ "python": "g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows').from_('a').to('b')"
},
{
"original": "g.E().dropLabel(\"knows\").labels().fold()",
@@ -42814,30 +42708,6 @@
"java": "g.E().dropLabel(\"knows\").labels().fold()",
"javascript": "g.E().dropLabel(\"knows\").labels().fold()",
"python": "g.E().drop_label('knows').labels().fold()"
- },
- {
- "original": "g.E().hasLabel(\"knows\")",
- "language": "g.E().hasLabel(\"knows\")",
- "canonical": "g.E().hasLabel(\"knows\")",
- "anonymized": "g.E().hasLabel(string0)",
- "dotnet": "g.E().HasLabel(\"knows\")",
- "go": "g.E().HasLabel(\"knows\")",
- "groovy": "g.E().hasLabel(\"knows\")",
- "java": "g.E().hasLabel(\"knows\")",
- "javascript": "g.E().hasLabel(\"knows\")",
- "python": "g.E().has_label('knows')"
- },
- {
- "original": "g.E().hasLabel(\"trusts\")",
- "language": "g.E().hasLabel(\"trusts\")",
- "canonical": "g.E().hasLabel(\"trusts\")",
- "anonymized": "g.E().hasLabel(string0)",
- "dotnet": "g.E().HasLabel(\"trusts\")",
- "go": "g.E().HasLabel(\"trusts\")",
- "groovy": "g.E().hasLabel(\"trusts\")",
- "java": "g.E().hasLabel(\"trusts\")",
- "javascript": "g.E().hasLabel(\"trusts\")",
- "python": "g.E().has_label('trusts')"
}
]
},
@@ -42845,16 +42715,16 @@
"scenario": "g_E_dropLabels_labels",
"traversals": [
{
- "original": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "language": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "canonical": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "anonymized": "g.addV(string0).as(string1).addV(string0).as(string2).addE(string3, string4).from(string1).to(string2)",
- "dotnet": "g.AddV((string) \"person\").As(\"a\").AddV((string) \"person\").As(\"b\").AddE(\"knows\", \"trusts\").From(\"a\").To(\"b\")",
- "go": "g.AddV(\"person\").As(\"a\").AddV(\"person\").As(\"b\").AddE(\"knows\", \"trusts\").From(\"a\").To(\"b\")",
- "groovy": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "java": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from(\"a\").to(\"b\")",
- "javascript": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\", \"trusts\").from_(\"a\").to(\"b\")",
- "python": "g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows', 'trusts').from_('a').to('b')"
+ "original": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "language": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "canonical": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "anonymized": "g.addV(string0).as(string1).addV(string0).as(string2).addE(string3).from(string1).to(string2)",
+ "dotnet": "g.AddV((string) \"person\").As(\"a\").AddV((string) \"person\").As(\"b\").AddE((string) \"knows\").From(\"a\").To(\"b\")",
+ "go": "g.AddV(\"person\").As(\"a\").AddV(\"person\").As(\"b\").AddE(\"knows\").From(\"a\").To(\"b\")",
+ "groovy": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "java": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
+ "javascript": "g.addV(\"person\").as(\"a\").addV(\"person\").as(\"b\").addE(\"knows\").from_(\"a\").to(\"b\")",
+ "python": "g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows').from_('a').to('b')"
},
{
"original": "g.E().dropLabels().labels()",
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AddEdge.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AddEdge.feature
index 267cefaec9..70058962bc 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AddEdge.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AddEdge.feature
@@ -529,35 +529,3 @@ Feature: Step - addE()
"""
When iterated to list
Then the traversal will raise an error with message containing text of "must resolve to a Vertex or the ID of a Vertex present in the graph"
-
- @MultiLabel
- Scenario: g_addEXa_bX_from_V_to_V_labels_fold
- Given the empty graph
- And the graph initializer of
- """
- g.addV("person").property("name", "marko").addV("person").property("name", "josh")
- """
- And the traversal of
- """
- g.addE("knows", "trusts").from(__.V().has("name", "marko")).to(__.V().has("name", "josh")).labels().fold()
- """
- When iterated to list
- Then the result should have a count of 1
- And the graph should return 1 for count of "g.E().hasLabel(\"knows\")"
- And the graph should return 1 for count of "g.E().hasLabel(\"trusts\")"
-
- @MultiLabel
- Scenario: g_addEXa_b_cX_from_V_to_V_labels_count
- Given the empty graph
- And the graph initializer of
- """
- g.addV("person").property("name", "marko").addV("person").property("name", "josh")
- """
- And the traversal of
- """
- g.addE("a", "b", "c").from(__.V().has("name", "marko")).to(__.V().has("name", "josh")).labels().count()
- """
- When iterated to list
- Then the result should be unordered
- | result |
- | d[3].l |
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 930e3a181b..af095ecc7a 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
@@ -1112,5 +1112,5 @@ Feature: Step - mergeV()
When iterated to list
Then the result should have a count of 1
And the graph should return 1 for count of "g.V()"
- And the graph should return 1 for count of "g.V().hasLabel(\"vertex\")"
+ And the graph should return 0 for count of "g.V().hasLabel(\"vertex\")"
And the graph should return 0 for count of "g.V().hasLabel(\"person\")"
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/AddLabel.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/AddLabel.feature
index 5c0891ebb8..0837669796 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/AddLabel.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/AddLabel.feature
@@ -77,6 +77,4 @@ Feature: Step - addLabel()
g.E().addLabel("friend").labels().fold()
"""
When iterated to list
- Then the result should have a count of 1
- And the graph should return 1 for count of "g.E().hasLabel(\"knows\")"
- And the graph should return 1 for count of "g.E().hasLabel(\"friend\")"
+ Then the traversal will raise an error with message containing text of "Cannot add label"
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/DropLabel.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/DropLabel.feature
index 0c60f6c3c0..c96e375cde 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/DropLabel.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/DropLabel.feature
@@ -46,9 +46,7 @@ Feature: Step - dropLabel() / dropLabels()
g.V().dropLabels().labels()
"""
When iterated to list
- Then the result should be unordered
- | result |
- | vertex |
+ Then the result should have a count of 0
@MultiLabel
Scenario: g_V_dropLabelXa_bX_labels
@@ -78,16 +76,14 @@ Feature: Step - dropLabel() / dropLabels()
g.V().dropLabels().labels()
"""
When iterated to list
- Then the result should be unordered
- | result |
- | vertex |
+ Then the result should have a count of 0
@MultiLabel
Scenario: g_E_dropLabelXknowsX_labels
Given the empty graph
And the graph initializer of
"""
- g.addV("person").as("a").addV("person").as("b").addE("knows", "trusts").from("a").to("b")
+ g.addV("person").as("a").addV("person").as("b").addE("knows").from("a").to("b")
"""
And the traversal of
"""
@@ -95,21 +91,17 @@ Feature: Step - dropLabel() / dropLabels()
"""
When iterated to list
Then the result should have a count of 1
- And the graph should return 0 for count of "g.E().hasLabel(\"knows\")"
- And the graph should return 1 for count of "g.E().hasLabel(\"trusts\")"
@MultiLabel
Scenario: g_E_dropLabels_labels
Given the empty graph
And the graph initializer of
"""
- g.addV("person").as("a").addV("person").as("b").addE("knows", "trusts").from("a").to("b")
+ g.addV("person").as("a").addV("person").as("b").addE("knows").from("a").to("b")
"""
And the traversal of
"""
g.E().dropLabels().labels()
"""
When iterated to list
- Then the result should be unordered
- | result |
- | edge |
+ Then the result should have a count of 0
diff --git a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/TypeSerializerFailureTests.java b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/TypeSerializerFailureTests.java
index c23ccee3e0..fced442b11 100644
--- a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/TypeSerializerFailureTests.java
+++ b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/TypeSerializerFailureTests.java
@@ -57,7 +57,10 @@ public class TypeSerializerFailureTests {
@Parameterized.Parameters(name = "Value={0}")
public static Collection input() {
- final ReferenceVertex vertex = new ReferenceVertex("a vertex", (String) null);
+ // Use an unserializable id (raw Object has no registered TypeSerializer) to force
+ // serialization failure for vertex/edge cases. Previously null labels caused the
+ // failure, but now empty labels are serialized gracefully as empty lists.
+ final ReferenceVertex vertex = new ReferenceVertex(new Object(), "a vertex");
final BulkSet<Object> bulkSet = new BulkSet<>();
bulkSet.add(vertex, 1L);
@@ -72,7 +75,7 @@ public class TypeSerializerFailureTests {
vertex,
bulkSet,
Collections.singletonList(vertex),
- new ReferenceEdge("an edge", (String) null, vertex, vertex),
+ new ReferenceEdge(new Object(), "an edge", vertex, vertex),
Lambda.supplier(null),
metrics,
new DefaultTraversalMetrics(1L, Collections.singletonList(metrics)),
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/AbstractTinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/AbstractTinkerGraph.java
index b2b5b99281..3f1f994af9 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/AbstractTinkerGraph.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/AbstractTinkerGraph.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.LabelCardinality;
import org.apache.tinkerpop.gremlin.structure.Transaction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
@@ -63,6 +64,8 @@ public abstract class AbstractTinkerGraph implements Graph {
public static final String GREMLIN_TINKERGRAPH_GRAPH_FORMAT = "gremlin.tinkergraph.graphFormat";
public static final String GREMLIN_TINKERGRAPH_ALLOW_NULL_PROPERTY_VALUES = "gremlin.tinkergraph.allowNullPropertyValues";
public static final String GREMLIN_TINKERGRAPH_SERVICE = "gremlin.tinkergraph.service";
+ public static final String GREMLIN_TINKERGRAPH_VERTEX_LABEL_CARDINALITY = "gremlin.tinkergraph.vertexLabelCardinality";
+ public static final String GREMLIN_TINKERGRAPH_EDGE_LABEL_CARDINALITY = "gremlin.tinkergraph.edgeLabelCardinality";
protected AtomicLong currentId = new AtomicLong(-1L);
protected Map<Object, VertexProperty> vertexProperties = new ConcurrentHashMap<>();
@@ -77,6 +80,10 @@ public abstract class AbstractTinkerGraph implements Graph {
protected IdManager<VertexProperty> vertexPropertyIdManager;
protected VertexProperty.Cardinality defaultVertexPropertyCardinality;
protected boolean allowNullPropertyValues;
+ protected LabelCardinality vertexLabelCardinality;
+ protected LabelCardinality edgeLabelCardinality;
+ protected String defaultVertexLabel;
+ protected String defaultEdgeLabel;
protected TinkerServiceRegistry serviceRegistry;
@@ -385,6 +392,16 @@ public abstract class AbstractTinkerGraph implements Graph {
public VertexProperty.Cardinality getCardinality(final String key) {
return defaultVertexPropertyCardinality;
}
+
+ @Override
+ public LabelCardinality getLabelCardinality() {
+ return vertexLabelCardinality;
+ }
+
+ @Override
+ public String getDefaultLabel() {
+ return defaultVertexLabel;
+ }
}
public class TinkerGraphEdgeFeatures implements Features.EdgeFeatures {
@@ -406,6 +423,16 @@ public abstract class AbstractTinkerGraph implements Graph {
public boolean willAllowId(final Object id) {
return edgeIdManager.allow(id);
}
+
+ @Override
+ public LabelCardinality getLabelCardinality() {
+ return edgeLabelCardinality;
+ }
+
+ @Override
+ public String getDefaultLabel() {
+ return defaultEdgeLabel;
+ }
}
public class TinkerGraphVertexPropertyFeatures implements Features.VertexPropertyFeatures {
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java
index b37913a0aa..c55b3a30ce 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java
@@ -29,6 +29,7 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -131,12 +132,21 @@ public class TinkerEdge extends TinkerElement implements Edge {
@Override
public Set<String> labels() {
+ if (this.edgeLabels.isEmpty()) {
+ if (!this.graph.edgeLabelCardinality.supportsZeroLabels()) {
+ return Collections.singleton(this.graph.defaultEdgeLabel);
+ }
+ return Collections.emptySet();
+ }
return Collections.unmodifiableSet(this.edgeLabels);
}
@Override
@Deprecated
public String label() {
+ if (this.edgeLabels.isEmpty()) {
+ return this.graph.edgeLabelCardinality.supportsZeroLabels() ? null : this.graph.defaultEdgeLabel;
+ }
return this.edgeLabels.iterator().next();
}
@@ -147,11 +157,9 @@ public class TinkerEdge extends TinkerElement implements Edge {
for (final String l : labels) {
ElementHelper.validateLabel(l);
}
- // Remove default label if it was the only label
- if (this.edgeLabels.size() == 1 && this.edgeLabels.contains(Edge.DEFAULT_LABEL)) {
- this.graph.removeEdgeFromAdjacency(this, Edge.DEFAULT_LABEL);
- this.edgeLabels.remove(Edge.DEFAULT_LABEL);
- }
+
+ this.graph.edgeLabelCardinality.validateAdd(this.edgeLabels, label, labels);
+
this.edgeLabels.add(label);
this.graph.addEdgeToAdjacency(this, label);
for (final String l : labels) {
@@ -164,18 +172,22 @@ public class TinkerEdge extends TinkerElement implements Edge {
@Override
public void dropLabels() {
graph.touch(this);
- for (final String l : new LinkedHashSet<>(this.edgeLabels)) {
+ this.graph.edgeLabelCardinality.validateDropAll(this.edgeLabels);
+
+ for (final String l : new HashSet<>(this.edgeLabels)) {
this.graph.removeEdgeFromAdjacency(this, l);
}
this.edgeLabels.clear();
- this.edgeLabels.add(Edge.DEFAULT_LABEL);
- this.graph.addEdgeToAdjacency(this, Edge.DEFAULT_LABEL);
- this.label = this.edgeLabels.iterator().next();
+ this.label = this.edgeLabels.isEmpty()
+ ? (this.graph.edgeLabelCardinality.supportsZeroLabels() ? null : this.graph.defaultEdgeLabel)
+ : this.edgeLabels.iterator().next();
}
@Override
public void dropLabel(final String label, final String... labels) {
graph.touch(this);
+ this.graph.edgeLabelCardinality.validateDrop(this.edgeLabels, label);
+
if (this.edgeLabels.contains(label)) {
this.graph.removeEdgeFromAdjacency(this, label);
this.edgeLabels.remove(label);
@@ -186,11 +198,9 @@ public class TinkerEdge extends TinkerElement implements Edge {
this.edgeLabels.remove(l);
}
}
- if (this.edgeLabels.isEmpty()) {
- this.edgeLabels.add(Edge.DEFAULT_LABEL);
- this.graph.addEdgeToAdjacency(this, Edge.DEFAULT_LABEL);
- }
- this.label = this.edgeLabels.iterator().next();
+ this.label = this.edgeLabels.isEmpty()
+ ? (this.graph.edgeLabelCardinality.supportsZeroLabels() ? null : this.graph.defaultEdgeLabel)
+ : this.edgeLabels.iterator().next();
}
@Override
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
index b780b71281..b47810dc75 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.LabelCardinality;
import org.apache.tinkerpop.gremlin.structure.Transaction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
@@ -86,6 +87,13 @@ public class TinkerGraph extends AbstractTinkerGraph {
configuration.getString(GREMLIN_TINKERGRAPH_DEFAULT_VERTEX_PROPERTY_CARDINALITY, VertexProperty.Cardinality.single.name()));
allowNullPropertyValues = configuration.getBoolean(GREMLIN_TINKERGRAPH_ALLOW_NULL_PROPERTY_VALUES, false);
+ vertexLabelCardinality = LabelCardinality.valueOf(
+ configuration.getString(GREMLIN_TINKERGRAPH_VERTEX_LABEL_CARDINALITY, LabelCardinality.ZERO_OR_MORE.name()));
+ edgeLabelCardinality = LabelCardinality.valueOf(
+ configuration.getString(GREMLIN_TINKERGRAPH_EDGE_LABEL_CARDINALITY, LabelCardinality.ZERO_OR_ONE.name()));
+ defaultVertexLabel = Vertex.DEFAULT_LABEL;
+ defaultEdgeLabel = Edge.DEFAULT_LABEL;
+
graphLocation = configuration.getString(GREMLIN_TINKERGRAPH_GRAPH_LOCATION, null);
graphFormat = configuration.getString(GREMLIN_TINKERGRAPH_GRAPH_FORMAT, null);
@@ -136,7 +144,7 @@ public class TinkerGraph extends AbstractTinkerGraph {
ElementHelper.legalPropertyKeyValueArray(keyValues);
Object idValue = vertexIdManager.convert(ElementHelper.getIdValue(keyValues).orElse(null));
final Set<String> labels = ElementHelper.getLabelsValue(keyValues).orElse(
- Collections.singleton(Vertex.DEFAULT_LABEL));
+ Collections.singleton(defaultVertexLabel));
if (null != idValue) {
if (this.vertices.containsKey(idValue))
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraph.java
index ad1c59a511..48bfc7a3de 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraph.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraph.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.LabelCardinality;
import org.apache.tinkerpop.gremlin.structure.Transaction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
@@ -85,6 +86,13 @@ public final class TinkerTransactionGraph extends AbstractTinkerGraph {
configuration.getString(GREMLIN_TINKERGRAPH_DEFAULT_VERTEX_PROPERTY_CARDINALITY, VertexProperty.Cardinality.single.name()));
allowNullPropertyValues = configuration.getBoolean(GREMLIN_TINKERGRAPH_ALLOW_NULL_PROPERTY_VALUES, false);
+ vertexLabelCardinality = LabelCardinality.valueOf(
+ configuration.getString(GREMLIN_TINKERGRAPH_VERTEX_LABEL_CARDINALITY, LabelCardinality.ZERO_OR_MORE.name()));
+ edgeLabelCardinality = LabelCardinality.valueOf(
+ configuration.getString(GREMLIN_TINKERGRAPH_EDGE_LABEL_CARDINALITY, LabelCardinality.ZERO_OR_ONE.name()));
+ defaultVertexLabel = Vertex.DEFAULT_LABEL;
+ defaultEdgeLabel = Edge.DEFAULT_LABEL;
+
graphLocation = configuration.getString(GREMLIN_TINKERGRAPH_GRAPH_LOCATION, null);
graphFormat = configuration.getString(GREMLIN_TINKERGRAPH_GRAPH_FORMAT, null);
@@ -138,7 +146,7 @@ public final class TinkerTransactionGraph extends AbstractTinkerGraph {
if (null == idValue)
idValue = vertexIdManager.getNextId(this);
final Set<String> labels = ElementHelper.getLabelsValue(keyValues).orElse(
- Collections.singleton(Vertex.DEFAULT_LABEL));
+ Collections.singleton(defaultVertexLabel));
this.tx().readWrite();
final long txNumber = transaction.getTxNumber();
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java
index f3a97beb13..c605841fd0 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java
@@ -30,6 +30,7 @@ import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -79,24 +80,41 @@ public class TinkerVertex extends TinkerElement implements Vertex {
* Canonical constructor. Constructs a TinkerVertex with multiple labels and a specific version (for transactional graphs).
*/
protected TinkerVertex(final Object id, final Set<String> labels, final AbstractTinkerGraph graph, final long currentVersion) {
- super(id, (labels == null || labels.isEmpty()) ? Vertex.DEFAULT_LABEL : labels.iterator().next(), currentVersion);
+ super(id, null, currentVersion); // label field set below
this.graph = graph;
this.isTxMode = graph instanceof TinkerTransactionGraph;
this.allowNullPropertyValues = graph.features().vertex().supportsNullPropertyValues();
- this.vertexLabels = (labels == null || labels.isEmpty())
- ? new LinkedHashSet<>(Collections.singleton(Vertex.DEFAULT_LABEL))
- : new LinkedHashSet<>(labels);
+ // Store only explicit labels — never store the default label physically
+ this.vertexLabels = (labels == null) ? new HashSet<>() : new HashSet<>(labels);
+ // Set the cached label field to the effective value (including virtual default)
+ this.label = effectiveLabel();
+ }
+
+ /**
+ * Computes the effective single label for the deprecated label() API and the cached field.
+ */
+ private String effectiveLabel() {
+ if (this.vertexLabels.isEmpty()) {
+ return this.graph.vertexLabelCardinality.supportsZeroLabels() ? null : this.graph.defaultVertexLabel;
+ }
+ return this.vertexLabels.iterator().next();
}
@Override
public Set<String> labels() {
+ if (this.vertexLabels.isEmpty()) {
+ if (!this.graph.vertexLabelCardinality.supportsZeroLabels()) {
+ return Collections.singleton(this.graph.defaultVertexLabel);
+ }
+ return Collections.emptySet();
+ }
return Collections.unmodifiableSet(this.vertexLabels);
}
@Override
@Deprecated
public String label() {
- return this.vertexLabels.iterator().next();
+ return effectiveLabel();
}
@Override
@@ -106,36 +124,33 @@ public class TinkerVertex extends TinkerElement implements Vertex {
ElementHelper.validateLabel(l);
}
- // Remove default label if it was the only label and we're adding real labels
- if (this.vertexLabels.size() == 1 && this.vertexLabels.contains(Vertex.DEFAULT_LABEL)) {
- this.vertexLabels.remove(Vertex.DEFAULT_LABEL);
- }
+ this.graph.vertexLabelCardinality.validateAdd(this.vertexLabels, label, labels);
this.vertexLabels.add(label);
Collections.addAll(this.vertexLabels, labels);
- this.label = this.vertexLabels.iterator().next();
+ this.label = effectiveLabel();
this.graph.updateVertexLabelIndex(this);
}
@Override
public void dropLabels() {
+ this.graph.vertexLabelCardinality.validateDropAll(this.vertexLabels);
+
this.vertexLabels.clear();
- this.vertexLabels.add(Vertex.DEFAULT_LABEL);
- this.label = this.vertexLabels.iterator().next();
+ this.label = effectiveLabel();
this.graph.updateVertexLabelIndex(this);
}
@Override
public void dropLabel(final String label, final String... labels) {
+ this.graph.vertexLabelCardinality.validateDrop(this.vertexLabels, label);
+
this.vertexLabels.remove(label);
for (final String l : labels) {
this.vertexLabels.remove(l);
}
- if (this.vertexLabels.isEmpty()) {
- this.vertexLabels.add(Vertex.DEFAULT_LABEL);
- }
- this.label = this.vertexLabels.iterator().next();
+ this.label = effectiveLabel();
this.graph.updateVertexLabelIndex(this);
}
@@ -267,6 +282,24 @@ public class TinkerVertex extends TinkerElement implements Vertex {
final List<Edge> edges = new ArrayList<>();
this.edges(Direction.BOTH).forEachRemaining(edge -> edges.add(edge));
edges.stream().filter(edge -> !((TinkerEdge) edge).removed).forEach(Edge::remove);
+
+ // Also remove any orphaned edges from the global edges map that reference this vertex.
+ // Edges can become orphaned from the adjacency maps when their labels are dropped via
+ // dropLabels()/dropLabel(), which removes them from the vertex's outEdges/inEdges but
+ // leaves them in the graph's global edges map.
+ final Object thisId = this.id();
+ final List<Object> orphanedEdgeIds = new ArrayList<>();
+ graph.edges().forEachRemaining(edge -> {
+ if (!((TinkerEdge) edge).removed) {
+ if (edge.outVertex().id().equals(thisId) || edge.inVertex().id().equals(thisId)) {
+ orphanedEdgeIds.add(edge.id());
+ }
+ }
+ });
+ for (final Object edgeId : orphanedEdgeIds) {
+ graph.removeEdge(edgeId);
+ }
+
TinkerIndexHelper.removeElementIndex(this);
if (null != this.properties)
this.properties.values().forEach(vpList -> vpList.forEach(vp -> graph.vertexProperties.remove(vp.id())));
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/MergeVMultiLabelTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/MergeVMultiLabelTest.java
index 2257a15b60..d1f39a8265 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/MergeVMultiLabelTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/MergeVMultiLabelTest.java
@@ -147,8 +147,7 @@ public class MergeVMultiLabelTest {
g.mergeV(Map.of(T.label, "person", "name", "marko"))
.option(Merge.onMatch, Map.of(T.label, Collections.emptySet())).next();
- // empty collection triggers default label behavior
- assertThat(v.labels(), hasSize(1));
- assertThat(v.labels(), containsInAnyOrder(Vertex.DEFAULT_LABEL));
+ // Under ZERO_OR_MORE cardinality, empty collection means no labels
+ assertThat(v.labels(), hasSize(0));
}
}
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/LabelMutationPropertyTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/LabelMutationPropertyTest.java
index 5aeea9daa8..71ffc0ce94 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/LabelMutationPropertyTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/LabelMutationPropertyTest.java
@@ -133,10 +133,9 @@ public class LabelMutationPropertyTest {
// drop all labels
g.V(v).dropLabels().iterate();
- assertThat("Iteration " + i + ": after dropLabels(), vertex should have exactly one label",
- v.labels(), hasSize(1));
- assertThat("Iteration " + i + ": after dropLabels(), vertex should have default label",
- v.labels(), containsInAnyOrder(Vertex.DEFAULT_LABEL));
+ // Under ZERO_OR_MORE cardinality, dropping all labels results in empty set
+ assertThat("Iteration " + i + ": after dropLabels(), vertex should have no labels",
+ v.labels(), hasSize(0));
}
}
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/LabelMutationStepTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/LabelMutationStepTest.java
index 7ac5c88015..d9c00fbfc6 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/LabelMutationStepTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/LabelMutationStepTest.java
@@ -78,14 +78,13 @@ public class LabelMutationStepTest {
assertThat(v.labels(), containsInAnyOrder("person", "manager"));
}
- @Test
+ @Test(expected = IllegalStateException.class)
public void shouldAddLabelToEdgeViaTraversal() {
final Vertex v1 = g.addV("person").next();
final Vertex v2 = g.addV("person").next();
final Edge e = v1.addEdge("knows", v2);
+ // Under ZERO_OR_ONE cardinality for edges, adding a second label throws
g.E().addLabel("friend").iterate();
- assertThat(e.labels(), hasSize(2));
- assertThat(e.labels(), containsInAnyOrder("knows", "friend"));
}
// --- dropLabel step tests ---
@@ -102,8 +101,8 @@ public class LabelMutationStepTest {
public void shouldDropAllLabelsViaTraversal() {
final Vertex v = g.addV("person").addLabel("employee").next();
g.V(v).dropLabels().iterate();
- assertThat(v.labels(), hasSize(1));
- assertThat(v.labels(), containsInAnyOrder(Vertex.DEFAULT_LABEL));
+ // Under ZERO_OR_MORE cardinality, dropping all labels results in empty set
+ assertThat(v.labels(), hasSize(0));
}
@Test
@@ -120,8 +119,8 @@ public class LabelMutationStepTest {
final Vertex v2 = g.addV("person").next();
final Edge e = v1.addEdge("knows", v2);
g.E().dropLabels().iterate();
- assertThat(e.labels(), hasSize(1));
- assertThat(e.labels(), containsInAnyOrder(Edge.DEFAULT_LABEL));
+ // Under ZERO_OR_ONE cardinality with supportsZeroLabels, edge labels become empty
+ assertThat(e.labels(), hasSize(0));
}
// --- addV multi-label tests ---
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertexMultiLabelTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertexMultiLabelTest.java
index 27387d60e9..ffd5efb697 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertexMultiLabelTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertexMultiLabelTest.java
@@ -119,8 +119,8 @@ public class TinkerVertexMultiLabelTest {
public void shouldDropAllLabelsAndAssignDefault() {
final Vertex v = g.addV("person").addLabel("employee").next();
v.dropLabels();
- assertThat(v.labels(), hasSize(1));
- assertThat(v.labels(), containsInAnyOrder(Vertex.DEFAULT_LABEL));
+ // Under ZERO_OR_MORE cardinality, dropping all labels results in empty set (no virtual default)
+ assertThat(v.labels(), hasSize(0));
}
@Test
@@ -143,18 +143,17 @@ public class TinkerVertexMultiLabelTest {
public void shouldAssignDefaultWhenDroppingLastSpecificLabel() {
final Vertex v = g.addV("person").next();
v.dropLabel("person");
- assertThat(v.labels(), hasSize(1));
- assertThat(v.labels(), containsInAnyOrder(Vertex.DEFAULT_LABEL));
+ // Under ZERO_OR_MORE cardinality, dropping last label results in empty set (no virtual default)
+ assertThat(v.labels(), hasSize(0));
}
- @Test
+ @Test(expected = IllegalStateException.class)
public void shouldAddLabelToEdge() {
final Vertex v1 = g.addV("person").next();
final Vertex v2 = g.addV("person").next();
final Edge e = v1.addEdge("knows", v2);
+ // Under ZERO_OR_ONE cardinality for edges, adding a second label throws
e.addLabel("friend");
- assertThat(e.labels(), hasSize(2));
- assertThat(e.labels(), containsInAnyOrder("knows", "friend"));
}
@Test
@@ -163,8 +162,8 @@ public class TinkerVertexMultiLabelTest {
final Vertex v2 = g.addV("person").next();
final Edge e = v1.addEdge("knows", v2);
e.dropLabels();
- assertThat(e.labels(), hasSize(1));
- assertThat(e.labels(), containsInAnyOrder(Edge.DEFAULT_LABEL));
+ // Under ZERO_OR_ONE cardinality for edges with supportsZeroLabels, labels() returns empty set
+ assertThat(e.labels(), hasSize(0));
}
@Test
@@ -177,12 +176,13 @@ public class TinkerVertexMultiLabelTest {
}
@Test
- public void shouldRemoveDefaultLabelWhenAddingFirstRealLabel() {
+ public void shouldAddLabelToExistingVertexWithDefaultLabel() {
+ // Under ZERO_OR_MORE, addV() assigns "vertex" physically. Adding "person" simply adds to the set.
final Vertex v = g.addV().next();
assertThat(v.labels(), containsInAnyOrder(Vertex.DEFAULT_LABEL));
v.addLabel("person");
- assertThat(v.labels(), hasSize(1));
- assertThat(v.labels(), containsInAnyOrder("person"));
+ assertThat(v.labels(), hasSize(2));
+ assertThat(v.labels(), containsInAnyOrder(Vertex.DEFAULT_LABEL, "person"));
}
@Test