You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2022/01/14 12:03:01 UTC
[tinkerpop] branch master updated: Squashed all commits and merged from master
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/master by this push:
new 33b0daa Squashed all commits and merged from master
new 4a1d59d Merge pull request #1546 from bechbd/TINKERPOP-2665-squashed
33b0daa is described below
commit 33b0daac89c4dc504d10ec05dab25b2de152b316
Author: Dave Bechberger <db...@amazon.com>
AuthorDate: Tue Jan 11 11:26:13 2022 -0900
Squashed all commits and merged from master
---
CHANGELOG.asciidoc | 1 +
docs/src/reference/the-traversal.asciidoc | 18 +++++---
docs/src/upgrade/release-3.6.x.asciidoc | 15 +++++++
.../language/grammar/GenericLiteralVisitor.java | 4 +-
.../language/grammar/GremlinBaseVisitor.java | 8 ++++
.../language/grammar/TraversalMethodVisitor.java | 19 ++++++++
.../traversal/dsl/graph/GraphTraversal.java | 50 +++++++++++++++++++---
.../gremlin/process/traversal/dsl/graph/__.java | 7 +++
.../grammar/TraversalMethodVisitorTest.java | 28 ++++++++++++
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 2 +
.../gremlin-javascript/test/cucumber/gremlin.js | 2 +
gremlin-language/src/main/antlr4/Gremlin.g4 | 2 +
gremlin-python/src/main/python/radish/gremlin.py | 2 +
gremlin-test/features/map/AddVertex.feature | 22 ++++++++++
14 files changed, 165 insertions(+), 15 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 03b36e4..6544cbe 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -54,6 +54,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Changed `NumberHelper` to properly cast to `byte` and `short` rather than default coercing to `Integer`.
* Modified some driver defaults (maximum content length, pool size, maximum in process) to be more consistent with one another.
* Fixed a potential connection load balancing issue due to a race condition not updating the usage count.
+* Extended property() to allow for setting a map of property values.
== TinkerPop 3.5.0 (The Sleeping Gremlin: No. 18 Entr'acte Symphonique)
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 98f2e8d..d9cf239 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -598,15 +598,21 @@ previous step to enable vertex and edge creation with all its properties in one
----
g.V(1).property('country','usa')
g.V(1).property('city','santa fe').property('state','new mexico').valueMap()
-g.V(1).property(list,'age',35) <1>
+g.V(1).property(['city': 'santa fe', 'state': 'new mexico']) <1>
+g.V(1).property(list,'age',35) <2>
+g.V(1).property(list, ['city': 'santa fe', 'state': 'new mexico']) <3>
g.V(1).valueMap()
-g.V(1).property('friendWeight',outE('knows').values('weight').sum(),'acl','private') <2>
-g.V(1).properties('friendWeight').valueMap() <3>
+g.V(1).property('friendWeight',outE('knows').values('weight').sum(),'acl','private') <4>
+g.V(1).properties('friendWeight').valueMap() <5>
+g.addV().property(T.label,'person').valueMap().with(WithOptions.tokens) <6>
----
-<1> For vertices, a cardinality can be provided for <<vertex-properties,vertex properties>>.
-<2> It is possible to select the property value (as well as key) via a traversal.
-<3> For vertices, the `property()`-step can add meta-properties.
+<1> Properties can also take a Map as an argument.
+<2> For vertices, a cardinality can be provided for <<vertex-properties,vertex properties>>.
+<3> If a cardinality is specified for a Map then that cardinality will be used for all properties in the map. If you need different cardinalities per property then you should individually add the property values.
+<4> It is possible to select the property value (as well as key) via a traversal.
+<5> For vertices, the `property()`-step can add meta-properties.
+<6> The label value can be specified as a property only at the time a vertex is added and if one is not specified in the addV()
*Additional References*
diff --git a/docs/src/upgrade/release-3.6.x.asciidoc b/docs/src/upgrade/release-3.6.x.asciidoc
index c848940..284f31d 100644
--- a/docs/src/upgrade/release-3.6.x.asciidoc
+++ b/docs/src/upgrade/release-3.6.x.asciidoc
@@ -549,6 +549,21 @@ link:https://tinkerpop.apache.org/docs/3.6.0/reference/#gremlin-python-differenc
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2650[TINKERPOP-2650]
+==== `property()` accepts a Map as an argument
+
+The `property()` step has been extended to take a `Map` of property key/value pairs as an argument with two new signatures:
+```
+property(Map)
+property(Cardinality, Map)
+```
+When called, each individual key/value pair in the map is saved as a property to the element.
+When the cardinality is specified, that cardinality will be applied to all elements in the map as they are saved to the element.
+
+If users need different cardinalities per property, then please use the existing pattern of stringing multiple property() calls together.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2665[TINKERPOP-2665]
+
+
=== Upgrading for Providers
==== Graph System Providers
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
index f71bcf7..336eecb 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
@@ -31,7 +31,7 @@ import org.apache.tinkerpop.gremlin.util.DatetimeHelper;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
@@ -277,7 +277,7 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
*/
@Override
public Object visitGenericLiteralMap(final GremlinParser.GenericLiteralMapContext ctx) {
- final HashMap<Object, Object> literalMap = new HashMap<>();
+ final LinkedHashMap<Object, Object> literalMap = new LinkedHashMap<>();
int childIndex = 1;
while (childIndex < ctx.getChildCount() && ctx.getChildCount() > 3) {
Object key = visitGenericLiteral((GremlinParser.GenericLiteralContext) ctx.getChild(childIndex));
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
index fd48b79..18f42f1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
@@ -679,6 +679,14 @@ public class GremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> implement
/**
* {@inheritDoc}
*/
+ @Override public T visitTraversalMethod_property_Cardinality_Object(final GremlinParser.TraversalMethod_property_Cardinality_ObjectContext ctx) { notImplemented(ctx); return null; }
+ /**
+ * {@inheritDoc}
+ */
+ @Override public T visitTraversalMethod_property_Object(final GremlinParser.TraversalMethod_property_ObjectContext ctx) { notImplemented(ctx); return null; }
+ /**
+ * {@inheritDoc}
+ */
@Override public T visitTraversalMethod_propertyMap(final GremlinParser.TraversalMethod_propertyMapContext 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 fac55d6..e866598 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
@@ -28,8 +28,11 @@ import org.apache.tinkerpop.gremlin.structure.Column;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality;
+import java.util.LinkedHashMap;
import java.util.Map;
+
import java.util.function.BiFunction;
import static org.apache.tinkerpop.gremlin.process.traversal.SackFunctions.Barrier.normSack;
@@ -1149,6 +1152,22 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
* {@inheritDoc}
*/
@Override
+ public Traversal visitTraversalMethod_property_Cardinality_Object(final GremlinParser.TraversalMethod_property_Cardinality_ObjectContext ctx) {
+ return graphTraversal.property(Cardinality.list, new GenericLiteralVisitor(antlr).visitGenericLiteralMap(ctx.genericLiteralMap()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Traversal visitTraversalMethod_property_Object(final GremlinParser.TraversalMethod_property_ObjectContext ctx) {
+ return graphTraversal.property((LinkedHashMap<Object, Object>) new GenericLiteralVisitor(antlr).visitGenericLiteralMap(ctx.genericLiteralMap()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public GraphTraversal visitTraversalMethod_range_Scope_long_long(final GremlinParser.TraversalMethod_range_Scope_long_longContext ctx) {
return graphTraversal.range(TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.getChild(2)),
Integer.valueOf(ctx.integerLiteral(0).getText()),
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
index 95927d6..376ed6b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
@@ -166,6 +166,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -2272,7 +2273,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
* and if the {@link Element} is a {@link VertexProperty}. This method is the long-hand version of
* {@link #property(Object, Object, Object...)} with the difference that the {@link VertexProperty.Cardinality}
* can be supplied.
- * <p/>
+ * <p/>*
* Generally speaking, this method will append an {@link AddPropertyStep} to the {@link Traversal} but when
* possible, this method will attempt to fold key/value pairs into an {@link AddVertexStep}, {@link AddEdgeStep} or
* {@link AddVertexStartStep}. This potential optimization can only happen if cardinality is not supplied
@@ -2353,6 +2354,10 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
* {@link VertexProperty.Cardinality} is defaulted to {@code null} which means that the default cardinality for
* the {@link Graph} will be used.
* <p/>
+ * If a {@link Map} is supplied then each of the key/value pairs in the map will
+ * be added as property. This method is the long-hand version of looping through the
+ * {@link #property(Object, Object, Object...)} method for each key/value pair supplied.
+ * <p />
* This method is effectively calls {@link #property(VertexProperty.Cardinality, Object, Object, Object...)}
* as {@code property(null, key, value, keyValues}.
*
@@ -2364,14 +2369,45 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
* @since 3.0.0-incubating
*/
public default GraphTraversal<S, E> property(final Object key, final Object value, final Object... keyValues) {
- return key instanceof VertexProperty.Cardinality ?
- this.property((VertexProperty.Cardinality) key, value, null == keyValues ? null : keyValues[0],
+ if (key instanceof VertexProperty.Cardinality) {
+ if (value instanceof Map) { //Handle the property(Cardinality, Map) signature
+ final Map<Object, Object> map = (Map)value;
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ property(key, entry.getKey(), entry.getValue());
+ }
+ return this;
+ } else {
+ return this.property((VertexProperty.Cardinality) key, value, null == keyValues ? null : keyValues[0],
keyValues != null && keyValues.length > 1 ?
Arrays.copyOfRange(keyValues, 1, keyValues.length) :
- new Object[]{}) :
- this.property(null, key, value, keyValues);
+ new Object[]{});
+ }
+ } else { //handles if cardinality is not the first parameter
+ return this.property(null, key, value, keyValues);
+ }
+ }
+
+ /**
+ * When a {@link Map} is supplied then each of the key/value pairs in the map will
+ * be added as property. This method is the long-hand version of looping through the
+ * {@link #property(Object, Object, Object...)} method for each key/value pair supplied.
+ * <p/>
+ * If a {@link Map} is not supplied then an exception is thrown.
+ * <p />
+ * This method is effectively calls {@link #property(VertexProperty.Cardinality, Object, Object, Object...)}
+ * as {@code property(null, key, value, keyValues}.
+ *
+ * @param value the value for the property
+ * @return the traversal with the last step modified to add a property
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addproperty-step" target="_blank">AddProperty Step</a>
+ * @since 3.0.0-incubating
+ */
+ public default GraphTraversal<S, E> property(final Map<Object, Object> value) {
+ for (Map.Entry<Object, Object> entry : value.entrySet()) {
+ property(null, entry.getKey(), entry.getValue());
+ }
+ return this;
}
-
///////////////////// BRANCH STEPS /////////////////////
/**
@@ -3219,4 +3255,4 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
public static final String option = "option";
}
-}
+}
\ No newline at end of file
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
index 08a4c07..0502ed0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
@@ -983,6 +983,13 @@ public class __ {
return __.<A>start().property(cardinality, key, value, keyValues);
}
+ /**
+ * @see GraphTraversal#property(Map)
+ */
+ public static <A> GraphTraversal<A, A> property(final Map<Object, Object> value) {
+ return __.<A>start().property(value);
+ }
+
///////////////////// BRANCH STEPS /////////////////////
/**
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java
index 6bad0e9..e3f6578 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitorTest.java
@@ -34,10 +34,14 @@ import org.apache.tinkerpop.gremlin.structure.Column;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
import org.junit.Before;
import org.junit.Test;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.function.Function;
import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
@@ -697,6 +701,30 @@ public class TraversalMethodVisitorTest {
}
@Test
+ public void testTraversalMethod_property_Object() throws Exception {
+ LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
+ map.put("key", "foo");
+ map.put("key1", "bar");
+ compare(g.V().property(map), eval("g.V().property(['key': 'foo', 'key1': 'bar'])"));
+ map.clear();
+ map.put("name", "foo");
+ map.put("age", 42);
+ compare(g.addV().property(map), eval("g.addV().property([\"name\": \"foo\", \"age\": 42 ])"));
+ map.clear();
+ map.put(label, "foo");
+ map.put("age", 42);
+ compare(g.addV().property(map), eval("g.addV().property([T.label: \"foo\", \"age\": 42 ])"));
+ }
+
+ @Test
+ public void testTraversalMethod_property_Cardinality_Object() throws Exception {
+ final LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
+ map.put("key", "foo");
+ map.put("key1", "bar");
+ compare(g.V().property(Cardinality.list, map), eval("g.V().property(list, ['key': 'foo', 'key1': 'bar'])"));
+ }
+
+ @Test
public void testTraversalMethod_propertyMap() throws Exception {
compare(g.V().propertyMap("venus", "mars"), eval("g.V().propertyMap('venus', 'mars')"));
}
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index ea6bf4d..332bbe4 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -369,6 +369,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMap_withXtokensX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>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( [...]
{"g_addVXV_hasXname_markoX_propertiesXnameX_keyX_label", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>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").Pro [...]
{"g_addV_propertyXlabel_personX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().Property(T.Label,"person"), (g,p) =>g.V().HasLabel("person")}},
+ {"g_addV_propertyXmapX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().Property("name","foo").Property("age",42), (g,p) =>g.V().Has("name","foo")}},
+ {"g_addV_propertyXsingle_mapX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().Property(Cardinality.Single,"name","foo").Property(Cardinality.Single,"age",42), (g,p) =>g.V().Has("name","foo")}},
{"g_addVXpersonX_propertyXname_joshX_propertyXage_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","josh").Property("age",null), (g,p) =>g.V().Has("person","age",(object) null)}},
{"g_addVXpersonX_propertyXname_markoX_propertyXfriendWeight_null_acl_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("friendWeight",null,"acl",null), (g,p) =>g.V().Has("person","name","marko").Has("friendWeight",(object) null), (g,p) =>g.V().Has("person","name","marko").Properties<object>("friendWeight").Has("acl",(object) null), (g,p) =>g.V().Has("person","name","marko").Prop [...]
{"g_V_coalesceXoutXfooX_outXbarXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Coalesce<object>(__.Out("foo"),__.Out("bar"))}},
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 52bb2c7..3557f44 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -357,6 +357,8 @@ const gremlins = {
g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMap_withXtokensX: [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("lan [...]
g_addVXV_hasXname_markoX_propertiesXnameX_keyX_label: [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").pro [...]
g_addV_propertyXlabel_personX: [function({g}) { return g.addV().property(T.label,"person") }, function({g}) { return g.V().hasLabel("person") }],
+ g_addV_propertyXmapX: [function({g}) { return g.addV().property("name","foo").property("age",42) }, function({g}) { return g.V().has("name","foo") }],
+ g_addV_propertyXsingle_mapX: [function({g}) { return g.addV().property(Cardinality.single,"name","foo").property(Cardinality.single,"age",42) }, function({g}) { return g.V().has("name","foo") }],
g_addVXpersonX_propertyXname_joshX_propertyXage_nullX: [function({g}) { return g.addV("person").property("name","josh").property("age",null) }, function({g}) { return g.V().has("person","age",null) }],
g_addVXpersonX_propertyXname_markoX_propertyXfriendWeight_null_acl_nullX: [function({g}) { return g.addV("person").property("name","marko").property("friendWeight",null,"acl",null) }, function({g}) { return g.V().has("person","name","marko").has("friendWeight",null) }, function({g}) { return g.V().has("person","name","marko").properties("friendWeight").has("acl",null) }, function({g}) { return g.V().has("person","name","marko").properties("friendWeight").count() }],
g_V_coalesceXoutXfooX_outXbarXX: [function({g}) { return g.V().coalesce(__.out("foo"),__.out("bar")) }],
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4
index e285634..78d8928 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -593,6 +593,8 @@ traversalMethod_properties
traversalMethod_property
: 'property' LPAREN traversalCardinality COMMA genericLiteral COMMA genericLiteral (COMMA genericLiteralList)? RPAREN #traversalMethod_property_Cardinality_Object_Object_Object
| 'property' LPAREN genericLiteral COMMA genericLiteral (COMMA genericLiteralList)? RPAREN #traversalMethod_property_Object_Object_Object
+ | 'property' LPAREN genericLiteralMap RPAREN # traversalMethod_property_Object
+ | 'property' LPAREN traversalCardinality COMMA genericLiteralMap RPAREN # traversalMethod_property_Cardinality_Object
;
traversalMethod_propertyMap
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 850ee7e..4c3d27c 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -342,6 +342,8 @@ world.gremlins = {
'g_V_asXaX_hasXname_markoX_outXcreatedX_asXbX_addVXselectXaX_labelX_propertyXtest_selectXbX_labelX_valueMap_withXtokensX': [(lambda g: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','jav [...]
'g_addVXV_hasXname_markoX_propertiesXnameX_keyX_label': [(lambda g: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( [...]
'g_addV_propertyXlabel_personX': [(lambda g:g.addV().property(T.label,'person')), (lambda g:g.V().hasLabel('person'))],
+ 'g_addV_propertyXmapX': [(lambda g:g.addV().property('name','foo').property('age',42)), (lambda g:g.V().has('name','foo'))],
+ 'g_addV_propertyXsingle_mapX': [(lambda g:g.addV().property(Cardinality.single,'name','foo').property(Cardinality.single,'age',42)), (lambda g:g.V().has('name','foo'))],
'g_addVXpersonX_propertyXname_joshX_propertyXage_nullX': [(lambda g:g.addV('person').property('name','josh').property('age',None)), (lambda g:g.V().has('person','age',None))],
'g_addVXpersonX_propertyXname_markoX_propertyXfriendWeight_null_acl_nullX': [(lambda g:g.addV('person').property('name','marko').property('friendWeight',None,'acl',None)), (lambda g:g.V().has('person','name','marko').has('friendWeight',None)), (lambda g:g.V().has('person','name','marko').properties('friendWeight').has('acl',None)), (lambda g:g.V().has('person','name','marko').properties('friendWeight').count())],
'g_V_coalesceXoutXfooX_outXbarXX': [(lambda g:g.V().coalesce(__.out('foo'),__.out('bar')))],
diff --git a/gremlin-test/features/map/AddVertex.feature b/gremlin-test/features/map/AddVertex.feature
index a47369e..a326fc4 100644
--- a/gremlin-test/features/map/AddVertex.feature
+++ b/gremlin-test/features/map/AddVertex.feature
@@ -429,6 +429,28 @@ Feature: Step - addV()
Then the result should have a count of 1
And the graph should return 1 for count of "g.V().hasLabel(\"person\")"
+
+ Scenario: g_addV_propertyXmapX
+ Given the empty graph
+ And the traversal of
+ """
+ g.addV().property(["name": "foo", "age": 42 ])
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"name\",\"foo\")"
+
+ Scenario: g_addV_propertyXsingle_mapX
+ Given the empty graph
+ And the traversal of
+ """
+ g.addV().property(Cardinality.single, ["name": "foo", "age": 42 ])
+ """
+ When iterated to list
+ Then the result should have a count of 1
+ And the graph should return 1 for count of "g.V().has(\"name\",\"foo\")"
+
+
@AllowNullPropertyValues
Scenario: g_addVXpersonX_propertyXname_joshX_propertyXage_nullX
Given the empty graph