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 2015/09/09 14:28:24 UTC

incubator-tinkerpop git commit: Improve support around cardinality on calls to GraphTraversal.property()

Repository: incubator-tinkerpop
Updated Branches:
  refs/heads/TINKERPOP3-333 d86b28560 -> 9aad7b26c


Improve support around cardinality on calls to GraphTraversal.property()

This change was done in relation to PartitionStrategy.  The big change is that if the cardinality is specified to property() then the property will not be folded into an AddVertex/Start/EdgeStep.  This lets the cardinality continue to be respected.  The alternative to this approach would be to push Cardinality down to the Parameters class, but that was much more involved.  The cost here is a possible optimization that a graph might be able to take advantage of with all mutations held under the AddVertex/Start/EdgeStep.  Added tests to better cover cardinality calls and tests that cover VertexProperty related tests on PartitionStrategy.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/9aad7b26
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/9aad7b26
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/9aad7b26

Branch: refs/heads/TINKERPOP3-333
Commit: 9aad7b26c9033cd88071724994c494ecd9b68b62
Parents: d86b285
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Wed Sep 9 08:19:24 2015 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Wed Sep 9 08:19:24 2015 -0400

----------------------------------------------------------------------
 .../traversal/dsl/graph/GraphTraversal.java     | 37 +++++++++-
 .../process/traversal/step/filter/DropStep.java |  5 +-
 .../strategy/decoration/PartitionStrategy.java  | 16 +++--
 .../PartitionStrategyTraverseTest.java          | 72 ++++++++++++--------
 .../step/map/GroovyAddVertexTest.groovy         | 22 ++++--
 .../traversal/step/map/AddVertexTest.java       | 72 ++++++++++++++++++++
 .../decoration/EventStrategyProcessTest.java    |  4 +-
 .../PartitionStrategyProcessTest.java           | 20 +++++-
 8 files changed, 203 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/9aad7b26/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
----------------------------------------------------------------------
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 70f61a3..b5e8e9c 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
@@ -987,9 +987,29 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new ProfileStep<>(this.asAdmin()));
     }
 
