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 2337b5a96b18600b3b684da09c2fe22983d31975 Author: Stephen Mallette <stepm...@amazon.com> AuthorDate: Sat Feb 5 09:52:48 2022 -0500 Prevent creation of vertices if they do not exist for mergeE() --- .../process/traversal/step/map/MergeEdgeStep.java | 28 +++++++++++---- .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 5 +-- .../gremlin-javascript/test/cucumber/gremlin.js | 5 +-- gremlin-python/src/main/python/radish/gremlin.py | 5 +-- gremlin-test/features/map/MergeEdge.feature | 40 ++++++++++++++++------ .../process/traversal/step/map/MergeEdgeTest.java | 17 +++++---- 6 files changed, 71 insertions(+), 29 deletions(-) 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 94d4273..7734f95 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 @@ -279,6 +279,13 @@ public class MergeEdgeStep<S> extends FlatMapStep<S, Edge> implements Mutating<E // searchCreate should have alredy been validated so only do it if it is overridden if (useOnCreate) validateMapInput(m, false); + // check if from/to were already determined by traverser/searchMatch, and if not, then at least ensure that + // the from/to is set in m + if (outV == PLACEHOLDER_VERTEX && !m.containsKey(Direction.OUT)) + throw new IllegalArgumentException("Out Vertex not specified - edge cannot be created"); + if (inV == PLACEHOLDER_VERTEX && !m.containsKey(Direction.IN)) + throw new IllegalArgumentException("In Vertex not specified - edge cannot be created"); + final List<Object> keyValues = new ArrayList<>(); String label = Edge.DEFAULT_LABEL; @@ -292,11 +299,9 @@ public class MergeEdgeStep<S> extends FlatMapStep<S, Edge> implements Mutating<E // only override if onCreate was specified otherwise stick to however traverser/searchMatch // was resolved if (useOnCreate && entry.getKey().equals(Direction.IN)) { - toV = ((Attachable<Vertex>) entry.getValue()) - .attach(Attachable.Method.getOrCreate(this.getTraversal().getGraph().orElse(EmptyGraph.instance()))); + toV = tryAttachVertex((Attachable<Vertex>) entry.getValue()); } else if (useOnCreate && entry.getKey().equals(Direction.OUT)) { - fromV = ((Attachable<Vertex>) entry.getValue()) - .attach(Attachable.Method.getOrCreate(this.getTraversal().getGraph().orElse(EmptyGraph.instance()))); + fromV = tryAttachVertex((Attachable<Vertex>) entry.getValue()); } } else if (entry.getKey().equals(T.label)) { label = (String) entry.getValue(); @@ -335,14 +340,25 @@ public class MergeEdgeStep<S> extends FlatMapStep<S, Edge> implements Mutating<E final Object o = searchCreate.getOrDefault(direction, possibleVertex); final Vertex v = o instanceof Vertex ? (Vertex) o : new ReferenceVertex(o); if (v != PLACEHOLDER_VERTEX && v instanceof Attachable) { - return ((Attachable<Vertex>) v) - .attach(Attachable.Method.getOrCreate(this.getTraversal().getGraph().orElse(EmptyGraph.instance()))); + return tryAttachVertex((Attachable<Vertex>) v); } else { return v; } } /** + * Tries to attach a {@link Vertex} to its host {@link Graph} of the traversal. If the {@link Vertex} cannot be + * found then an {@code IllegalArgumentException} is expected. + */ + protected Vertex tryAttachVertex(final Attachable<Vertex> attachable) { + try { + return attachable.attach(Attachable.Method.get(this.getTraversal().getGraph().orElse(EmptyGraph.instance()))); + } catch (IllegalStateException ise) { + throw new IllegalArgumentException(String.format("%s could not be found and edge could not be created", attachable)); + } + } + + /** * Validates input to any {@code Map} arguments to this step. For {@link Merge#onMatch} updates cannot be applied * to immutable parts of an {@link Edge} (id, label, incident vertices) so those can be ignored in the validation. */ diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index d600ff9..95c4069 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -505,8 +505,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_withSideEffectXa_label_knows_out_marko_in_vadasX_mergeEXselectXaXX", 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.WithSideEffect("a",p["xx1"]).MergeE((IDictionary<object,object>) __.Select<object>("a")), (g,p) =>g.V().Has("person","name","marko").Out("knows").Has("person","name","vadas")}}, {"g_mergeEXlabel_knows_out_marko1_in_vadas1X", 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_vadas_weight_05X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"])}}, + {"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_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 [...] @@ -519,6 +519,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_withSideEffectXc_created_YX_withSideEffectXm_matchedX_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_selectXcXX_optionXonMatch_selectXmXX", 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"), (g,p) =>g.WithSideEffect("c",p["xx2"]).WithSideEffect("m",p["xx3"]).MergeE((IDictionary<object,object [...] {"g_V_mapXmergeEXlabel_self_weight_05XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (g,p) =>g.V().Map<object>(__.MergeE((IDictionary<object,object>) p["xx1"])), (g,p) =>g.E(), (g,p) =>g.V(), (g,p) =>g.V().Has("person","name","marko").As("a").OutE("self").Has("weight",0.5).InV().Where(P.Eq("a"))}}, {"g_mergeEXlabel_knows_out_marko_in_vadasX_aliased_direction", 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_injectXlabel_knows_out_marko_in_vadas_label_self_out_vadas_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.Inject(p["xx1"],p["xx2"]).MergeE(), (g,p) =>g.V(), (g,p) =>g.E(), (g,p) =>g.V().Has("person","name","marko").Out("knows").Has("person","name","vadas"), (g,p) =>g.V().Has("person","name [...] {"g_mergeVXlabel_person_name_stephenX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (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), (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), (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)}}, 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 cfc914c..58b4316 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 @@ -496,8 +496,8 @@ const gremlins = { g_withSideEffectXa_label_knows_out_marko_in_vadasX_mergeEXselectXaXX: [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.withSideEffect("a",xx1).mergeE(__.select("a")) }, function({g, xx1}) { return g.V().has("person","name","marko").out("knows").has("person","name","vadas") }], g_mergeEXlabel_knows_out_marko1_in_vadas1X: [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.mergeE(xx1) }, function({g, xx1}) { return g.V().has("person","name","marko").out("knows").has("person","name","vadas") }], g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists: [function({g, xx1}) { 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") }, function({g, xx1}) { return g.mergeE(xx1) }, function({g, xx1}) { return g.V().has("person","name","marko").outE("knows").has("weight",0.5).inV().has("person","name","vadas") }, function({g, xx1}) { return g.V().has("person","na [...] - g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X: [function({g, xx1}) { return g.mergeE(xx1) }, function({g, xx1}) { return g.V() }, function({g, xx1}) { return g.E().hasLabel("knows").has("weight",0.5) }], - g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX: [function({g, xx1, xx3, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) }, function({g, xx1, xx3, xx2}) { return g.V() }, function({g, xx1, xx3, xx2}) { return g.E().hasLabel("knows").has("created","Y") }, function({g, xx1, xx3, xx2}) { return g.E().hasLabel("knows").has("created","N") }], + g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X: [function({g, xx1}) { return g.mergeE(xx1) }], + g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX: [function({g, xx1, xx3, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) }], g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists: [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") }, function({g, xx1, xx3, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) }, function({g, xx1, xx3, xx2}) { return g.V() }, function({g, xx1, xx [...] 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 [...] @@ -510,6 +510,7 @@ const gremlins = { g_withSideEffectXc_created_YX_withSideEffectXm_matchedX_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_selectXcXX_optionXonMatch_selectXmXX: [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") }, function({g, xx1, xx3, xx2}) { return g.withSideEffect("c",xx2).withSideEffect("m",xx3).mergeE(xx1).option(Merge.onCreate,__.select("c")).option(Merge.onMatch,__ [...] g_V_mapXmergeEXlabel_self_weight_05XX: [function({g, xx1}) { return g.addV("person").property("name","marko").property("age",29) }, function({g, xx1}) { return g.V().map(__.mergeE(xx1)) }, function({g, xx1}) { return g.E() }, function({g, xx1}) { return g.V() }, function({g, xx1}) { return g.V().has("person","name","marko").as("a").outE("self").has("weight",0.5).inV().where(P.eq("a")) }], g_mergeEXlabel_knows_out_marko_in_vadasX_aliased_direction: [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.mergeE(xx1) }, function({g, xx1}) { return g.V().has("person","name","marko").out("knows").has("person","name","vadas") }], + g_injectXlabel_knows_out_marko_in_vadas_label_self_out_vadas_in_vadasX: [function({g, xx1, xx2}) { return g.addV("person").property(T.id,100).property("name","marko").addV("person").property(T.id,101).property("name","vadas") }, function({g, xx1, xx2}) { return g.inject(xx1,xx2).mergeE() }, function({g, xx1, xx2}) { return g.V() }, function({g, xx1, xx2}) { return g.E() }, function({g, xx1, xx2}) { 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) }, 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) }, 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) }, 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) }], diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 761ad00..0199247 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -478,8 +478,8 @@ world.gremlins = { 'g_withSideEffectXa_label_knows_out_marko_in_vadasX_mergeEXselectXaXX': [(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.withSideEffect('a',xx1).mergeE(__.select('a'))), (lambda g, xx1=None:g.V().has('person','name','marko').out('knows').has('person','name','vadas'))], 'g_mergeEXlabel_knows_out_marko1_in_vadas1X': [(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.mergeE(xx1)), (lambda g, xx1=None:g.V().has('person','name','marko').out('knows').has('person','name','vadas'))], 'g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists': [(lambda g, xx1=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')), (lambda g, xx1=None:g.mergeE(xx1)), (lambda g, xx1=None:g.V().has('person','name','marko').outE('knows').has('weight',float(0.5)).inV().has('person','name','vadas')), (lambda g, xx1=None:g.V().has('person','name','marko').out('know [...] - 'g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X': [(lambda g, xx1=None:g.mergeE(xx1)), (lambda g, xx1=None:g.V()), (lambda g, xx1=None:g.E().hasLabel('knows').has('weight',float(0.5)))], - 'g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX': [(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,xx2=None:g.V()), (lambda g, xx1=None,xx3=None,xx2=None:g.E().hasLabel('knows').has('created','Y')), (lambda g, xx1=None,xx3=None,xx2=None:g.E().hasLabel('knows').has('created','N'))], + 'g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X': [(lambda g, xx1=None:g.mergeE(xx1))], + 'g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX': [(lambda g, xx1=None,xx3=None,xx2=None:g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3))], 'g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists': [(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')), (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,xx2=None:g.V()), (lambda g, xx1=No [...] '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. [...] @@ -492,6 +492,7 @@ world.gremlins = { 'g_withSideEffectXc_created_YX_withSideEffectXm_matchedX_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_selectXcXX_optionXonMatch_selectXmXX': [(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')), (lambda g, xx1=None,xx3=None,xx2=None:g.withSideEffect('c',xx2).withSideEffect('m',xx3).mergeE(xx1).option(Merge.onCreate,__.select('c')).option(Merge.onMat [...] 'g_V_mapXmergeEXlabel_self_weight_05XX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29)), (lambda g, xx1=None:g.V().map(__.mergeE(xx1))), (lambda g, xx1=None:g.E()), (lambda g, xx1=None:g.V()), (lambda g, xx1=None:g.V().has('person','name','marko').as_('a').outE('self').has('weight',float(0.5)).inV().where(P.eq('a')))], 'g_mergeEXlabel_knows_out_marko_in_vadasX_aliased_direction': [(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.mergeE(xx1)), (lambda g, xx1=None:g.V().has('person','name','marko').out('knows').has('person','name','vadas'))], + 'g_injectXlabel_knows_out_marko_in_vadas_label_self_out_vadas_in_vadasX': [(lambda g, xx1=None,xx2=None:g.addV('person').property(T.id_,100).property('name','marko').addV('person').property(T.id_,101).property('name','vadas')), (lambda g, xx1=None,xx2=None:g.inject(xx1,xx2).mergeE()), (lambda g, xx1=None,xx2=None:g.V()), (lambda g, xx1=None,xx2=None:g.E()), (lambda g, xx1=None,xx2=None:g.V().has('person','name','marko').out('knows').has('person','name','vadas')), (lambda g, xx1=None, [...] 'g_mergeVXlabel_person_name_stephenX': [(lambda g, xx1=None:g.addV('person').property('name','marko').property('age',29)), (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)), (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)), (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))], diff --git a/gremlin-test/features/map/MergeEdge.feature b/gremlin-test/features/map/MergeEdge.feature index 4d9304a..b696e05 100644 --- a/gremlin-test/features/map/MergeEdge.feature +++ b/gremlin-test/features/map/MergeEdge.feature @@ -44,11 +44,11 @@ Feature: Step - mergeE() # g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X # - mergeE(Map) specifying out/in for vertices in the match/create with no option() # - no vertices/edges in graph - # - results in two new vertices and one new edge + # - results in error as vertices don't exist # g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX # - mergeE(Map) specifying out/in for vertices in the match/create with option(Map) # - no vertices/edges in graph - # - results in two new vertices and one new edge + # - results in error as vertices don't exist # g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists # - mergeE(Map) specifying out/in for vertices in the match/create with option(Map) # - vertices and edge already exist @@ -89,6 +89,10 @@ Feature: Step - mergeE() # - mergeE(Map) using the vertex of current traverser as reference - testing child traversal # - vertex already exists # - results in a self edge + # g_injectXlabel_knows_out_marko_in_vadas_label_self_out_vadas_in_vadasX + # - mergeE() using the map of current traverser as reference + # - vertices already exists + # - results in two new edges Scenario: g_V_mergeEXlabel_self_weight_05X Given the empty graph @@ -186,9 +190,7 @@ Feature: Step - mergeE() g.mergeE(xx1) """ When iterated to list - Then the result should have a count of 1 - And the graph should return 2 for count of "g.V()" - And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"weight\",0.5)" + Then the traversal will raise an error @UserSuppliedVertexIds Scenario: g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX @@ -201,10 +203,7 @@ Feature: Step - mergeE() g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) """ When iterated to list - Then the result should have a count of 1 - And the graph should return 2 for count of "g.V()" - And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"Y\")" - And the graph should return 0 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"N\")" + Then the traversal will raise an error @UserSuppliedVertexIds Scenario: g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists @@ -460,4 +459,25 @@ Feature: Step - 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 + And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").out(\"knows\").has(\"person\",\"name\",\"vadas\")" + + @UserSuppliedVertexIds + Scenario: g_injectXlabel_knows_out_marko_in_vadas_label_self_out_vadas_in_vadasX + 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 using the parameter xx2 defined as "m[{\"t[label]\": \"self\", \"D[OUT]\":\"v[101]\", \"D[IN]\":\"v[101]\"}]" + And the traversal of + """ + g.inject(xx1, xx2).mergeE() + """ + When iterated to list + Then the result should have a count of 2 + And the graph should return 2 for count of "g.V()" + And the graph should return 2 for count of "g.E()" + And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"marko\").out(\"knows\").has(\"person\",\"name\",\"vadas\")" + And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"vadas\").out(\"self\").has(\"person\",\"name\",\"vadas\")" \ No newline at end of file diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeTest.java index c30201c..15dffee 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeTest.java @@ -37,8 +37,11 @@ import org.junit.runner.RunWith; import java.util.Map; import static org.apache.tinkerpop.gremlin.util.tools.CollectionFactory.asMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringEndsWith.endsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; @RunWith(GremlinProcessRunner.class) public abstract class MergeEdgeTest extends AbstractGremlinTest { @@ -112,13 +115,13 @@ public abstract class MergeEdgeTest extends AbstractGremlinTest { public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX() { final Traversal<Edge, Edge> traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX(); printTraversalForm(traversal); - final Edge edge = traversal.next(); - assertEquals("knows", edge.label()); - assertEquals(100, edge.outVertex().id()); - assertEquals(101, edge.inVertex().id()); - assertEquals("Y", edge.<String>value("created")); - assertFalse(traversal.hasNext()); - assertEquals(1, IteratorUtils.count(g.E())); + try { + traversal.next(); + fail("Should have failed as vertices are not created"); + } catch (Exception ex) { + assertThat(ex.getMessage(), endsWith("could not be found and edge could not be created")); + } + assertEquals(0, IteratorUtils.count(g.E())); } @Test