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/21 21:25:25 UTC

[tinkerpop] 01/01: wip

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2681
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 780e720c6e4854e74f096b1973d3692efdfac499
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Fri Jan 21 16:19:29 2022 -0500

    wip
---
 docs/src/dev/io/graphbinary.asciidoc               |  5 ++
 .../traversal/step/map/MergeVertexStep.java        |  2 +-
 .../traversal/translator/DotNetTranslator.java     | 29 ++++++++++
 .../traversal/translator/GroovyTranslator.java     |  2 +-
 .../gremlin/structure/io/binary/DataType.java      |  1 +
 .../io/binary/TypeSerializerRegistry.java          |  2 +
 .../structure/io/binary/types/EnumSerializer.java  |  2 +
 .../structure/io/graphson/GraphSONModule.java      |  7 +++
 .../Process/Traversal/GraphTraversal.cs            | 46 ++++++++++++++++
 .../Process/Traversal/GraphTraversalSource.cs      | 54 +++++++++++++++++--
 .../src/Gremlin.Net/Process/Traversal/Merge.cs     | 63 ++++++++++++++++++++++
 .../Structure/IO/GraphBinary/DataType.cs           |  1 +
 .../IO/GraphBinary/TypeSerializerRegistry.cs       |  2 +
 .../IO/GraphBinary/Types/EnumSerializer.cs         |  6 +++
 .../Structure/IO/GraphSON/GraphSONReader.cs        |  1 +
 .../Structure/IO/GraphSON/MergeDeserializer.cs     | 36 +++++++++++++
 .../Gherkin/CommonSteps.cs                         |  9 +++-
 .../Gherkin/GherkinTestRunner.cs                   |  5 ++
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 51 +++++++++---------
 .../Gherkin/IgnoreException.cs                     |  9 +++-
 .../Structure/IO/GraphBinary/GraphBinaryTests.cs   | 15 ++++++
 .../Structure/IO/GraphSON/GraphSONReaderTests.cs   | 12 +++++
 .../Structure/IO/GraphSON/GraphSONWriterTests.cs   | 13 ++++-
 .../gremlin/driver/message/RequestMessage.java     |  3 +-
 .../lib/process/graph-traversal.js                 | 20 +++++++
 .../test/cucumber/feature-steps.js                 |  8 ++-
 .../gremlin-javascript/test/cucumber/gremlin.js    |  3 +-
 .../language/grammar/ReferenceGrammarTest.java     |  2 +-
 gremlin-python/build/generate.groovy               |  2 +-
 .../gremlin_python/process/graph_traversal.py      | 38 +++++++++++++
 .../python/gremlin_python/process/traversal.py     |  5 ++
 .../gremlin_python/structure/io/graphbinaryV1.py   | 10 +++-
 .../src/main/python/radish/feature_steps.py        |  8 ++-
 gremlin-python/src/main/python/radish/gremlin.py   |  5 +-
 .../tests/driver/test_driver_remote_connection.py  |  1 +
 .../tests/structure/io/test_graphbinaryV1.py       |  7 ++-
 .../handler/WsGremlinBinaryRequestDecoder.java     |  1 +
 .../handler/WsGremlinTextRequestDecoder.java       |  1 +
 gremlin-test/features/sideEffect/Group.feature     | 22 ++++----
 gremlin-test/features/sideEffect/Inject.feature    |  7 ++-
 .../tinkerpop/gremlin/features/StepDefinition.java |  5 +-
 .../traversal/step/map/TinkerMergeVertexStep.java  |  1 +
 .../gremlin/tinkergraph/TinkerGraphWorld.java      |  1 +
 43 files changed, 454 insertions(+), 69 deletions(-)

diff --git a/docs/src/dev/io/graphbinary.asciidoc b/docs/src/dev/io/graphbinary.asciidoc
index 065370a..f3b0a82 100644
--- a/docs/src/dev/io/graphbinary.asciidoc
+++ b/docs/src/dev/io/graphbinary.asciidoc
@@ -107,6 +107,7 @@ Changes to existing types require new revision.
 - `0x2b`: Tree
 - `0x2c`: Metrics
 - `0x2d`: TraversalMetrics
+- `0x2e`: Merge
 - `0xfe`: Unspecified null object
 - `0x00`: Custom
 
@@ -567,6 +568,10 @@ Where:
 - `{duration}` is a `Long` describing the duration in nanoseconds.
 - `{metrics}` is a `List` composed by `Metrics` items.
 
+==== Merge
+
+Format: a single `String` representing the enum value.
+
 ==== Custom
 
 A custom type, represented as a blob value.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
index 319db5d..365d83a 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
@@ -60,7 +60,7 @@ public class MergeVertexStep<S> extends FlatMapStep<S, Vertex> implements Mutati
     private Traversal.Admin<S, Map<Object, Object>> onCreateTraversal = null;
     private Traversal.Admin<S, Map<String, Object>> onMatchTraversal = null;
 