+    /**
+     * Sets a {@link Property} value and related meta properties if supplied, if supported by the {@link Graph}
+     * 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/>
+     * 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
+     * and when meta-properties are not included.
+     *
+     * @param cardinality the specified cardinality of the property where {@code null} will allow the {@link Graph}
+     *                    to use its default settings
+     * @param key the key for the property
+     * @param value the value for the property
+     * @param keyValues any meta properties to be assigned to this property
+     */
     public default GraphTraversal<S, E> property(final VertexProperty.Cardinality cardinality, final Object key, final Object value, final Object... keyValues) {
-        if ((this.asAdmin().getEndStep() instanceof AddVertexStep || this.asAdmin().getEndStep() instanceof AddEdgeStep || this.asAdmin().getEndStep() instanceof AddVertexStartStep) && keyValues.length == 0) {
-            ((Mutating) this.asAdmin().getEndStep()).addPropertyMutations(new Object[]{key, value});
+        // if it can be detected that this call to property() is related to an addV/E() then we can attempt to fold
+        // the properties into that step to gain an optimization for those graphs that support such capabilities.
+        if ((this.asAdmin().getEndStep() instanceof AddVertexStep || this.asAdmin().getEndStep() instanceof AddEdgeStep
+                || this.asAdmin().getEndStep() instanceof AddVertexStartStep) && keyValues.length == 0 && null == cardinality) {
+            ((Mutating) this.asAdmin().getEndStep()).addPropertyMutations(key, value);
         } else {
             this.asAdmin().addStep(new AddPropertyStep(this.asAdmin(), cardinality, key, value));
             ((AddPropertyStep) this.asAdmin().getEndStep()).addPropertyMutations(keyValues);
@@ -997,6 +1017,19 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this;
     }
 
+    /**
+     * Sets the key and value of a {@link Property}. If the {@link Element} is a {@link VertexProperty} and the
+     * {@link Graph} supports it, meta properties can be set.  Use of this method assumes that the
+     * {@link VertexProperty.Cardinality} is defaulted to {@code null} which means that the default cardinality
+     * for the {@link Graph} will be used.
+     * <p/>
+     * This method is effectively calls {@link #property(VertexProperty.Cardinality, Object, Object, Object...)} as
+     * {@code property(null, key, value, keyValues}.
+     *
+     * @param key the key for the property
+     * @param value the value for the property
+     * @param keyValues any meta properties to be assigned to this property
+     */
     public default GraphTraversal<S, E> property(final Object key, final Object value, final Object... keyValues) {
         return this.property(null, key, value, keyValues);
     }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/9aad7b26/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
index cb9272c..9c4ae2f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
@@ -88,7 +88,10 @@ public final class DropStep<S> extends FilterStep<S> implements Mutating<Event>
         return callbackRegistry;
     }
 
+    /**
+     * This method doesn't do anything as {@code drop()} doesn't take property mutation arguments.
+     */
     public void addPropertyMutations(final Object... keyValues) {
-
+        // do nothing
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/9aad7b26/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
index 4ba0b95..aaabddc 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
@@ -108,8 +108,9 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal
 
     @Override
     public void apply(final Traversal.Admin<?, ?> traversal) {
-        final boolean supportsMetaProperties = traversal.getGraph().isPresent()
-                && traversal.getGraph().get().features().vertex().supportsMetaProperties();
+        final Graph graph = traversal.getGraph().orElseThrow(() -> new IllegalStateException("PartitionStrategy does not work with anonymous Traversals"));
+        final Graph.Features.VertexFeatures vertexFeatures = graph.features().vertex();
+        final boolean supportsMetaProperties = vertexFeatures.supportsMetaProperties();
         if (includeMetaProperties && !supportsMetaProperties)
             throw new IllegalStateException("PartitionStrategy is configured to include meta-properties but the Graph does not support them");
 
@@ -202,20 +203,25 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal
 
             if (includeMetaProperties) {
                 // GraphTraversal folds g.addV().property('k','v') to just AddVertexStep/AddVertexStartStep so this
-                // has to be exploded back to g.addV().property('k','v','partition','A')
+                // has to be exploded back to g.addV().property(cardinality, 'k','v','partition','A')
                 if (step instanceof AddVertexStartStep || step instanceof AddVertexStep) {
                     final Parameters parameters = ((Parameterizing) step).getParameters();
                     final Map<Object, List<Object>> params = parameters.getRaw();
                     params.forEach((k, v) -> {
+                        final List<Step> addPropertyStepsToAppend = new ArrayList<>(v.size());
+                        final VertexProperty.Cardinality cardinality = vertexFeatures.getCardinality((String) k);
                         v.forEach(o -> {
-                            final AddPropertyStep addPropertyStep = new AddPropertyStep(traversal, null, k, o);
+                            final AddPropertyStep addPropertyStep = new AddPropertyStep(traversal, cardinality, k, o);
                             addPropertyStep.addPropertyMutations(partitionKey, writePartition);
-                            TraversalHelper.insertAfterStep(addPropertyStep, step, traversal);
+                            addPropertyStepsToAppend.add(addPropertyStep);
 
                             // need to remove the parameter from the AddVertex/StartStep because it's now being added
                             // via the AddPropertyStep
                             parameters.remove(k);
                         });
+
+                        Collections.reverse(addPropertyStepsToAppend);
+                        addPropertyStepsToAppend.forEach(s -> TraversalHelper.insertAfterStep(s, step, traversal));
                     });
                 }
             }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/9aad7b26/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTraverseTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTraverseTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTraverseTest.java
index 13b6ca6..b2e075f 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTraverseTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyTraverseTest.java
@@ -21,7 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
 import org.apache.tinkerpop.gremlin.process.traversal.Contains;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
@@ -30,9 +30,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GraphStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 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.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -43,46 +45,40 @@ import java.util.List;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 @RunWith(Parameterized.class)
 public class PartitionStrategyTraverseTest {
-    private static Traversal traversalWithAddV;
-
-    static {
-        final Graph mockedGraph = mock(Graph.class);
-        final DefaultGraphTraversal t = new DefaultGraphTraversal<>(mockedGraph);
-        t.asAdmin().addStep(new GraphStep<>(t.asAdmin(), Vertex.class));
-        traversalWithAddV = t.addV();
-    }
 
     @Parameterized.Parameters(name = "{0}")
     public static Iterable<Object[]> data() {
         return Arrays.asList(new Object[][]{
-                {"bothV()", __.bothV(), 1, false},
-                {"inV()", __.inV(), 1, false},
-                {"outV()", __.outV(), 1, false},
-                {"in()", __.in(), 1, false},
-                {"in(args)", __.in("test"), 1, false},
-                {"both()", __.both(), 1, false},
-                {"both(args)", __.both("test"), 1, false},
-                {"out()", __.out(), 1, false},
-                {"out(args)", __.out("test"), 1, false},
-                {"out().inE().otherV", __.out().inE().otherV(), 3, false},
-                {"addV()", traversalWithAddV, 1, true},
-                {"addE(test).from(x)", __.addE("test").from("x"), 0, true},
-                {"addE(test).to(x)", __.addE("test").to("x"), 0, true},
-                {"addE(test).from(x).property(other,args)", __.addE("test").from("x").property("other", "args"), 0, true},
-                {"addE(test).to(x).property(other,args)", __.addE("test").to("x").property("other", "args"), 0, true},
-                {"in().out()", __.in().out(), 2, false},
-                {"in().out().addE(test).from(x)", __.in().out().addE("test").from("x"), 2, true},
-                {"in().out().addE(test).to(x)", __.in().out().addE("test").to("x"), 2, true},
-                {"out().out().out()", __.out().out().out(), 3, false},
-                {"in().out().in()", __.in().out().in(), 3, false},
-                {"inE().outV().inE().outV()", __.inE().outV().inE().outV(), 4, false}});
+                {"bothV()", create().bothV(), 1, false},
+                {"inV()", create().inV(), 1, false},
+                {"outV()", create().outV(), 1, false},
+                {"in()", create().in(), 1, false},
+                {"in(args)", create().in("test"), 1, false},
+                {"both()", create().both(), 1, false},
+                {"both(args)", create().both("test"), 1, false},
+                {"out()", create().out(), 1, false},
+                {"out(args)", create().out("test"), 1, false},
+                {"out().inE().otherV", create().out().inE().otherV(), 3, false},
+                {"addV()", create(Vertex.class).addV(), 1, true},
+                {"addE(test).from(x)", create().addE("test").from("x"), 0, true},
+                {"addE(test).to(x)", create().addE("test").to("x"), 0, true},
+                {"addE(test).from(x).property(other,args)", create().addE("test").from("x").property("other", "args"), 0, true},
+                {"addE(test).to(x).property(other,args)", create().addE("test").to("x").property("other", "args"), 0, true},
+                {"in().out()", create().in().out(), 2, false},
+                {"in().out().addE(test).from(x)", create().in().out().addE("test").from("x"), 2, true},
+                {"in().out().addE(test).to(x)", create().in().out().addE("test").to("x"), 2, true},
+                {"out().out().out()", create().out().out().out(), 3, false},
+                {"in().out().in()", create().in().out().in(), 3, false},
+                {"inE().outV().inE().outV()", create().inE().outV().inE().outV(), 4, false}});
     }
 
     @Parameterized.Parameter(value = 0)
@@ -140,4 +136,20 @@ public class PartitionStrategyTraverseTest {
             assertEquals(Contains.within, hasContainer.getBiPredicate());
         });
     }
