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/24 19:15:48 UTC

[tinkerpop] branch TINKERPOP-2681 updated: added zero-arg mergeE/V()

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


The following commit(s) were added to refs/heads/TINKERPOP-2681 by this push:
     new 7c39939  added zero-arg mergeE/V()
7c39939 is described below

commit 7c399397ca5200111538f3398f40039c77bc008b
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Mon Jan 24 14:15:26 2022 -0500

    added zero-arg mergeE/V()
---
 .../language/grammar/GremlinBaseVisitor.java       | 10 +++++
 .../language/grammar/TraversalMethodVisitor.java   | 10 +++++
 .../traversal/dsl/graph/GraphTraversal.java        | 26 ++++++++++++
 .../gremlin/process/traversal/dsl/graph/__.java    | 14 +++++++
 .../process/traversal/step/map/MergeEdgeStep.java  |  5 +++
 .../traversal/step/map/MergeVertexStep.java        |  5 +++
 .../Process/Traversal/GraphTraversal.cs            | 18 ++++++++
 .../src/Gremlin.Net/Process/Traversal/__.cs        | 48 ++++++++++++++++++++++
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  2 +
 .../gremlin-javascript/test/cucumber/gremlin.js    |  2 +
 gremlin-language/src/main/antlr4/Gremlin.g4        |  6 ++-
 gremlin-python/src/main/python/radish/gremlin.py   |  2 +
 gremlin-test/features/map/MergeEdge.feature        | 19 ++++++++-
 gremlin-test/features/map/MergeVertex.feature      | 18 ++++++++
 14 files changed, 182 insertions(+), 3 deletions(-)

diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
index e987683..8cc7807 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
@@ -1584,4 +1584,14 @@ public class GremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> implement
 	public T visitTraversalSourceSpawnMethod_mergeE_Traversal(final GremlinParser.TraversalSourceSpawnMethod_mergeE_TraversalContext ctx) {
 		notImplemented(ctx); return null;
 	}
+
+	@Override
+	public T visitTraversalMethod_mergeV_empty(final GremlinParser.TraversalMethod_mergeV_emptyContext ctx) {
+		notImplemented(ctx); return null;
+	}
+
+	@Override
+	public T visitTraversalMethod_mergeE_empty(final GremlinParser.TraversalMethod_mergeE_emptyContext ctx) {
+		notImplemented(ctx); return null;
+	}
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
index c89ed86..e4eaa4d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
@@ -100,6 +100,16 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
     }
 
     @Override
+    public Traversal visitTraversalMethod_mergeV_empty(final GremlinParser.TraversalMethod_mergeV_emptyContext ctx) {
+        return this.graphTraversal.mergeV();
+    }
+
+    @Override
+    public Traversal visitTraversalMethod_mergeE_empty(final GremlinParser.TraversalMethod_mergeE_emptyContext ctx) {
+        return this.graphTraversal.mergeE();
+    }
+
+    @Override
     public GraphTraversal visitTraversalMethod_mergeE_Map(final GremlinParser.TraversalMethod_mergeE_MapContext ctx) {
         return this.graphTraversal.mergeE(GenericLiteralVisitor.getMapLiteral(ctx.genericLiteralMap()));
     }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