-    private CallbackRegistry<Event> callbackRegistry;
+    protected CallbackRegistry<Event> callbackRegistry;
 
     public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Map<Object, Object> searchCreate) {
         this(traversal, isStart, new ConstantTraversal<>(searchCreate));
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
index 5f3ae25..6bd3442 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
@@ -21,12 +21,14 @@ package org.apache.tinkerpop.gremlin.process.traversal.translator;
 
 import org.apache.commons.text.StringEscapeUtils;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.Pick;
 import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
 import org.apache.tinkerpop.gremlin.process.traversal.Script;
 import org.apache.tinkerpop.gremlin.process.traversal.TextP;
 import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
@@ -345,6 +347,33 @@ public final class DotNetTranslator implements Translator.ScriptTranslator {
                         script.append(", (").append(castSecondArgTo).append(") ");
                         convertToScript(instruction.getArguments()[1]);
                         script.append(",");
+                    } else if (methodName.equals(GraphTraversal.Symbols.mergeE) || methodName.equals(GraphTraversal.Symbols.mergeV)) {
+                        // there must be at least one argument - if null go with Map
+                        final Object instArg = instruction.getArguments()[0];
+                        if (null == instArg) {
+                            script.append("(IDictionary<object,object>) null");
+                        } else {
+                            if (instArg instanceof Traversal) {
+                                script.append("(ITraversal) ");
+                            } else {
+                                script.append("(IDictionary<object,object>) ");
+                            }
+                            convertToScript(instArg);
+                            script.append(")");
+                        }
+                    } else if (methodName.equals(GraphTraversal.Symbols.option) &&
+                            instruction.getArguments().length == 2 && instruction.getArguments()[0] instanceof Merge) {
+                        final Object[] instArgs = instruction.getArguments();
+                        // trying to catch option(Merge,Traversal|Map)
+                        convertToScript(instArgs[0]);
+                        script.append(", ");
+                        if (instArgs[1] instanceof Traversal) {
+                            script.append("(ITraversal) ");
+                        } else {
+                            script.append("(IDictionary<object,object>) ");
+                        }
+                        convertToScript(instArgs[1]);
+                        script.append(")");
                     } else {
                         final Object[] instArgs = instruction.getArguments();
                         for (int idx = 0; idx < instArgs.length; idx++) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
index bfa3065..0143c27 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
@@ -174,7 +174,7 @@ public final class GroovyTranslator implements Translator.ScriptTranslator {
 
         @Override
         protected String getSyntax(final Pick o) {
-            return "TraversalOptionParent.Pick." + o.toString();
+            return "Pick." + o.toString();
         }
 
         @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java
index 0159d4f..75b8060 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java
@@ -70,6 +70,7 @@ public enum DataType {
     TREE(0X2B),
     METRICS(0x2C),
     TRAVERSALMETRICS(0x2D),
+    MERGE(0x2E),
 
     CHAR(0X80),
     DURATION(0X81),
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
index 0c4b913..4edcad2 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.structure.io.binary;
 
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
 import org.apache.tinkerpop.gremlin.process.traversal.Pick;
 import org.apache.tinkerpop.gremlin.structure.io.binary.types.*;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
@@ -117,6 +118,7 @@ public class TypeSerializerRegistry {
             new RegistryEntry<>(VertexProperty.Cardinality.class, EnumSerializer.CardinalitySerializer),
             new RegistryEntry<>(Column.class, EnumSerializer.ColumnSerializer),
             new RegistryEntry<>(Direction.class, EnumSerializer.DirectionSerializer),
+            new RegistryEntry<>(Merge.class, EnumSerializer.MergeSerializer),
             new RegistryEntry<>(Operator.class, EnumSerializer.OperatorSerializer),
             new RegistryEntry<>(Order.class, EnumSerializer.OrderSerializer),
             new RegistryEntry<>(Pick.class, EnumSerializer.PickSerializer),
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EnumSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EnumSerializer.java
index 52b4c08..41cf7fc 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EnumSerializer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/EnumSerializer.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.structure.io.binary.types;
 
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
 import org.apache.tinkerpop.gremlin.process.traversal.Pick;
 import org.apache.tinkerpop.gremlin.structure.io.binary.DataType;
 import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
@@ -47,6 +48,7 @@ public class EnumSerializer<E extends Enum> extends SimpleTypeSerializer<E> {
     public static final EnumSerializer<VertexProperty.Cardinality> CardinalitySerializer = new EnumSerializer<>(DataType.CARDINALITY, VertexProperty.Cardinality::valueOf);
     public static final EnumSerializer<Column> ColumnSerializer = new EnumSerializer<>(DataType.COLUMN, Column::valueOf);
     public static final EnumSerializer<Direction> DirectionSerializer = new EnumSerializer<>(DataType.DIRECTION, Direction::valueOf);
+    public static final EnumSerializer<Merge> MergeSerializer = new EnumSerializer<>(DataType.MERGE, Merge::valueOf);
     public static final EnumSerializer<Operator> OperatorSerializer = new EnumSerializer<>(DataType.OPERATOR, Operator::valueOf);
     public static final EnumSerializer<Order> OrderSerializer = new EnumSerializer<>(DataType.ORDER, Order::valueOf);
     public static final EnumSerializer<Pick> PickSerializer = new EnumSerializer<>(DataType.PICK, Pick::valueOf);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index fccab3f..82d818f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
 import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
@@ -174,6 +175,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                             VertexProperty.Cardinality.class,
                             Column.class,
                             Direction.class,
+                            Merge.class,
                             Operator.class,
                             Order.class,
                             Pop.class,
@@ -257,6 +259,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
             Stream.of(VertexProperty.Cardinality.class,
                     Column.class,
                     Direction.class,
+                    Merge.class,
                     Operator.class,
                     Order.class,
                     Pop.class,
@@ -299,6 +302,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
             Stream.of(VertexProperty.Cardinality.values(),
                     Column.values(),
                     Direction.values(),
+                    Merge.values(),
                     Operator.values(),
                     Order.values(),
                     Pop.values(),
@@ -412,6 +416,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                             VertexProperty.Cardinality.class,
                             Column.class,
                             Direction.class,
+                            Merge.class,
                             Operator.class,
                             Order.class,
                             Pop.class,
@@ -491,6 +496,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
             Stream.of(VertexProperty.Cardinality.class,
                     Column.class,
                     Direction.class,
+                    Merge.class,
                     Operator.class,
                     Order.class,
                     Pop.class,
@@ -527,6 +533,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
             Stream.of(VertexProperty.Cardinality.values(),
                     Column.values(),
                     Direction.values(),
+                    Merge.values(),
                     Operator.values(),
                     Order.values(),
                     Pop.values(),
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index ebd1cdf..59ecb1a 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -1090,6 +1090,43 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Adds the mergeE step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, Edge> MergeE (IDictionary<object,object> m)
+        {
+            Bytecode.AddStep("mergeE", m);
+            return Wrap<S, Edge>(this);
+        }
+
+        /// <summary>
+        ///     Adds the mergeE step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, Edge> MergeE (ITraversal t)
+        {
+            Bytecode.AddStep("mergeE", t);
+            return Wrap<S, Edge>(this);
+        }
+
+        /// <summary>
+        ///     Adds the mergeV step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, Vertex> MergeV (IDictionary<object,object> m)
+        {
+            Bytecode.AddStep("mergeV", m);
+            return Wrap<S, Vertex>(this);
+        }
+
+        /// <summary>
+        ///     Adds the mergeV step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, Vertex> MergeV (ITraversal t)
+        {
+            Bytecode.AddStep("mergeV", t);
+            return Wrap<S, Vertex>(this);
+        }
+
+
+        /// <summary>
         ///     Adds the min step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
         public GraphTraversal<S, E2> Min<E2> ()
@@ -1137,6 +1174,15 @@ namespace Gremlin.Net.Process.Traversal
         /// <summary>
         ///     Adds the option step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
+        public GraphTraversal<S, E> Option (object pickToken, IDictionary<object,object> traversalOption)
+        {
+            Bytecode.AddStep("option", pickToken, traversalOption);
+            return Wrap<S, E>(this);
+        }
+
+        /// <summary>
+        ///     Adds the option step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
         public GraphTraversal<S, E> Option (ITraversal traversalOption)
         {
             Bytecode.AddStep("option", traversalOption);
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
index ae5b1d9..5ad2f7d 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
@@ -336,7 +336,7 @@ namespace Gremlin.Net.Process.Traversal
         public GraphTraversal<Edge, Edge> AddE(string label)
         {
             var traversal = new GraphTraversal<Edge, Edge>(TraversalStrategies, new Bytecode(Bytecode));
-                traversal.Bytecode.AddStep("addE", label);
+            traversal.Bytecode.AddStep("addE", label);
             return traversal;
         }
 
@@ -347,7 +347,29 @@ namespace Gremlin.Net.Process.Traversal
         public GraphTraversal<Edge, Edge> AddE(ITraversal edgeLabelTraversal)
         {
             var traversal = new GraphTraversal<Edge, Edge>(TraversalStrategies, new Bytecode(Bytecode));
-                traversal.Bytecode.AddStep("addE", edgeLabelTraversal);
+            traversal.Bytecode.AddStep("addE", edgeLabelTraversal);
+            return traversal;
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> off this graph traversal source and adds the mergeE step to that
+        ///     traversal.
+        /// </summary>
+        public GraphTraversal<Edge, Edge> MergeE(IDictionary<object,object> m)
+        {
+            var traversal = new GraphTraversal<Edge, Edge>(TraversalStrategies, new Bytecode(Bytecode));
+            traversal.Bytecode.AddStep("mergeE", m);
+            return traversal;
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> off this graph traversal source and adds the mergeE step to that
+        ///     traversal.
+        /// </summary>
+        public GraphTraversal<Edge, Edge> MergeE(ITraversal t)
+        {
+            var traversal = new GraphTraversal<Edge, Edge>(TraversalStrategies, new Bytecode(Bytecode));
+            traversal.Bytecode.AddStep("mergeE", t);
             return traversal;
         }
 
@@ -358,7 +380,7 @@ namespace Gremlin.Net.Process.Traversal
         public GraphTraversal<Vertex, Vertex> AddV()
         {
             var traversal = new GraphTraversal<Vertex, Vertex>(TraversalStrategies, new Bytecode(Bytecode));
-                traversal.Bytecode.AddStep("addV");
+            traversal.Bytecode.AddStep("addV");
             return traversal;
         }
 
@@ -369,7 +391,7 @@ namespace Gremlin.Net.Process.Traversal
         public GraphTraversal<Vertex, Vertex> AddV(string label)
         {
             var traversal = new GraphTraversal<Vertex, Vertex>(TraversalStrategies, new Bytecode(Bytecode));
-                traversal.Bytecode.AddStep("addV", label);
+            traversal.Bytecode.AddStep("addV", label);
             return traversal;
         }
 
@@ -380,7 +402,29 @@ namespace Gremlin.Net.Process.Traversal
         public GraphTraversal<Vertex, Vertex> AddV(ITraversal vertexLabelTraversal)
         {
             var traversal = new GraphTraversal<Vertex, Vertex>(TraversalStrategies, new Bytecode(Bytecode));
-                traversal.Bytecode.AddStep("addV", vertexLabelTraversal);
+            traversal.Bytecode.AddStep("addV", vertexLabelTraversal);
+            return traversal;
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> off this graph traversal source and adds the mergeV step to that
+        ///     traversal.
+        /// </summary>
+        public GraphTraversal<Vertex, Vertex> MergeV(IDictionary<object,object> m)
+        {
+            var traversal = new GraphTraversal<Vertex, Vertex>(TraversalStrategies, new Bytecode(Bytecode));
+            traversal.Bytecode.AddStep("mergeV", m);
+            return traversal;
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> off this graph traversal source and adds the mergeV step to that
+        ///     traversal.
+        /// </summary>
+        public GraphTraversal<Vertex, Vertex> MergeV(ITraversal t)
+        {
+            var traversal = new GraphTraversal<Vertex, Vertex>(TraversalStrategies, new Bytecode(Bytecode));
+            traversal.Bytecode.AddStep("mergeV", t);
             return traversal;
         }
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Merge.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Merge.cs
new file mode 100644
index 0000000..b62fc8f
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Merge.cs
@@ -0,0 +1,63 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace Gremlin.Net.Process.Traversal
+{
+#pragma warning disable 1591
+
+    public class Merge : EnumWrapper, IFunction
+    {
+        private Merge(string enumValue)
+            : base("Merge", enumValue)
+        {
+        }
+
+        public static Merge OnCreate => new Merge("onCreate");
+
+        public static Merge OnMatch => new Merge("onMatch");
+
+        private static readonly IDictionary<string, Merge> Properties = new Dictionary<string, Merge>
+        {
+            { "onCreate", OnCreate },
+            { "onMatch", OnMatch },
+        };
+
+        /// <summary>
+        /// Gets the Merge enumeration by value.
+        /// </summary>
+        public static Merge GetByValue(string value)
+        {
+            if (!Properties.TryGetValue(value, out var property))
+            {
+                throw new ArgumentException($"No matching Merge for value '{value}'");
+            }
+            return property;
+        }
+    }
+
+
+#pragma warning restore 1591
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/DataType.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/DataType.cs
index 3e65ad0..ee33cee 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/DataType.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/DataType.cs
@@ -55,6 +55,7 @@ namespace Gremlin.Net.Structure.IO.GraphBinary
         public static readonly DataType Cardinality = new DataType(0x16);
         public static readonly DataType Column = new DataType(0x17);
         public static readonly DataType Direction = new DataType(0x18);
+        public static readonly DataType Merge = new DataType(0x2E);
         public static readonly DataType Operator = new DataType(0x19);
         public static readonly DataType Order = new DataType(0x1A);
         public static readonly DataType Pick = new DataType(0x1B);
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs
index 87c28e6..d30b5ca 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs
@@ -60,6 +60,7 @@ namespace Gremlin.Net.Structure.IO.GraphBinary
                 {typeof(Cardinality), EnumSerializers.CardinalitySerializer},
                 {typeof(Column), EnumSerializers.ColumnSerializer},
                 {typeof(Direction), EnumSerializers.DirectionSerializer},
+                {typeof(Merge), EnumSerializers.MergeSerializer},
                 {typeof(Operator), EnumSerializers.OperatorSerializer},
                 {typeof(Order), EnumSerializers.OrderSerializer},
                 {typeof(Pick), EnumSerializers.PickSerializer},
@@ -107,6 +108,7 @@ namespace Gremlin.Net.Structure.IO.GraphBinary
                 {DataType.Cardinality, EnumSerializers.CardinalitySerializer},
                 {DataType.Column, EnumSerializers.ColumnSerializer},
                 {DataType.Direction, EnumSerializers.DirectionSerializer},
+                {DataType.Merge, EnumSerializers.MergeSerializer},
                 {DataType.Operator, EnumSerializers.OperatorSerializer},
                 {DataType.Order, EnumSerializers.OrderSerializer},
                 {DataType.Pick, EnumSerializers.PickSerializer},
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/EnumSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/EnumSerializer.cs
index d561c2a..5bc6080 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/EnumSerializer.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/EnumSerializer.cs
@@ -58,6 +58,12 @@ namespace Gremlin.Net.Structure.IO.GraphBinary.Types
             new EnumSerializer<Direction>(DataType.Direction, Direction.GetByValue);
 
         /// <summary>
+        /// A serializer for <see cref="Merge"/> values.
+        /// </summary>
+        public static readonly EnumSerializer<Merge> MergeSerializer =
+            new EnumSerializer<Merge>(DataType.Merge, Merge.GetByValue);
+
+        /// <summary>
         /// A serializer for <see cref="Operator"/> values.
         /// </summary>
         public static readonly EnumSerializer<Operator> OperatorSerializer =
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs
index 16901bd..3208966 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs
@@ -45,6 +45,7 @@ namespace Gremlin.Net.Structure.IO.GraphSON
                 {"g:Float", new FloatConverter()},
                 {"g:Double", new DoubleConverter()},
                 {"g:Direction", new DirectionDeserializer()},
+                {"g:Merge", new MergeDeserializer()},
                 {"g:UUID", new UuidDeserializer()},
                 {"g:Date", new DateDeserializer()},
                 {"g:Timestamp", new DateDeserializer()},
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/MergeDeserializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/MergeDeserializer.cs
new file mode 100644
index 0000000..26b6869
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/MergeDeserializer.cs
@@ -0,0 +1,36 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+using System.Text.Json;
+using Gremlin.Net.Process.Traversal;
+
+namespace Gremlin.Net.Structure.IO.GraphSON
+{
+    internal class MergeDeserializer : IGraphSONDeserializer
+    {
+        public dynamic Objectify(JsonElement graphsonObject, GraphSONReader reader)
+        {
+            return Merge.GetByValue(graphsonObject.GetString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
index 03b429a..fcac51a 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
@@ -379,7 +379,14 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
 
         private static Vertex ToVertex(string name, string graphName)
         {
-            return ScenarioData.GetByGraphName(graphName).Vertices[name];
+            if (ScenarioData.GetByGraphName(graphName).Vertices.ContainsKey(name))
+            {
+                return ScenarioData.GetByGraphName(graphName).Vertices[name];
+            }
+            else
+            {
+                return new Vertex(name);
+            }
         }
 
         private static Edge ToEdge(string name, string graphName)
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
index 0209c7b..365bedb 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
@@ -87,6 +87,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                 },
                 {"g_V_properties_order", IgnoreReason.VertexPropertyNotSupportedInGherkin},
                 {"g_V_properties_order_id", IgnoreReason.VertexPropertyNotSupportedInGherkin},
+                {"g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", IgnoreReason.MergeVEWithTraversalNotSupportedInTranslation},
+                {"g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", IgnoreReason.MergeVEWithTraversalNotSupportedInTranslation},
+                {"g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_injectX0X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option", IgnoreReason.MergeVEWithTraversalNotSupportedInTranslation},
+                {"g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX", IgnoreReason.MergeVEWithTraversalNotSupportedInTranslation},
+                {"g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option", IgnoreReason.MergeVEWithTraversalNotSupportedInTranslation},
             };
 
         private static class Keywords
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index f89e2a5..4ff7bbf 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -500,30 +500,30 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Mean<object>()}}, 
                {"g_injectXnull_10_20_nullX_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Mean<object>()}}, 
                {"g_injectXlistXnull_10_20_nullXX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Mean<object>(Scope.Local)}}, 
-               {"g_V_mergeEXlabel_self_weight_05X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (g,p) =>g.V().MergeE(p["xx1"]), (g,p) =>g.E().HasLabel("self").Has("weight",0.5)}}, 
-               {"g_mergeEXlabel_knows_out_marko_in_vadasX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").AddV("person").Property(T.Id,101).Property("name","vadas"), (g,p) =>g.MergeE(p["xx1"]), (g,p) =>g.V().Has("person","name","marko").Out("knows").Has("person","name","vadas")}}, 
-               {"g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b"), (g,p) =>g.MergeE(p["xx1"]), (g,p) =>g.V().Has("person","name","marko").OutE("knows").Has("weight",0.5).InV().Has("person","name","vadas"), (g,p) =>g.V().Has("p [...]
-               {"g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeE(p["xx1"]), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows").Has("weight",0.5)}}, 
-               {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeE(p["xx1"]).Option(Merge.OnCreate,p["xx2"]).Option(Merge.OnMatch,p["xx3"]), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows").Has("created","Y"), (g,p) =>g.E().HasLabel("knows").Has("created","N")}}, 
-               {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b"), (g,p) =>g.MergeE(p["xx1"]).Option(Merge.OnCreate,p["xx2"]).Option(Merge.OnMatch,p["xx3"]), (g,p) =>g.V(), (g,p) =>g. [...]
-               {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y"), (g,p) =>g.MergeE(p["xx1"]).Option(Merge.OnCreate,p["xx2"]).Option(Merge.OnMatch,p["x [...]
-               {"g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y").AddE("knows").From("a").To("b"), (g,p) =>g.V().Has("person","name","marko").Me [...]
-               {"g_VX100X_VX101X_mergeEXlabel_knows_out_marko_in_vadasX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko"), (g,p) =>g.V(100).V(101).MergeE(p["xx1"]), (g,p) =>g.V(), (g,p) =>g.E()}}, 
-               {"g_mergeVXlabel_person_name_stephenX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]), (g,p) =>g.V().Has("person","name","stephen")}}, 
-               {"g_mergeVXlabel_person_name_markoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]), (g,p) =>g.V().Has("person","name","marko")}}, 
-               {"g_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]).Option(Merge.OnCreate,p["xx2"]), (g,p) =>g.V().Has("person","name","stephen").Has("age",19)}}, 
-               {"g_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]).Option(Merge.OnMatch,p["xx2"]), (g,p) =>g.V().Has("person","name","marko").Has("age",19)}}, 
-               {"g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).MergeV(__.Select<object>("c")).Option(Merge.OnCreate,__.Select<object>("m")), (g,p) =>g.V().Has [...]
-               {"g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).MergeV(__.Select<object>("c")).Option(Merge.OnMatch,__.Select<object>("m")), (g,p) =>g.V().Has("person","name","marko").Has( [...]
-               {"g_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV(p["xx1"]).Property("name","vadas","acl","public"), (g,p) =>g.V().Properties<object>("name").HasValue("vadas").Has("acl","public")}}, 
-               {"g_injectX0X_mergeVXlabel_person_name_stephenX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]), (g,p) =>g.V().Has("person","name","stephen")}}, 
-               {"g_injectX0X_mergeVXlabel_person_name_markoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]), (g,p) =>g.V().Has("person","name","marko")}}, 
-               {"g_injectX0X_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]).Option(Merge.OnCreate,p["xx2"]), (g,p) =>g.V().Has("person","name","stephen").Has("age",19)}}, 
-               {"g_injectX0X_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]).Option(Merge.OnMatch,p["xx2"]), (g,p) =>g.V().Has("person","name","marko").Has("age",19)}}, 
-               {"g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_injectX0X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).Inject(0).MergeV(__.Select<object>("c")).Option(Merge.OnCreate,__.Select<object>("m") [...]
-               {"g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).Inject(0).MergeV(__.Select<object>("c")).Option(Merge.OnMatch,__.Select<object>("m")), (g,p) =>g.V().Has("person", [...]
-               {"g_injectX0X_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV(p["xx1"]).Property("name","vadas","acl","public"), (g,p) =>g.V().Properties<object>("name").HasValue("vadas").Has("acl","public")}}, 
-               {"g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(p["xx1"],p["xx2"]).MergeV(__.Identity()), (g,p) =>g.V().Has("person","name","stephen"), (g,p) =>g.V().Has("person","name","marko"), (g,p) =>g.V()}}, 
+               {"g_V_mergeEXlabel_self_weight_05X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (g,p) =>g.V().MergeE((IDictionary<object,object>) p["xx1"]), (g,p) =>g.E().HasLabel("self").Has("weight",0.5)}}, 
+               {"g_mergeEXlabel_knows_out_marko_in_vadasX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").AddV("person").Property(T.Id,101).Property("name","vadas"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V().Has("person","name","marko").Out("knows").Has("person","name","vadas")}}, 
+               {"g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V().Has("person","name","marko").OutE("knows").Has("weight",0.5).InV().Has("person","name"," [...]
+               {"g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows").Has("weight",0.5)}}, 
+               {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"]).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx3"]), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows").Has("created","Y"), (g,p) =>g.E().HasLabel("knows").Has("created","N")}}, 
+               {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"] [...]
+               {"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDic [...]
+               {"g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko").As("a").AddV("person").Property(T.Id,101).Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y").AddE("knows").From("a").To("b"), (g,p) =>g.V().Has("person","name","marko").Me [...]
+               {"g_VX100X_VX101X_mergeEXlabel_knows_out_marko_in_vadasX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property(T.Id,100).Property("name","marko"), (g,p) =>g.V(100).V(101).MergeE((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V(), (g,p) =>g.E()}}, 
+               {"g_mergeVXlabel_person_name_stephenX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V().Has("person","name","stephen")}}, 
+               {"g_mergeVXlabel_person_name_markoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V().Has("person","name","marko")}}, 
+               {"g_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V().Has("person","name","stephen").Has("age",19)}}, 
+               {"g_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V().Has("person","name","marko").Has("age",19)}}, 
+               {"g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).MergeV((IDictionary<object,object>) __.Select<object>("c")).Option(Merge.OnCreate, (IDictionary [...]
+               {"g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).MergeV((IDictionary<object,object>) __.Select<object>("c")).Option(Merge.OnMatch, (IDictionary<object,object>) __.Select<obj [...]
+               {"g_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).Property("name","vadas","acl","public"), (g,p) =>g.V().Properties<object>("name").HasValue("vadas").Has("acl","public")}}, 
+               {"g_injectX0X_mergeVXlabel_person_name_stephenX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V().Has("person","name","stephen")}}, 
+               {"g_injectX0X_mergeVXlabel_person_name_markoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V().Has("person","name","marko")}}, 
+               {"g_injectX0X_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V().Has("person","name","stephen").Has("age",19)}}, 
+               {"g_injectX0X_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V().Has("person","name","marko").Has("age",19)}}, 
+               {"g_withSideEffectXc_label_person_name_stephenX_withSideEffectXm_label_person_name_stephen_age_19X_injectX0X_mergeVXselectXcXX_optionXonCreate_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).Inject(0).MergeV((IDictionary<object,object>) __.Select<object>("c")).Option(Merge.On [...]
+               {"g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.WithSideEffect("c",p["xx1"]).WithSideEffect("m",p["xx2"]).Inject(0).MergeV((IDictionary<object,object>) __.Select<object>("c")).Option(Merge.OnMatch, (IDictionary<object,ob [...]
+               {"g_injectX0X_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(0).MergeV((IDictionary<object,object>) p["xx1"]).Property("name","vadas","acl","public"), (g,p) =>g.V().Properties<object>("name").HasValue("vadas").Has("acl","public")}}, 
+               {"g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko"), (g,p) =>g.Inject(p["xx1"],p["xx2"]).MergeV((IDictionary<object,object>) __.Identity()), (g,p) =>g.V().Has("person","name","stephen"), (g,p) =>g.V().Has("person","name","marko"), (g,p) =>g.V()}}, 
                {"g_V_age_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Min<object>()}}, 
                {"g_V_foo_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Min<object>()}}, 
                {"g_V_name_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Min<object>()}}, 
@@ -831,7 +831,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_outXfollowedByX_group_byXsongTypeX_byXbothE_group_byXlabelX_byXweight_sumXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out("followedBy").Group<object,object>().By("songType").By(__.BothE().Group<object,object>().By(T.Label).By(__.Values<object>("weight").Sum<object>()))}}, 
                {"g_V_groupXmX_byXnameX_byXinXknowsX_nameX_capXmX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group("m").By("name").By(__.In("knows").Values<object>("name")).Cap<object>("m")}}, 
                {"g_V_group_byXlabelX_byXbothE_groupXaX_byXlabelX_byXweight_sumX_weight_sumX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By(T.Label).By(__.BothE().Group("a").By(T.Label).By(__.Values<object>("weight").Sum<object>()).Values<object>("weight").Sum<object>())}}, 
-               {"g_withSideEffectXa__marko_666_noone_blahX_V_groupXaX_byXnameX_byXoutE_label_foldX_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSideEffect("a",p["xx1"]).V().Group("a").By("name").By(__.OutE().Label().Fold()).Cap<object>("a").Unfold<object>().Group<object,object>().By(Column.Keys).By(__.Select<object>(Column.Values).Order(Scope.Local).By(Order.Asc))}}, 
                {"g_V_hasLabelXpersonX_asXpX_outXcreatedX_group_byXnameX_byXselectXpX_valuesXageX_sumX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").As("p").Out("created").Group<object,object>().By("name").By(__.Select<object>("p").Values<object>("age").Sum<object>())}}, 
                {"g_V_hasLabelXpersonX_asXpX_outXcreatedX_groupXaX_byXnameX_byXselectXpX_valuesXageX_sumX_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").As("p").Out("created").Group("a").By("name").By(__.Select<object>("p").Values<object>("age").Sum<object>()).Cap<object>("a")}}, 
                {"g_V_group_byXlabelX_byXlabel_countX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By(__.Label()).By(__.Label().Count())}}, 
@@ -854,7 +853,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_hasXperson_name_markoX_bothXknowsX_groupCount_byXvaluesXnameX_foldX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("person","name","marko").Both("knows").GroupCount<object>().By(__.Values<object>("name").Fold())}}, 
                {"g_VX1X_out_injectXv2X_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Inject((Vertex) p["v2"]).Values<object>("name")}}, 
                {"g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Values<object>("name").Inject("daniel").As("a").Map<object>((IFunction) p["l1"]).Path()}}, 
-               {"g_VX1X_injectXg_VX4XX_out_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Inject((Vertex) p["v4"]).Out().Values<object>("name")}}, 
+               {"g_VX1X_injectXg_VX4XX_out_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Inject((Vertex) p["v2"]).Out().Values<object>("name")}}, 
                {"g_injectXnull_1_3_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,1,3,null)}}, 
                {"g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(10,20,null,20,10,10).GroupCount("x").Dedup().As("y").Project<object>("a","b").By().By(__.Select<object>("x").Select<object>(__.Select<object>("y")))}}, 
                {"g_injectXname_marko_age_nullX_selectXname_ageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Select<object>("name","age")}}, 
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs
index 2fa881b..8f1bbac 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs
@@ -70,6 +70,13 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
         /// <summary>
         /// Need a Gherkin parser for VertexProperty results: https://issues.apache.org/jira/browse/TINKERPOP-2686
         /// </summary>
-        VertexPropertyNotSupportedInGherkin
+        VertexPropertyNotSupportedInGherkin,
+
+       /// <summary>
+       /// VarAsBindingASTTransformation isn't capable of properly casting arguments so the DotNetTranslator can't
+       /// produce the right Gremlin. These tests could be static translated but there are a lot of them so it would
+       /// be better to solve this more directly.
+       /// </summary>
+       MergeVEWithTraversalNotSupportedInTranslation
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary/GraphBinaryTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary/GraphBinaryTests.cs
index 7978005..eecb385 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary/GraphBinaryTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary/GraphBinaryTests.cs
@@ -535,6 +535,21 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary
             
             Assert.Equal(expected, actual);
         }
+
+        [Fact]
+        public async Task TestMerge()
+        {
+            var expected = Merge.OnCreate;
+            var writer = CreateGraphBinaryWriter();
+            var reader = CreateGraphBinaryReader();
+            var serializationStream = new MemoryStream();
+
+            await writer.WriteAsync(expected, serializationStream);
+            serializationStream.Position = 0;
+            var actual = await reader.ReadAsync(serializationStream);
+
+            Assert.Equal(expected, actual);
+        }
         
         [Fact]
         public async Task TestOperator()
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
index 5bc232b..2c33a1e 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
@@ -312,6 +312,18 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON
         
             Assert.Equal(Direction.Out, deserializedValue);
         }
+
+        [Theory, MemberData(nameof(Versions))]
+        public void ShouldDeserializeMerge(int version)
+        {
+            const string serializedValue = "{\"@type\":\"g:Merge\",\"@value\":\"onMatch\"}";
+            var reader = CreateStandardGraphSONReader(version);
+
+            var jsonElement = JsonSerializer.Deserialize<JsonElement>(serializedValue);
+            var deserializedValue = reader.ToObject(jsonElement);
+
+            Assert.Equal(Merge.OnMatch, deserializedValue);
+        }
         
         [Fact]
         public void ShouldDeserializePathFromGraphSON2()
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs
index ce593f7..9247831 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs
@@ -258,7 +258,7 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON
         }
 
         [Theory, MemberData(nameof(Versions))]
-        public void ShouldSerializeEnum(int version)
+        public void ShouldSerializeDirection(int version)
         {
             var writer = CreateGraphSONWriter(version);
 
@@ -269,6 +269,17 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON
         }
 
         [Theory, MemberData(nameof(Versions))]
+        public void ShouldSerializeMerge(int version)
+        {
+            var writer = CreateGraphSONWriter(version);
+
+            var serializedEnum = writer.WriteObject(Merge.OnMatch);
+
+            var expectedGraphSON = "{\"@type\":\"g:Merge\",\"@value\":\"onMatch\"}";
+            Assert.Equal(expectedGraphSON, serializedEnum);
+        }
+
+        [Theory, MemberData(nameof(Versions))]
         public void ShouldSerializeList(int version)
         {
             var writer = CreateGraphSONWriter(version);
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/RequestMessage.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/RequestMessage.java
index dc10baf..aeebf35 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/RequestMessage.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/RequestMessage.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.driver.message;
 
+import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
 
 import java.util.HashMap;
@@ -34,7 +35,7 @@ public final class RequestMessage {
     /**
      * An "invalid" message.  Used internally only.
      */
-    public static final RequestMessage INVALID = new RequestMessage("invalid");
+    public static final RequestMessage INVALID = new RequestMessage(Tokens.OPS_INVALID);
 
     private final UUID requestId;
     private final String op;
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
index c609ba0..ae3d7f3 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
@@ -212,6 +212,16 @@ class GraphTraversalSource {
     const b = new Bytecode(this.bytecode).addStep('addE', args);
     return new this.graphTraversalClass(this.graph, new TraversalStrategies(this.traversalStrategies), b);
   }
+
+  /**
+   * mergeV GraphTraversalSource step method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  mergeE(...args) {
+    const b = new Bytecode(this.bytecode).addStep('mergeE', args);
+    return new this.graphTraversalClass(this.graph, new TraversalStrategies(this.traversalStrategies), b);
+  }
   
   /**
    * addV GraphTraversalSource step method.
@@ -222,6 +232,16 @@ class GraphTraversalSource {
     const b = new Bytecode(this.bytecode).addStep('addV', args);
     return new this.graphTraversalClass(this.graph, new TraversalStrategies(this.traversalStrategies), b);
   }
+
+  /**
+   * mergeV GraphTraversalSource step method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  mergeV(...args) {
+    const b = new Bytecode(this.bytecode).addStep('mergeV', args);
+    return new this.graphTraversalClass(this.graph, new TraversalStrategies(this.traversalStrategies), b);
+  }
   
   /**
    * inject GraphTraversalSource step method.
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
index d500feb..ba23650 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
@@ -273,7 +273,13 @@ function toNumeric(stringValue) {
 }
 
 function toVertex(name) {
-  return this.getData().vertices.get(name);
+  // some vertices are cached, like those from toy graphs but some are just references. if they are
+  // not cached then they are meant to be references.
+  const vertices = this.getData().vertices;
+  if (vertices.has(name))
+    return this.getData().vertices.get(name);
+  else
+    return new graphModule.Vertex(name, "vertex")
 }
 
 function toVertexId(name) {
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 6079579..6af6338 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
@@ -820,7 +820,6 @@ const gremlins = {
     g_V_outXfollowedByX_group_byXsongTypeX_byXbothE_group_byXlabelX_byXweight_sumXX: [function({g}) { return g.V().out("followedBy").group().by("songType").by(__.bothE().group().by(T.label).by(__.values("weight").sum())) }], 
     g_V_groupXmX_byXnameX_byXinXknowsX_nameX_capXmX: [function({g}) { return g.V().group("m").by("name").by(__.in_("knows").values("name")).cap("m") }], 
     g_V_group_byXlabelX_byXbothE_groupXaX_byXlabelX_byXweight_sumX_weight_sumX: [function({g}) { return g.V().group().by(T.label).by(__.bothE().group("a").by(T.label).by(__.values("weight").sum()).values("weight").sum()) }], 
-    g_withSideEffectXa__marko_666_noone_blahX_V_groupXaX_byXnameX_byXoutE_label_foldX_capXaX: [function({g, xx1}) { return g.withSideEffect("a",xx1).V().group("a").by("name").by(__.outE().label().fold()).cap("a").unfold().group().by(Column.keys).by(__.select(Column.values).order(Scope.local).by(Order.asc)) }], 
     g_V_hasLabelXpersonX_asXpX_outXcreatedX_group_byXnameX_byXselectXpX_valuesXageX_sumX: [function({g}) { return g.V().hasLabel("person").as("p").out("created").group().by("name").by(__.select("p").values("age").sum()) }], 
     g_V_hasLabelXpersonX_asXpX_outXcreatedX_groupXaX_byXnameX_byXselectXpX_valuesXageX_sumX_capXaX: [function({g}) { return g.V().hasLabel("person").as("p").out("created").group("a").by("name").by(__.select("p").values("age").sum()).cap("a") }], 
     g_V_group_byXlabelX_byXlabel_countX: [function({g}) { return g.V().group().by(__.label()).by(__.label().count()) }], 
@@ -843,7 +842,7 @@ const gremlins = {
     g_V_hasXperson_name_markoX_bothXknowsX_groupCount_byXvaluesXnameX_foldX: [function({g}) { return g.V().has("person","name","marko").both("knows").groupCount().by(__.values("name").fold()) }], 
     g_VX1X_out_injectXv2X_name: [function({g, vid1, v2}) { return g.V(vid1).out().inject(v2).values("name") }], 
     g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path: [function({g, l1, vid1}) { return g.V(vid1).out().values("name").inject("daniel").as("a").map(l1).path() }], 
-    g_VX1X_injectXg_VX4XX_out_name: [function({g, vid1, v4}) { return g.V(vid1).inject(v4).out().values("name") }], 
+    g_VX1X_injectXg_VX4XX_out_name: [function({g, vid1, v2}) { return g.V(vid1).inject(v2).out().values("name") }], 
     g_injectXnull_1_3_nullX: [function({g}) { return g.inject(null,1,3,null) }], 
     g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX: [function({g}) { return g.inject(10,20,null,20,10,10).groupCount("x").dedup().as("y").project("a","b").by().by(__.select("x").select(__.select("y"))) }], 
     g_injectXname_marko_age_nullX_selectXname_ageX: [function({g, xx1}) { return g.inject(xx1).select("name","age") }], 
diff --git a/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ReferenceGrammarTest.java b/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ReferenceGrammarTest.java
index a0e91fe..b241c59 100644
--- a/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ReferenceGrammarTest.java
+++ b/gremlin-language/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ReferenceGrammarTest.java
@@ -70,7 +70,7 @@ public class ReferenceGrammarTest extends AbstractGrammarTest {
             return String.format("[%s]", listItems);
         }));
         add(Pair.with(Pattern.compile("v\\[(.+)\\]"), (k,v) -> "\"1\""));
-        add(Pair.with(Pattern.compile("v(\\d)"), (k,v) -> String.format("new Vertex(%s)", v)));
+        add(Pair.with(Pattern.compile("v(\\d)"), (k,v) -> String.format("new Vertex(%s, \"vertex\")", v)));
         add(Pair.with(Pattern.compile("e\\[(.+)\\]"), (k,v) -> "\"1\""));
         add(Pair.with(Pattern.compile("d\\[(.*)\\]\\.?.*"), (k,v) -> v));
         add(Pair.with(Pattern.compile("m\\[(.*)\\]"), (k,v) -> v.replace('{','[').replace('}', ']')));
diff --git a/gremlin-python/build/generate.groovy b/gremlin-python/build/generate.groovy
index a633cd7..0c6dd42 100644
--- a/gremlin-python/build/generate.groovy
+++ b/gremlin-python/build/generate.groovy
@@ -87,7 +87,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
                     'from gremlin_python.process.traversal import TraversalStrategy\n' +
                     'from gremlin_python.process.graph_traversal import __\n' +
                     'from gremlin_python.structure.graph import Graph\n' +
-                    'from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, T, Pick, Operator, IO, WithOptions\n')
+                    'from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, Merge, T, Pick, Operator, IO, WithOptions\n')
 
     // Groovy can't process certain null oriented calls because it gets confused with the right overload to call
     // at runtime. using this approach for now as these are the only such situations encountered so far. a better
diff --git a/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py
index 525bc9d..c8e8ae4 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py
@@ -144,6 +144,16 @@ class GraphTraversalSource(object):
         traversal.bytecode.add_step("addV", *args)
         return traversal
 
+    def mergeV(self, *args):
+        traversal = self.get_graph_traversal()
+        traversal.bytecode.add_step("mergeV", *args)
+        return traversal
+
+    def mergeE(self, *args):
+        traversal = self.get_graph_traversal()
+        traversal.bytecode.add_step("mergeE", *args)
+        return traversal
+
     def inject(self, *args):
         traversal = self.get_graph_traversal()
         traversal.bytecode.add_step("inject", *args)
@@ -401,6 +411,14 @@ class GraphTraversal(Traversal):
         self.bytecode.add_step("mean", *args)
         return self
 
+    def mergeE(self, *args):
+        self.bytecode.add_step("mergeE", *args)
+        return self
+
+    def mergeV(self, *args):
+        self.bytecode.add_step("mergeV", *args)
+        return self
+
     def min_(self, *args):
         self.bytecode.add_step("min", *args)
         return self
@@ -827,6 +845,14 @@ class __(object, metaclass=MagicType):
         return cls.graph_traversal(None, None, Bytecode()).mean(*args)
 
     @classmethod
+    def mergeE(cls, *args):
+        return cls.graph_traversal(None, None, Bytecode()).mergeE(*args)
+
+    @classmethod
+    def mergeV(cls, *args):
+        return cls.graph_traversal(None, None, Bytecode()).mergeV(*args)
+
+    @classmethod
     def min_(cls, *args):
         return cls.graph_traversal(None, None, Bytecode()).min_(*args)
 
@@ -1254,6 +1280,14 @@ def mean(*args):
     return __.mean(*args)
 
 
+def mergeE(*args):
+    return __.mergeE(*args)
+
+
+def mergeV(*args):
+    return __.mergeV(*args)
+
+
 def min_(*args):
     return __.min_(*args)
 
@@ -1514,6 +1548,10 @@ statics.add_static('max_', max_)
 
 statics.add_static('mean', mean)
 
+statics.add_static('mergeE', mergeE)
+
+statics.add_static('mergeV', mergeV)
+
 statics.add_static('min_', min_)
 
 statics.add_static('not_', not_)
diff --git a/gremlin-python/src/main/python/gremlin_python/process/traversal.py b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
index f7bd31d..e24948c 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
@@ -152,6 +152,11 @@ GryoVersion = Enum('GryoVersion', ' V1_0 V3_0')
 statics.add_static('V1_0', GryoVersion.V1_0)
 statics.add_static('V3_0', GryoVersion.V3_0)
 
+Merge = Enum('Merge', ' onCreate onMatch')
+
+statics.add_static('onCreate', Merge.onCreate)
+statics.add_static('onMatch', Merge.onMatch)
+
 Order = Enum('Order', ' asc desc shuffle')
 
 statics.add_static('shuffle', Order.shuffle)
diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py
index a5a258a..f39197a 100644
--- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py
+++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py
@@ -33,8 +33,8 @@ from datetime import timedelta
 from gremlin_python import statics
 from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, TypeType, DictType, ListType, SetType, \
                                    SingleByte, ByteBufferType, GremlinType, SingleChar
-from gremlin_python.process.traversal import Barrier, Binding, Bytecode, Cardinality, Column, Direction, Operator, \
-                                             Order, Pick, Pop, P, Scope, TextP, Traversal, Traverser, \
+from gremlin_python.process.traversal import Barrier, Binding, Bytecode, Cardinality, Column, Direction, Merge, \
+                                             Operator, Order, Pick, Pop, P, Scope, TextP, Traversal, Traverser, \
                                              TraversalStrategy, T
 from gremlin_python.process.graph_traversal import GraphTraversal
 from gremlin_python.structure.graph import Graph, Edge, Property, Vertex, VertexProperty, Path
@@ -96,6 +96,7 @@ class DataType(Enum):
     tree = 0x2b                   # not supported - no tree object in Python yet
     metrics = 0x2c
     traversalmetrics = 0x2d
+    merge = 0x2e
     char = 0x80
     duration = 0x81
     inetaddress = 0x82            # todo
@@ -850,6 +851,11 @@ class PSerializer(_GraphBinaryTypeIO):
         return to_extend
 
 
+class MergeIO(_EnumIO):
+    graphbinary_type = DataType.merge
+    python_type = Merge
+
+
 class ScopeIO(_EnumIO):
     graphbinary_type = DataType.scope
     python_type = Scope
diff --git a/gremlin-python/src/main/python/radish/feature_steps.py b/gremlin-python/src/main/python/radish/feature_steps.py
index 66bfc45..6d0a72d 100644
--- a/gremlin-python/src/main/python/radish/feature_steps.py
+++ b/gremlin-python/src/main/python/radish/feature_steps.py
@@ -20,7 +20,7 @@
 import json
 import re
 from gremlin_python.statics import long
-from gremlin_python.structure.graph import Path
+from gremlin_python.structure.graph import Path, Vertex
 from gremlin_python.process.anonymous_traversal import traversal
 from gremlin_python.process.graph_traversal import __
 from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, T, Pick, Operator, IO, WithOptions
@@ -221,7 +221,11 @@ def __find_cached_element(ctx, graph_name, identifier, element_type):
     else:
         cache = ctx.lookup_v[graph_name] if element_type == "v" else ctx.lookup_e[graph_name]
 
-    return cache[identifier]
+    # try to lookup the element - if it can't be found then it must be a reference Vertex
+    if identifier in cache:
+        return cache[identifier]
+    else:
+        return Vertex(identifier)
 
 
 def _convert_results(val):
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 7f4a084..8f9368d 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -29,7 +29,7 @@ from gremlin_python.process.anonymous_traversal import traversal
 from gremlin_python.process.traversal import TraversalStrategy
 from gremlin_python.process.graph_traversal import __
 from gremlin_python.structure.graph import Graph
-from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, T, Pick, Operator, IO, WithOptions
+from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, Merge, T, Pick, Operator, IO, WithOptions
 
 world.gremlins = {
     'g_V_branchXlabel_eq_person__a_bX_optionXa__ageX_optionXb__langX_optionXb__nameX': [(lambda g, l1=None:g.V().branch(l1).option('a',__.age).option('b',__.lang).option('b',__.name))], 
@@ -804,7 +804,6 @@ world.gremlins = {
     'g_V_outXfollowedByX_group_byXsongTypeX_byXbothE_group_byXlabelX_byXweight_sumXX': [(lambda g:g.V().out('followedBy').group().by('songType').by(__.bothE().group().by(T.label).by(__.weight.sum_())))], 
     'g_V_groupXmX_byXnameX_byXinXknowsX_nameX_capXmX': [(lambda g:g.V().group('m').by('name').by(__.in_('knows').name).cap('m'))], 
     'g_V_group_byXlabelX_byXbothE_groupXaX_byXlabelX_byXweight_sumX_weight_sumX': [(lambda g:g.V().group().by(T.label).by(__.bothE().group('a').by(T.label).by(__.weight.sum_()).weight.sum_()))], 
-    'g_withSideEffectXa__marko_666_noone_blahX_V_groupXaX_byXnameX_byXoutE_label_foldX_capXaX': [(lambda g, xx1=None:g.withSideEffect('a',xx1).V().group('a').by('name').by(__.outE().label().fold()).cap('a').unfold().group().by(Column.keys).by(__.select(Column.values).order(Scope.local).by(Order.asc)))], 
     'g_V_hasLabelXpersonX_asXpX_outXcreatedX_group_byXnameX_byXselectXpX_valuesXageX_sumX': [(lambda g:g.V().hasLabel('person').as_('p').out('created').group().by('name').by(__.select('p').age.sum_()))], 
     'g_V_hasLabelXpersonX_asXpX_outXcreatedX_groupXaX_byXnameX_byXselectXpX_valuesXageX_sumX_capXaX': [(lambda g:g.V().hasLabel('person').as_('p').out('created').group('a').by('name').by(__.select('p').age.sum_()).cap('a'))], 
     'g_V_group_byXlabelX_byXlabel_countX': [(lambda g:g.V().group().by(__.label()).by(__.label().count()))], 
@@ -827,7 +826,7 @@ world.gremlins = {
     'g_V_hasXperson_name_markoX_bothXknowsX_groupCount_byXvaluesXnameX_foldX': [(lambda g:g.V().has('person','name','marko').both('knows').groupCount().by(__.name.fold()))], 
     'g_VX1X_out_injectXv2X_name': [(lambda g, vid1=None,v2=None:g.V(vid1).out().inject(v2).name)], 
     'g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path': [(lambda g, l1=None,vid1=None:g.V(vid1).out().name.inject('daniel').as_('a').map(l1).path())], 
-    'g_VX1X_injectXg_VX4XX_out_name': [(lambda g, vid1=None,v4=None:g.V(vid1).inject(v4).out().name)], 
+    'g_VX1X_injectXg_VX4XX_out_name': [(lambda g, vid1=None,v2=None:g.V(vid1).inject(v2).out().name)], 
     'g_injectXnull_1_3_nullX': [(lambda g:g.inject(None,1,3,None))], 
     'g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX': [(lambda g:g.inject(10,20,None,20,10,10).groupCount('x').dedup().as_('y').project('a','b').by().by(__.select('x').select(__.select('y'))))], 
     'g_injectXname_marko_age_nullX_selectXname_ageX': [(lambda g, xx1=None:g.inject(xx1).select('name','age'))], 
diff --git a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
index c931bc9..14b9577 100644
--- a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
+++ b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
@@ -44,6 +44,7 @@ class TestDriverRemoteConnection(object):
         assert long(6) == g.V().count().toList()[0]
         # #
         assert Vertex(1) == g.V(1).next()
+        assert Vertex(1) == g.V(Vertex(1)).next()
         assert 1 == g.V(1).id_().next()
         assert Traverser(Vertex(1)) == g.V(1).nextTraverser()
         assert 1 == len(g.V(1).toList())
diff --git a/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV1.py b/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV1.py
index 2f2b211..b12c48a 100644
--- a/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV1.py
+++ b/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV1.py
@@ -24,7 +24,7 @@ import math
 from gremlin_python.statics import timestamp, long, SingleByte, SingleChar, ByteBufferType
 from gremlin_python.structure.graph import Vertex, Edge, Property, VertexProperty, Path
 from gremlin_python.structure.io.graphbinaryV1 import GraphBinaryWriter, GraphBinaryReader
-from gremlin_python.process.traversal import Barrier, Binding, Bytecode
+from gremlin_python.process.traversal import Barrier, Binding, Bytecode, Merge
 
 
 class TestGraphBinaryReader(object):
@@ -166,6 +166,11 @@ class TestGraphSONWriter(object):
         output = self.graphbinary_reader.readObject(self.graphbinary_writer.writeObject(x))
         assert x == output
 
+    def test_merge(self):
+        x = Merge.onMatch
+        output = self.graphbinary_reader.readObject(self.graphbinary_writer.writeObject(x))
+        assert x == output
+
     def test_binding(self):
         x = Binding("name", "marko")
         output = self.graphbinary_reader.readObject(self.graphbinary_writer.writeObject(x))
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinBinaryRequestDecoder.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinBinaryRequestDecoder.java
index b05ae92..3af49f4 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinBinaryRequestDecoder.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinBinaryRequestDecoder.java
@@ -76,6 +76,7 @@ public class WsGremlinBinaryRequestDecoder extends MessageToMessageDecoder<Binar
             try {
                 objects.add(serializer.deserializeRequest(messageBytes.discardReadBytes()));
             } catch (SerializationException se) {
+                logger.warn(se.getMessage());
                 objects.add(RequestMessage.INVALID);
             }
         } finally {
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinTextRequestDecoder.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinTextRequestDecoder.java
index 50dc6f3..84b0888 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinTextRequestDecoder.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinTextRequestDecoder.java
@@ -63,6 +63,7 @@ public class WsGremlinTextRequestDecoder extends MessageToMessageDecoder<TextWeb
 
             objects.add(serializer.deserializeRequest(frame.text()));
         } catch (SerializationException se) {
+            logger.warn(se.getMessage());
             objects.add(RequestMessage.INVALID);
         }
     }
diff --git a/gremlin-test/features/sideEffect/Group.feature b/gremlin-test/features/sideEffect/Group.feature
index b8e7b5c..01a5a21 100644
--- a/gremlin-test/features/sideEffect/Group.feature
+++ b/gremlin-test/features/sideEffect/Group.feature
@@ -264,17 +264,17 @@ Feature: Step - group()
       | m[{"software":"d[2.0].d", "person":"d[5.0].d"}] |
 
   # The post-ordering really isn't really right but works around TINKERPOP-2600
-  Scenario: g_withSideEffectXa__marko_666_noone_blahX_V_groupXaX_byXnameX_byXoutE_label_foldX_capXaX
-    Given the modern graph
-    And using the parameter xx1 defined as "m[{\"marko\":l[\"666\"], \"noone\":l[\"blah\"]}]"
-    And the traversal of
-      """
-      g.withSideEffect("a", xx1).V().group("a").by("name").by(__.outE().label().fold()).cap("a").unfold().group().by(keys).by(select(values).order(Scope.local).by(Order.asc))
-      """
-    When iterated to list
-    Then the result should be unordered
-      | result |
-      | m[{"ripple":[], "peter":["created"], "noone":["blah"], "vadas":[], "josh":["created", "created"], "lop":[], "marko":["666", "created", "knows", "knows"]}] |
+#  Scenario: g_withSideEffectXa__marko_666_noone_blahX_V_groupXaX_byXnameX_byXoutE_label_foldX_capXaX
+#    Given the modern graph
+#    And using the parameter xx1 defined as "m[{\"marko\":\"l[\"666\"]\", \"noone\":\"l[\"blah\"]\"}]"
+#    And the traversal of
+#      """
+#      g.withSideEffect("a", xx1).V().group("a").by("name").by(__.outE().label().fold()).cap("a").unfold().group().by(Column.keys).by(select(Column.values).order(Scope.local).by(Order.asc))
+#      """
+#    When iterated to list
+#    Then the result should be unordered
+#      | result |
+#      | m[{"ripple":[], "peter":["created"], "noone":["blah"], "vadas":[], "josh":["created", "created"], "lop":[], "marko":["666", "created", "knows", "knows"]}] |
 
   @GraphComputerVerificationStarGraphExceeded
   Scenario: g_V_hasLabelXpersonX_asXpX_outXcreatedX_group_byXnameX_byXselectXpX_valuesXageX_sumX
diff --git a/gremlin-test/features/sideEffect/Inject.feature b/gremlin-test/features/sideEffect/Inject.feature
index a186095..75edc15 100644
--- a/gremlin-test/features/sideEffect/Inject.feature
+++ b/gremlin-test/features/sideEffect/Inject.feature
@@ -51,13 +51,14 @@ Feature: Step - inject()
       | p[v[marko],v[vadas],vadas,d[5].i] |
       | p[v[marko],v[josh],josh,d[4].i] |
 
+  @GraphComputerVerificationInjectionNotSupported
   Scenario: g_VX1X_injectXg_VX4XX_out_name
     Given the modern graph
     And using the parameter vid1 defined as "v[marko].id"
-    And using the parameter v4 defined as "v[josh]"
+    And using the parameter v2 defined as "v[josh]"
     And the traversal of
       """
-      g.V(vid1).inject(v4).out().values("name")
+      g.V(vid1).inject(v2).out().values("name")
       """
     When iterated to list
     Then the result should be unordered
@@ -130,7 +131,6 @@ Feature: Step - inject()
       | result |
       | null |
 
-
   Scenario: g_inject
     Given the empty graph
     And the traversal of
@@ -140,7 +140,6 @@ Feature: Step - inject()
     When iterated to list
     Then the result should be empty
 
-  @GraphComputerVerificationInjectionNotSupported
   Scenario: g_VX1X_valuesXageX_injectXnull_nullX
     Given the modern graph
     And using the parameter xx1 defined as "v[marko].id"
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
index ea5eed3..d896228 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
@@ -125,7 +125,7 @@ public final class StepDefinition {
         add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.sid"), s -> g.V().has("name", s).id().next().toString()));
         add(Pair.with(Pattern.compile("v\\[(.+)\\]"), s -> {
             final Iterator<Object> itty = g.V().has("name", s).id();
-            return String.format("new Vertex(\"%s\",\"%s\")", itty.hasNext() ? itty.next() : s, Vertex.DEFAULT_LABEL);
+            return String.format("new Vertex(%s,\"%s\")", itty.hasNext() ? itty.next() : s, Vertex.DEFAULT_LABEL);
         }));
         add(Pair.with(Pattern.compile("e\\[(.+)\\]\\.id"), s -> getEdgeIdString(g, s)));
         add(Pair.with(Pattern.compile("e\\[(.+)\\]\\.sid"), s -> getEdgeIdString(g, s)));
@@ -138,9 +138,6 @@ public final class StepDefinition {
         add(Pair.with(Pattern.compile("c\\[(.*)\\]"), s -> {
             throw new AssumptionViolatedException("This test uses a lambda as a parameter which is not supported by gremlin-language");
         }));
-        add(Pair.with(Pattern.compile("v\\[(.+)\\]"), s -> {
-            throw new AssumptionViolatedException("This test uses a Vertex as a parameter which is not supported by gremlin-language");
-        }));
         add(Pair.with(Pattern.compile("e\\[(.+)\\]"), s -> {
             throw new AssumptionViolatedException("This test uses a Edge as a parameter which is not supported by gremlin-language");
         }));
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/TinkerMergeVertexStep.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/TinkerMergeVertexStep.java
index 5876879..6771ebd 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/TinkerMergeVertexStep.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/map/TinkerMergeVertexStep.java
@@ -42,6 +42,7 @@ public class TinkerMergeVertexStep<S> extends MergeVertexStep<S> {
         super(step.getTraversal(), step.isStart(), step.getSearchCreateTraversal());
         if (step.getOnMatchTraversal() != null) this.addChildOption(Merge.onMatch, step.getOnMatchTraversal());
         if (step.getOnCreateTraversal() != null) this.addChildOption(Merge.onCreate, step.getOnCreateTraversal());
+        if (step.getCallbackRegistry() != null) this.callbackRegistry = step.getCallbackRegistry();
     }
 
     @Override
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerGraphWorld.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerGraphWorld.java
index 7d6f820..46856f8 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerGraphWorld.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerGraphWorld.java
@@ -106,6 +106,7 @@ public class TinkerGraphWorld implements World {
 
         private static final List<String> TAGS_TO_IGNORE = Arrays.asList(
                 "@StepDrop",
+                "@StepInject",
                 "@StepV",
                 "@GraphComputerVerificationOneBulk",
                 "@GraphComputerVerificationStrategyNotSupported",