+
+    public static GraphTraversal create() {
+        return create(null);
+    }
+
+    public static GraphTraversal create(final Class<? extends Element> clazz) {
+        final Graph mockedGraph = mock(Graph.class);
+        final Graph.Features features = mock(Graph.Features.class);
+        final Graph.Features.VertexFeatures vertexFeatures = mock(Graph.Features.VertexFeatures.class);
+        when(mockedGraph.features()).thenReturn(features);
+        when(features.vertex()).thenReturn(vertexFeatures);
+        when(vertexFeatures.getCardinality(any())).thenReturn(VertexProperty.Cardinality.single);
+        final DefaultGraphTraversal t = new DefaultGraphTraversal<>(mockedGraph);
+        if (clazz != null) t.asAdmin().addStep(new GraphStep<>(t.asAdmin(), clazz));
+        return t;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/9aad7b26/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddVertexTest.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddVertexTest.groovy b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddVertexTest.groovy
index b42f4ce..40092f1 100644
--- a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddVertexTest.groovy
+++ b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddVertexTest.groovy
@@ -31,14 +31,13 @@ public abstract class GroovyAddVertexTest {
     public static class Traversals extends AddVertexTest {
 
         @Override
-        public Traversal<Vertex, Vertex> get_g_VX1X_addVXanimalX_propertyXage_selectXaX_byXageXX_propertyXname_puppyX(
-                final Object v1Id) {
-            TraversalScriptHelper.compute("g.V(${v1Id}).as('a').addV('animal').property('age', select('a').by('age')).property('name', 'puppy')", g, "v1Id", v1Id);
+        public Traversal<Vertex, Vertex> get_g_VX1X_addVXanimalX_propertyXage_selectXaX_byXageXX_propertyXname_puppyX(final Object v1Id) {
+            TraversalScriptHelper.compute("g.V(v1Id).as('a').addV('animal').property('age', select('a').by('age')).property('name', 'puppy')", g, "v1Id", v1Id);
         }
 
         @Override
         public Traversal<Vertex, Vertex> get_g_V_addVXanimalX_propertyXage_0X() {
-            TraversalScriptHelper.compute("g.V.addV(label, 'animal', 'age', 0)", g)
+            TraversalScriptHelper.compute("g.V().addV('animal').property('age', 0)", g)
         }
 
         @Override
@@ -47,6 +46,21 @@ public abstract class GroovyAddVertexTest {
         }
 
         @Override
+        public Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXname_stephenX_propertyXname_stephenmX() {
+            TraversalScriptHelper.compute("g.addV('person').property('name', 'stephen').property('name', 'stephenm')", g)
+        }
+
+        @Override
+        public Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX() {
+            TraversalScriptHelper.compute("g.addV('person').property(VertexProperty.Cardinality.single, 'name', 'stephen').property(VertexProperty.Cardinality.single, 'name', 'stephenm')", g)
+        }
+
+        @Override
+        public Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenm_since_2010X() {
+            TraversalScriptHelper.compute("g.addV('person').property(VertexProperty.Cardinality.single, 'name', 'stephen').property(VertexProperty.Cardinality.single, 'name', 'stephenm', 'since', 2010)", g)
+        }
+
+        @Override
         public Traversal<Vertex, Vertex> get_g_V_hasXname_markoX_propertyXfriendWeight_outEXknowsX_weight_sum__acl_privateX() {
             TraversalScriptHelper.compute("g.V.has('name', 'marko').property('friendWeight', outE('knows').weight.sum(), 'acl', 'private')", g)
         }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/9aad7b26/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java
index c6ede24..3b2f8b5 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java
@@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -34,6 +35,7 @@ import org.junit.runner.RunWith;
 import java.util.List;
 
 import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.junit.Assert.*;
 
 /**
@@ -48,6 +50,12 @@ public abstract class AddVertexTest extends AbstractGremlinTest {
 
     public abstract Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXname_stephenX();
 
+    public abstract Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXname_stephenX_propertyXname_stephenmX();
+
+    public abstract Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX();
+
+    public abstract Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenm_since_2010X();
+
     public abstract Traversal<Vertex, Vertex> get_g_V_hasXname_markoX_propertyXfriendWeight_outEXknowsX_weight_sum__acl_privateX();
 
     public abstract Traversal<Vertex, Vertex> get_g_addVXanimalX_propertyXname_mateoX_propertyXname_gateoX_propertyXname_cateoX_propertyXage_5X();
@@ -115,6 +123,55 @@ public abstract class AddVertexTest extends AbstractGremlinTest {
     @LoadGraphWith(MODERN)
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES)
+    public void g_addVXpersonX_propertyXname_stephenX_propertyXname_stephenmX() {
+        final Traversal<Vertex, Vertex> traversal = get_g_addVXpersonX_propertyXname_stephenX_propertyXname_stephenmX();
+        printTraversalForm(traversal);
+        final Vertex stephen = traversal.next();
+        assertFalse(traversal.hasNext());
+        assertEquals("person", stephen.label());
+        assertThat((List<String>) IteratorUtils.asList(stephen.values("name")), containsInAnyOrder("stephen", "stephenm"));
+        assertEquals(2, IteratorUtils.count(stephen.properties()));
+        assertEquals(7, IteratorUtils.count(graph.vertices()));
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY)
+    public void g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX() {
+        final Traversal<Vertex, Vertex> traversal = get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX();
+        printTraversalForm(traversal);
+        final Vertex stephen = traversal.next();
+        assertFalse(traversal.hasNext());
+        assertEquals("person", stephen.label());
+        assertEquals("stephenm", stephen.value("name"));
+        assertEquals(1, IteratorUtils.count(stephen.properties()));
+        assertEquals(7, IteratorUtils.count(graph.vertices()));
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES)
+    public void g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenm_since_2010X() {
+        final Traversal<Vertex, Vertex> traversal = get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenm_since_2010X();
+        printTraversalForm(traversal);
+        final Vertex stephen = traversal.next();
+        assertFalse(traversal.hasNext());
+        assertEquals("person", stephen.label());
+        assertEquals("stephenm", stephen.value("name"));
+        assertEquals(2010, Integer.parseInt(stephen.property("name").value("since").toString()));
+        assertEquals(1, IteratorUtils.count(stephen.property("name").properties()));
+        assertEquals(1, IteratorUtils.count(stephen.properties()));
+        assertEquals(7, IteratorUtils.count(graph.vertices()));
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY)
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
     public void g_V_hasXname_markoX_addVXmetaPersonX_propertyXname_nameX_propertyXfriendWeight_outEXknowsX_weight_sum__acl_privateX() {
         final Traversal<Vertex, Vertex> traversal = get_g_V_hasXname_markoX_propertyXfriendWeight_outEXknowsX_weight_sum__acl_privateX();
@@ -236,6 +293,21 @@ public abstract class AddVertexTest extends AbstractGremlinTest {
         }
 
         @Override
+        public Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXname_stephenX_propertyXname_stephenmX() {
+            return g.addV("person").property("name", "stephen").property("name", "stephenm");
+        }
+
+        @Override
+        public Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX() {
+            return g.addV("person").property(VertexProperty.Cardinality.single, "name", "stephen").property(VertexProperty.Cardinality.single, "name", "stephenm");
+        }
+
+        @Override
+        public Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenm_since_2010X() {
+            return g.addV("person").property(VertexProperty.Cardinality.single, "name", "stephen").property(VertexProperty.Cardinality.single, "name", "stephenm", "since", 2010);
+        }
+
+        @Override
         public Traversal<Vertex, Vertex> get_g_V_hasXname_markoX_propertyXfriendWeight_outEXknowsX_weight_sum__acl_privateX() {
             return g.V().has("name", "marko").property("friendWeight", __.outE("knows").values("weight").sum(), "acl", "private");
         }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/9aad7b26/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
index 979020b..077ada0 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
@@ -199,8 +199,8 @@ public class EventStrategyProcessTest extends AbstractGremlinProcessTest {
 
         assertEquals(1, listener1.addVertexEventRecorded());
         assertEquals(1, listener2.addVertexEventRecorded());
-        assertEquals(0, listener2.vertexPropertyChangedEventRecorded());
-        assertEquals(0, listener1.vertexPropertyChangedEventRecorded());
+        assertEquals(1, listener2.vertexPropertyChangedEventRecorded());
+        assertEquals(1, listener1.vertexPropertyChangedEventRecorded());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/9aad7b26/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyProcessTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyProcessTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyProcessTest.java
index e8300dd..10b887f 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyProcessTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategyProcessTest.java
@@ -21,7 +21,6 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
 import org.apache.tinkerpop.gremlin.FeatureRequirement;
 import org.apache.tinkerpop.gremlin.FeatureRequirementSet;
 import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Graph;
@@ -31,8 +30,10 @@ import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.junit.Test;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.junit.Assert.*;
 
 /**
@@ -71,6 +72,23 @@ public class PartitionStrategyProcessTest extends AbstractGremlinProcessTest {
     @Test
     @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES)
+    public void shouldAppendPartitionToVertexPropertyOverMultiProperty() {
+        final PartitionStrategy partitionStrategy = PartitionStrategy.build()
+                .includeMetaProperties(true)
+                .partitionKey(partition).writePartition("A").addReadPartition("A").create();
+        final Vertex v = create(partitionStrategy).addV().property(VertexProperty.Cardinality.list, "any", "thing")
+                .property(VertexProperty.Cardinality.list, "any", "more").next();
+
+        assertNotNull(v);
+        assertThat((List<String>) IteratorUtils.asList(v.properties("any")).stream().map(p -> ((VertexProperty) p).value()).collect(Collectors.toList()), containsInAnyOrder("thing", "more"));
+        assertEquals("A", v.property(partition).value());
+        assertThat((List<String>) IteratorUtils.asList(v.properties("any")).stream().map(p -> ((VertexProperty) p).value(partition)).collect(Collectors.toList()), containsInAnyOrder("A", "A"));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
     public void shouldNotAppendPartitionToVertexProperty() {
         final PartitionStrategy partitionStrategy = PartitionStrategy.build()
                 .includeMetaProperties(false)