index 9b0bdc5..53eb732 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
@@ -1077,6 +1077,20 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
+     * Performs a merge (i.e. upsert) style operation for an {@link Vertex} using the incoming {@code Map} traverser as
+     * an argument. The {@code Map} represents search criteria and will match each of the supplied key/value pairs where
+     * the keys may be {@code String} property values or a value of {@link T}. If a match is not made it will use that
+     * search criteria to create the new {@link Vertex}.
+     *
+     * @since 3.6.0
+     */
+    public default GraphTraversal<S, Vertex> mergeV() {
+        this.asAdmin().getBytecode().addStep(Symbols.mergeV);
+        final MergeVertexStep<S> step = new MergeVertexStep<>(this.asAdmin(), false);
+        return this.asAdmin().addStep(step);
+    }
+
+    /**
      * Performs a merge (i.e. upsert) style operation for an {@link Vertex} using a {@code Map} as an argument.
      * The {@code Map} represents search criteria and will match each of the supplied key/value pairs where the keys
      * may be {@code String} property values or a value of {@link T}. If a match is not made it will use that search
@@ -1108,6 +1122,18 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
+     * Spawns a {@link GraphTraversal} by doing a merge (i.e. upsert) style operation for an {@link Edge} using an
+     * incoming {@code Map} as an argument.
+     *
+     * @since 3.6.0
+     */
+    public default GraphTraversal<S, Edge> mergeE() {
+        this.asAdmin().getBytecode().addStep(Symbols.mergeE);
+        final MergeEdgeStep<S> step = new MergeEdgeStep(this.asAdmin(), false);
+        return this.asAdmin().addStep(step);
+    }
+
+    /**
      * Spawns a {@link GraphTraversal} by doing a merge (i.e. upsert) style operation for an {@link Edge} using a
      * {@code Map} as an argument.
      *
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
index 01e5c0f..6e6374f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
@@ -516,6 +516,13 @@ public class __ {
     }
 
     /**
+     * @see GraphTraversal#mergeV()
+     */
+    public static <A> GraphTraversal<A, Vertex> mergeV() {
+        return __.<A>start().mergeV();
+    }
+
+    /**
      * @see GraphTraversal#mergeV(Map)
      */
     public static <A> GraphTraversal<A, Vertex> mergeV(final Map<Object, Object> searchCreate) {
@@ -544,6 +551,13 @@ public class __ {
     }
 
     /**
+     * @see GraphTraversal#mergeE()
+     */
+    public static <A> GraphTraversal<A, Edge> mergeE() {
+        return __.<A>start().mergeE();
+    }
+
+    /**
      * @see GraphTraversal#mergeE(Map)
      */
     public static <A> GraphTraversal<A, Edge> mergeE(final Map<?, Object> searchCreate) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
index e5e4a19..ded688c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
@@ -67,6 +68,10 @@ public class MergeEdgeStep<S> extends FlatMapStep<S, Edge> implements Mutating<E
 
     private CallbackRegistry<Event> callbackRegistry;
 
+    public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart) {
+        this(traversal, isStart, new IdentityTraversal<>());
+    }
+
     public MergeEdgeStep(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/step/map/MergeVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
index 365d83a..d000ca3 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
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
@@ -62,6 +63,10 @@ public class MergeVertexStep<S> extends FlatMapStep<S, Vertex> implements Mutati
 
     protected CallbackRegistry<Event> callbackRegistry;
 
+    public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart) {
+        this(traversal, isStart, new IdentityTraversal());
+    }
+
     public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Map<Object, Object> searchCreate) {
         this(traversal, isStart, new ConstantTraversal<>(searchCreate));
     }
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index 59ecb1a..03521f0 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -1092,6 +1092,15 @@ namespace Gremlin.Net.Process.Traversal
         /// <summary>
         ///     Adds the mergeE step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
+        public GraphTraversal<S, Edge> MergeE ()
+        {
+            Bytecode.AddStep("mergeE");
+            return Wrap<S, Edge>(this);
+        }
+
+        /// <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);
@@ -1110,6 +1119,15 @@ namespace Gremlin.Net.Process.Traversal
         /// <summary>
         ///     Adds the mergeV step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
