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