+        public GraphTraversal<S, Vertex> MergeV ()
+        {
+            Bytecode.AddStep("mergeV");
+            return Wrap<S, Vertex>(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);
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
index acad588..62c19e0 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
@@ -812,6 +812,54 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the mergeE step to that traversal.
+        /// </summary>
+        public static GraphTraversal<object, Edge> MergeE ()
+        {
+            return new GraphTraversal<object, Edge>().MergeE();
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the mergeE step to that traversal.
+        /// </summary>
+        public static GraphTraversal<object, Edge> MergeE (IDictionary<object,object> m)
+        {
+            return new GraphTraversal<object, Edge>().MergeE(m);
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the mergeE step to that traversal.
+        /// </summary>
+        public static GraphTraversal<object, Edge> MergeE (ITraversal t)
+        {
+            return new GraphTraversal<object, Edge>().MergeE(t);
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the mergeV step to that traversal.
+        /// </summary>
+        public static GraphTraversal<object, Vertex> MergeV ()
+        {
+            return new GraphTraversal<object, Vertex>().MergeV();
+        }
+
+        /// <summary>
+        ///     Adds the mergeV step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public static GraphTraversal<object, Vertex> MergeV (IDictionary<object,object> m)
+        {
+            return new GraphTraversal<object, Vertex>().MergeV(m);
+        }
+
+        /// <summary>
+        ///     Adds the mergeV step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public static GraphTraversal<object, Vertex> MergeV (ITraversal t)
+        {
+            return new GraphTraversal<object, Vertex>().MergeV(t);
+        }
+
+        /// <summary>
         ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the min step to that traversal.
         /// </summary>
         public static GraphTraversal<object, E2> Min<E2>()
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 4ff7bbf..6a2f5ef 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -509,6 +509,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"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_injectXlabel_knows_out_marko_in_vadasX_mergeE", 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.Inject(p["xx1"]).MergeE(), (g,p) =>g.V().Has("person","name","marko").Out("knows").Has("person","name","vadas")}}, 
                {"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)}}, 
@@ -524,6 +525,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"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_injectXlabel_person_name_marko_label_person_name_stephenX_mergeV", 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(), (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>()}}, 
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 6af6338..2d6db86 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
@@ -498,6 +498,7 @@ const gremlins = {
     g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated: [function({g, xx1, xx3, xx2}) { return 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") }, function({g, xx1, xx3, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) }, function({g, xx1, xx3, xx2}) { retu [...]
     g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated: [function({g, xx1, xx3, xx2}) { return 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") }, function({g, xx1, xx3, xx2}) { return g.V().has("person","name","marko").mergeE(xx1).option(Merge.onCre [...]
     g_VX100X_VX101X_mergeEXlabel_knows_out_marko_in_vadasX: [function({g, xx1}) { return g.addV("person").property(T.id,100).property("name","marko") }, function({g, xx1}) { return g.V(100).V(101).mergeE(xx1) }, function({g, xx1}) { return g.V() }, function({g, xx1}) { return g.E() }], 
+    g_injectXlabel_knows_out_marko_in_vadasX_mergeE: [function({g, xx1}) { return g.addV("person").property(T.id,100).property("name","marko").addV("person").property(T.id,101).property("name","vadas") }, function({g, xx1}) { return g.inject(xx1).mergeE() }, function({g, xx1}) { return g.V().has("person","name","marko").out("knows").has("person","name","vadas") }], 
     g_mergeVXlabel_person_name_stephenX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.mergeV(xx1) }, function({g, xx1}) { return g.V().has("person","name","stephen") }], 
     g_mergeVXlabel_person_name_markoX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.mergeV(xx1) }, function({g, xx1}) { return g.V().has("person","name","marko") }], 
     g_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.mergeV(xx1).option(Merge.onCreate,xx2) }, function({g, xx1, xx2}) { return g.V().has("person","name","stephen").has("age",19) }], 
@@ -513,6 +514,7 @@ const gremlins = {
     g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.withSideEffect("c",xx1).withSideEffect("m",xx2).inject(0).mergeV(__.select("c")).option(Merge.onMatch,__.select("m")) }, function({g, xx1, xx2}) { return g.V().has("person","name","marko").has("age",19) }], 
     g_injectX0X_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1}) { return g.inject(0).mergeV(xx1).property("name","vadas","acl","public") }, function({g, xx1}) { return g.V().properties("name").hasValue("vadas").has("acl","public") }], 
     g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.inject(xx1,xx2).mergeV(__.identity()) }, function({g, xx1, xx2}) { return g.V().has("person","name","stephen") }, function({g, xx1, xx2}) { return g.V().has("person","name","marko") }, function({g, xx1, xx2}) { return g.V() }], 
+    g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeV: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29).as("marko") }, function({g, xx1, xx2}) { return g.inject(xx1,xx2).mergeV() }, function({g, xx1, xx2}) { return g.V().has("person","name","stephen") }, function({g, xx1, xx2}) { return g.V().has("person","name","marko") }, function({g, xx1, xx2}) { return g.V() }], 
     g_V_age_min: [function({g}) { return g.V().values("age").min() }], 
     g_V_foo_min: [function({g}) { return g.V().values("foo").min() }], 
     g_V_name_min: [function({g}) { return g.V().values("name").min() }], 
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4
index 242b79c..7095e7b 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -287,12 +287,14 @@ traversalMethod_addV
 	;
 
 traversalMethod_mergeV
-    : 'mergeV' LPAREN genericLiteralMap RPAREN #traversalMethod_mergeV_Map
+    : 'mergeV' LPAREN RPAREN #traversalMethod_mergeV_empty
+    | 'mergeV' LPAREN genericLiteralMap RPAREN #traversalMethod_mergeV_Map
     | 'mergeV' LPAREN nestedTraversal RPAREN #traversalMethod_mergeV_Traversal
     ;
 
 traversalMethod_mergeE
-    : 'mergeE' LPAREN genericLiteralMap RPAREN #traversalMethod_mergeE_Map
+    : 'mergeE' LPAREN RPAREN #traversalMethod_mergeE_empty
+    | 'mergeE' LPAREN genericLiteralMap RPAREN #traversalMethod_mergeE_Map
     | 'mergeE' LPAREN nestedTraversal RPAREN #traversalMethod_mergeE_Traversal
     ;
 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 8f9368d..16ee2a2 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -482,6 +482,7 @@ world.gremlins = {
     'g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated': [(lambda g, xx1=None,xx3=None,xx2=None: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')), (lambda g, xx1=None,xx3=None,xx2=None:g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3)), (lambda g, xx1=None,xx3=None,xx [...]
     'g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated': [(lambda g, xx1=None,xx3=None,xx2=None: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')), (lambda g, xx1=None,xx3=None,xx2=None:g.V().has('person','name','marko').mergeE(xx1).option(Merge. [...]
     'g_VX100X_VX101X_mergeEXlabel_knows_out_marko_in_vadasX': [(lambda g, xx1=None:g.addV('person').property(T.id_,100).property('name','marko')), (lambda g, xx1=None:g.V(100).V(101).mergeE(xx1)), (lambda g, xx1=None:g.V()), (lambda g, xx1=None:g.E())], 
+    'g_injectXlabel_knows_out_marko_in_vadasX_mergeE': [(lambda g, xx1=None:g.addV('person').property(T.id_,100).property('name','marko').addV('person').property(T.id_,101).property('name','vadas')), (lambda g, xx1=None:g.inject(xx1).mergeE()), (lambda g, xx1=None:g.V().has('person','name','marko').out('knows').has('person','name','vadas'))], 
     'g_mergeVXlabel_person_name_stephenX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.mergeV(xx1)), (lambda g, xx1=None:g.V().has('person','name','stephen'))], 
     'g_mergeVXlabel_person_name_markoX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.mergeV(xx1)), (lambda g, xx1=None:g.V().has('person','name','marko'))], 
     'g_mergeVXlabel_person_name_stephenX_optionXonCreate_label_person_name_stephen_age_19X_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.mergeV(xx1).option(Merge.onCreate,xx2)), (lambda g, xx1=None,xx2=None:g.V().has('person','name','stephen').has('age',19))], 
@@ -497,6 +498,7 @@ world.gremlins = {
     'g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_injectX0X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.withSideEffect('c',xx1).withSideEffect('m',xx2).inject(0).mergeV(__.select('c')).option(Merge.onMatch,__.select('m'))), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko').has('age',19))], 
     'g_injectX0X_mergeVXlabel_person_name_markoX_propertyXname_vadas_acl_publicX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None:g.inject(0).mergeV(xx1).property('name','vadas','acl','public')), (lambda g, xx1=None:g.V().properties('name').hasValue('vadas').has('acl','public'))], 
     'g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeVXidentityX': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.inject(xx1,xx2).mergeV(__.identity())), (lambda g, xx1=None,xx2=None:g.V().has('person','name','stephen')), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko')), (lambda g, xx1=None,xx2=None:g.V())], 
+    'g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeV': [(lambda g, xx1=None,xx2=None:g.addV('person').property('name','marko').property('age',29).as_('marko')), (lambda g, xx1=None,xx2=None:g.inject(xx1,xx2).mergeV()), (lambda g, xx1=None,xx2=None:g.V().has('person','name','stephen')), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko')), (lambda g, xx1=None,xx2=None:g.V())], 
     'g_V_age_min': [(lambda g:g.V().age.min_())], 
     'g_V_foo_min': [(lambda g:g.V().foo.min_())], 
     'g_V_name_min': [(lambda g:g.V().name.min_())], 
diff --git a/gremlin-test/features/map/MergeEdge.feature b/gremlin-test/features/map/MergeEdge.feature
index 65173ce..a04ca40 100644
--- a/gremlin-test/features/map/MergeEdge.feature
+++ b/gremlin-test/features/map/MergeEdge.feature
@@ -181,4 +181,21 @@ Feature: Step - mergeE()
     When iterated to list
     Then the result should have a count of 0
     And the graph should return 1 for count of "g.V()"
-    And the graph should return 0 for count of "g.E()"
\ No newline at end of file
+    And the graph should return 0 for count of "g.E()"
+
+  @AllowUserSuppliedIds
+  Scenario: g_injectXlabel_knows_out_marko_in_vadasX_mergeE
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property(T.id, 100).property("name", "marko").
+        addV("person").property(T.id, 101).property("name", "vadas")
+      """
+    And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[100]\", \"D[IN]\":\"v[101]\"}]"
+    And the traversal of
+      """
+      g.inject(xx1).mergeE()
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").out(\"knows\").has(\"person\",\"name\",\"vadas\")"
\ No newline at end of file
diff --git a/gremlin-test/features/map/MergeVertex.feature b/gremlin-test/features/map/MergeVertex.feature
index 385f2f8..cda2e42 100644
--- a/gremlin-test/features/map/MergeVertex.feature
+++ b/gremlin-test/features/map/MergeVertex.feature
@@ -263,3 +263,21 @@ Feature: Step - mergeV()
     And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\")"
     And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\")"
     And the graph should return 2 for count of "g.V()"
+
+  Scenario: g_injectXlabel_person_name_marko_label_person_name_stephenX_mergeV
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name", "marko").property("age", 29).as("marko")
+      """
+    And using the parameter xx1 defined as "m[{\"t[label]\": \"person\", \"name\":\"marko\"}]"
+    And using the parameter xx2 defined as "m[{\"t[label]\": \"person\", \"name\":\"stephen\"}]"
+    And the traversal of
+      """
+      g.inject(xx1, xx2).mergeV()
+      """
+    When iterated to list
+    Then the result should have a count of 2
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\")"
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\")"
+    And the graph should return 2 for count of "g.V()"
\ No newline at end of file