This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 1c8ad966f0ae68a95ff35ea920c9ccf44760fa82 Author: Stephen Mallette <[email protected]> AuthorDate: Fri Mar 13 09:43:39 2026 -0400 TINKERPOP-3235 Fixed mergeE validation of static onCreate overrides --- CHANGELOG.asciidoc | 1 + docs/src/dev/provider/gremlin-semantics.asciidoc | 10 +- docs/src/upgrade/release-3.7.x.asciidoc | 22 +++ .../process/traversal/step/map/MergeStep.java | 22 +++ .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 14 +- gremlin-go/driver/cucumber/gremlin.go | 14 +- .../gremlin-javascript/test/cucumber/gremlin.js | 14 +- .../src/main/python/tests/feature/gremlin.py | 14 +- .../process/traversal/step/map/MergeEdgeTest.java | 124 +++++++++++++++ .../traversal/step/map/MergeVertexTest.java | 40 +++++ .../gremlin/test/features/map/MergeEdge.feature | 167 +++++++++++++++++++-- .../gremlin/test/features/map/MergeVertex.feature | 40 +++-- 12 files changed, 424 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 40d3eab415..fc90710d02 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima * Integrated Python driver examples into automated build process to ensure examples remain functional. * Fixed bug in `SubgraphStrategy` where specifying `edges` and `vertices` filters that had `map`-type steps could generate an error. * Fixed bug in `ReservedKeysVerificationStrategy` where `AddPropertyStep` was not triggering proper validations. +* Fixed bug in `mergeE` where `onCreate` validation of invalid static argument overrides did not trigger until traversal runtime. * Added `closeSessionPostGraphOp` to the Gremlin Server settings to indicate that the `Session` should be closed on either a successful commit or rollback. * Added `SessionedChildClient` that borrows connections from a different `Client` for use with `Sessions`. * Added `reuseConnectionsForSessions` to Java GLV settings to decide whether to use `SessionedChildClient` for remote transactions. diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc index a58fdda374..6679c50b18 100644 --- a/docs/src/dev/provider/gremlin-semantics.asciidoc +++ b/docs/src/dev/provider/gremlin-semantics.asciidoc @@ -1260,7 +1260,9 @@ instance to be applied. `Null` is considered semantically equivalent to an empty If `T.id` is used for `searchCreate` or `onCreate`, it may be ignored for edge creation if the `Graph` does not support user supplied identifiers. `onCreate` inherits from `searchCreate` - values for `T.id`, `T.label`, and `Direction.OUT/IN` do not need to be specified twice. Additionally, `onCreate` cannot override values in `searchCreate` -(i.e. `if (exists(x)) return(x) else create(y)` is not supported). +(i.e. `if (exists(x)) return(x) else create(y)` is not supported). An important point here is that validation for +`onCreate` must occur at traversal construction for static values (i.e. a `Map`) and at runtime for dynamic values +(i.e. a `Traversal`). *Modulation:* @@ -1327,8 +1329,10 @@ instance to be applied. `Null` is considered semantically equivalent to an empty If `T.id` is used for `searchCreate` or `onCreate`, it may be ignored for vertex creation if the `Graph` does not support user supplied identifiers. `onCreate` inherits from `searchCreate` - values for `T.id`, `T.label` do not need -to be specified twice. Additionally, `onCreate` cannot override values in `searchCreate` -(i.e. `if (exists(x)) return(x) else create(y)` is not supported). +to be specified twice. Additionally, `onCreate` cannot override values in `searchCreate` (i.e. +`if (exists(x)) return(x) else create(y)` is not supported). An important point here is that validation for `onCreate` +must occur at traversal construction for static values (i.e. a `Map`) and at runtime for dynamic values (i.e. a +`Traversal`). *Modulation:* diff --git a/docs/src/upgrade/release-3.7.x.asciidoc b/docs/src/upgrade/release-3.7.x.asciidoc index 887787622c..7cb95dda1d 100644 --- a/docs/src/upgrade/release-3.7.x.asciidoc +++ b/docs/src/upgrade/release-3.7.x.asciidoc @@ -30,6 +30,28 @@ complete list of all the modifications that are part of this release. === Upgrading for Users +==== mergeE/V onCreate Validation + +There is an important validation with `mergeE` and `mergeV` that prevents syntax that allows the `onCreate` option from +containing input that would override what is given for the search criteria, as shown in the following example: + +[source,groovy] +---- +g.mergeV([name: 'april']). + option(Merge.onCreate, [name: 'bill']) +---- + +In that example, the validation prevents a search for "april" whose failure results in creation of a vertex with the +name "bill". While demonstrated with `mergeV` for simplicity, the same behavior is expected with `mergeE`. The latter +had this validation, but only in cases where the `onCreate` path was actually triggered. As a result, it was possible +to knowingly write invalid Gremlin that would always fail under create conditions. + +With this change, use of `onCreate` with static values (i.e. `Map` input, not `Traversal`) will validate properly and +earlier, at traversal construction time. It is therefore possible that an upgrade to this version will expose runtime +validation errors for both `mergeE` and `mergeV` that were either not present at all before or now fail earlier +respectively. It is important that you review your code for these patterns as part of your usage. + +See: link:https://issues.apache.org/jira/browse/TINKERPOP-3235[TINKERPOP-3235] === Upgrading for Providers diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java index 5168dc950b..c20904a8c6 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java @@ -141,6 +141,8 @@ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E> @Override public void addChildOption(final Merge token, final Traversal.Admin<S, C> traversalOption) { if (token == Merge.onCreate) { + // early swipe at validation if both search and create arguments are static + validateStaticNoOverrides(mergeTraversal, traversalOption); this.onCreateTraversal = this.integrateChild(traversalOption); } else if (token == Merge.onMatch) { // add a guard rail to ensure that the incoming object is not an Element. this will prevent @@ -388,6 +390,26 @@ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E> protected abstract Set getAllowedTokens(); + /** + * Tests the search input and create input if they are {@link ConstantTraversal} which implies that they were + * derived from static input. If they both are, we can validate that keys in create input are not overriding + * search input. + */ + protected void validateStaticNoOverrides(final Traversal.Admin search, final Traversal.Admin create) { + if (search instanceof ConstantTraversal && create instanceof ConstantTraversal) { + final Object searchObj = search.next(); + final Object createObj = create.next(); + + if (!(searchObj instanceof Map) || !(createObj instanceof Map)) { + return; + } + + final Map searchMap = (Map) searchObj; + final Map createMap = (Map) createObj; + validateNoOverrides(searchMap, createMap); + } + } + /** * Guard rail to ensure that the incoming object is not an {@link Element}. */ diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 7c8fb414ad..cff1dee67a 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -906,7 +906,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_injectXlabel_knows_out_marko_in_vadasX_mergeE", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").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_mergeEXlabel_knows_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").As("a").AddV("person").Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y").AddE("knows").From("b").To("a").Property("created","Y"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreat [...] {"g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").As("a").AddV("person").Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y").AddE("knows").From("b").To("a").Property("created","Y"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCrea [...] - {"g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").As("a").AddV("person").Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y").AddE("knows").From("b").To("a").Property("created","Y"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictio [...] + {"g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_error", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").As("a").AddV("person").Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y").AddE("knows").From("b").To("a").Property("created","Y"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (I [...] + {"g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_dynamic_override_sketchily_allowed", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").As("a").AddV("person").Property("name","vadas").As("b").AddE("knows").From("a").To("b").Property("created","Y").AddE("knows").From("b").To("a").Property("created","Y"), (g,p) =>g.WithSideEffect("x",p["xx2"]).MergeE((IDict [...] {"g_V_hasXperson_name_marko_X_mergeEXlabel_self_out_vadas1_in_vadas1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.V().Has("person","name","marko").MergeE((IDictionary<object,object>) p["xx1"]), (g,p) =>g.V(), (g,p) =>g.E(), (g,p) =>g.E().HasLabel("self").BothV().Has("name","vadas")}}, {"g_withSideEffectXc_created_YX_withSideEffectXm_matchedX_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_selectXcXX_optionXonMatch_selectXmXX_exists", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").As("a").AddV("person").Property("name","vadas").As("b").AddE("knows").From("a").To("b"), (g,p) =>g.WithSideEffect("c",p["xx2"]).WithSideEffect("m",p["xx3"]).MergeE((IDictionary<object,objec [...] {"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("name","marko").As("a").AddV("person").Property("name","vadas").As("b"), (g,p) =>g.WithSideEffect("c",p["xx2"]).WithSideEffect("m",p["xx3"]).MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (I [...] @@ -918,10 +919,10 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_mergeE_with_outVinV_options_select", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.V(p["vid1"]).As("x").V(p["vid2"]).As("y").MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OutV, (ITraversal) __.Select<object>("x")).Option(Merge.InV, (ITraversal) __.Select<object>("y")), (g,p) =>g.V(), (g,p) =>g.V().Has("name","marko").Out("kn [...] {"g_mergeE_with_eid_specified_and_inheritance_1", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.E(), (g,p) =>g.E("201"), (g,p) =>g.V().Has("name","marko").Out("knows").Has("name","vadas")}}, {"g_mergeE_with_eid_specified_and_inheritance_2", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.E(), (g,p) =>g.E("201"), (g,p) =>g.V().Has("name","marko").Out("knows").Has("name","vadas")}}, - {"g_mergeE_outV_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"])}}, - {"g_mergeE_inV_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"])}}, - {"g_mergeE_label_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"])}}, - {"g_mergeE_id_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"])}}, + {"g_mergeE_outV_dynamic_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.WithSideEffect("x",p["xx2"]).MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (ITraversal) __.Select<object>("x"))}}, + {"g_mergeE_inV_dynamic_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.WithSideEffect("x",p["xx2"]).MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (ITraversal) __.Select<object>("x"))}}, + {"g_mergeE_label_dynamic_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.WithSideEffect("x",p["xx2"]).MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (ITraversal) __.Select<object>("x"))}}, + {"g_mergeE_id_dynamic_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.WithSideEffect("x",p["xx2"]).MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (ITraversal) __.Select<object>("x"))}}, {"g_mergeV_mergeE_combination_new_vertices", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).As("outV").MergeV((IDictionary<object,object>) p["xx2"]).As("inV").MergeE((IDictionary<object,object>) p["xx3"]).Option(Merge.OutV, (ITraversal) __.Select<object>("outV")).Option(Merge.InV, (ITraversal) __.Select<object>("inV")), (g,p) =>g.V(), (g,p) =>g.E(), (g,p) =>g.V().Has("name","marko").Out [...] {"g_mergeV_mergeE_combination_existing_vertices", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").AddV("person").Property("name","vadas"), (g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).As("outV").MergeV((IDictionary<object,object>) p["xx2"]).As("inV").MergeE((IDictionary<object,object>) p["xx3"]).Option(Merge.OutV, (ITraversal) __.Select<object>("outV")).Option(Merge.InV, (ITraversal [...] {"g_V_asXvX_mergeEXxx1X_optionXMerge_onMatch_xx2X_optionXMerge_outV_selectXvXX_optionXMerge_inV_selectXvXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (g,p) =>g.V().As("v").MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx2"]).Option(Merge.OutV, (ITraversal) __.Select<object>("v")).Option(Merge.InV, (ITraversal) __. [...] @@ -969,8 +970,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_mergeV_onCreate_inheritance_existing", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","mike").Property(T.Id,"1"), (g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.V("1").Has("person","name","mike")}}, {"g_mergeV_onCreate_inheritance_new_1", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.V("1").Has("person","name","mike")}}, {"g_mergeV_onCreate_inheritance_new_2", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.V("1").Has("person","name","mike")}}, - {"g_mergeV_label_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"])}}, - {"g_mergeV_id_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (IDictionary<object,object>) p["xx2"])}}, + {"g_mergeV_label_dynamic_override_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSideEffect("x",p["xx2"]).MergeV((IDictionary<object,object>) p["xx1"]).Option(Merge.OnCreate, (ITraversal) __.Select<object>("x"))}}, {"g_mergeV_hidden_id_key_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"])}}, {"g_mergeV_hidden_label_key_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"])}}, {"g_mergeV_hidden_label_value_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) p["xx1"])}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index 0d7446288a..840b351efa 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -877,7 +877,8 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_injectXlabel_knows_out_marko_in_vadasX_mergeE": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).MergeE()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", [...] "g_mergeEXlabel_knows_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "vadas").As("b").AddE("knows").From("a").To("b").Property("created", "Y").AddE("knows").From("b").To("a").Property("created", "Y")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gr [...] "g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "vadas").As("b").AddE("knows").From("a").To("b").Property("created", "Y").AddE("knows").From("b").To("a").Property("created", "Y")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *g [...] - "g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "vadas").As("b").AddE("knows").From("a").To("b").Property("created", "Y").AddE("knows").From("b").To("a").Property("created", "Y")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.Gra [...] + "g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_error": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "vadas").As("b").AddE("knows").From("a").To("b").Property("created", "Y").AddE("knows").From("b").To("a").Property("created", "Y")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlin [...] + "g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_dynamic_override_sketchily_allowed": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "vadas").As("b").AddE("knows").From("a").To("b").Property("created", "Y").AddE("knows").From("b").To("a").Property("created", "Y")}, func(g *gremlingo.GraphTraversalSource, p map [...] "g_V_hasXperson_name_marko_X_mergeEXlabel_self_out_vadas1_in_vadas1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "marko").MergeE(p["xx1"])}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlin [...] "g_withSideEffectXc_created_YX_withSideEffectXm_matchedX_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_selectXcXX_optionXonMatch_selectXmXX_exists": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "vadas").As("b").AddE("knows").From("a").To("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {re [...] "g_withSideEffectXc_created_YX_withSideEffectXm_matchedX_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_selectXcXX_optionXonMatch_selectXmXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "vadas").As("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithSideEffect("c", p["xx2"]).Wi [...] @@ -889,10 +890,10 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_mergeE_with_outVinV_options_select": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).As("x").V(p["vid2"]).As("y").MergeE(p["xx1"]).Option(gremlingo.Merge.OutV, gremlingo.T__.Select("x")).Option(gremlingo.Merge.InV, gremlingo.T__.Se [...] "g_mergeE_with_eid_specified_and_inheritance_1": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraver [...] "g_mergeE_with_eid_specified_and_inheritance_2": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraver [...] - "g_mergeE_outV_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}}, - "g_mergeE_inV_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}}, - "g_mergeE_label_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}}, - "g_mergeE_id_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}}, + "g_mergeE_outV_dynamic_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithSideEffect("x", p["xx2"]).MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, gremlingo.T__.Select("x"))}}, + "g_mergeE_inV_dynamic_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithSideEffect("x", p["xx2"]).MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, gremlingo.T__.Select("x"))}}, + "g_mergeE_label_dynamic_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithSideEffect("x", p["xx2"]).MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, gremlingo.T__.Select("x"))}}, + "g_mergeE_id_dynamic_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithSideEffect("x", p["xx2"]).MergeE(p["xx1"]).Option(gremlingo.Merge.OnCreate, gremlingo.T__.Select("x"))}}, "g_mergeV_mergeE_combination_new_vertices": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"]).As("outV").MergeV(p["xx2"]).As("inV").MergeE(p["xx3"]).Option(gremlingo.Merge.OutV, gremlingo.T__.Select("outV")).Option(gremlingo.Merge.InV, gremlingo.T__.Select("inV"))}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p m [...] "g_mergeV_mergeE_combination_existing_vertices": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").AddV("person").Property("name", "vadas")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"]).As("outV").MergeV(p["xx2"]).As("inV").MergeE(p["xx3"]).Option(gremlingo.Merge.OutV, gremlingo.T__.Select("outV")).Option(gremlingo.Me [...] "g_V_asXvX_mergeEXxx1X_optionXMerge_onMatch_xx2X_optionXMerge_outV_selectXvXX_optionXMerge_inV_selectXvXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().As("v").MergeE(p["xx1"]).Option(gremlingo.Merge.OnMatch, p["xx2"]).Option(gremlingo.Merge.OutV, gremlingo.T_ [...] @@ -940,8 +941,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_mergeV_onCreate_inheritance_existing": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "mike").Property(gremlingo.T.Id, "1")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, [...] "g_mergeV_onCreate_inheritance_new_1": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V("1").Has("person", "name", "mike")}}, "g_mergeV_onCreate_inheritance_new_2": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V("1").Has("person", "name", "mike")}}, - "g_mergeV_label_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}}, - "g_mergeV_id_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"]).Option(gremlingo.Merge.OnCreate, p["xx2"])}}, + "g_mergeV_label_dynamic_override_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithSideEffect("x", p["xx2"]).MergeV(p["xx1"]).Option(gremlingo.Merge.OnCreate, gremlingo.T__.Select("x"))}}, "g_mergeV_hidden_id_key_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"])}}, "g_mergeV_hidden_label_key_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"])}}, "g_mergeV_hidden_label_value_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(p["xx1"])}}, 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 1de8032d81..1403e37d53 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 @@ -897,7 +897,8 @@ const gremlins = { g_injectXlabel_knows_out_marko_in_vadasX_mergeE: [function({g, xx1}) { return g.addV("person").property("name","marko").addV("person").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_mergeEXlabel_knows_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated: [function({g, xx1, xx3, xx2}) { return g.addV("person").property("name","marko").as("a").addV("person").property("name","vadas").as("b").addE("knows").from_("a").to("b").property("created","Y").addE("knows").from_("b").to("a").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 [...] g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated: [function({g, xx1, xx3, xx2}) { return g.addV("person").property("name","marko").as("a").addV("person").property("name","vadas").as("b").addE("knows").from_("a").to("b").property("created","Y").addE("knows").from_("b").to("a").property("created","Y") }, function({g, xx1, xx3, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) }, function({g, xx1, xx3, xx [...] - g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated: [function({g, xx1, xx3, xx2}) { return g.addV("person").property("name","marko").as("a").addV("person").property("name","vadas").as("b").addE("knows").from_("a").to("b").property("created","Y").addE("knows").from_("b").to("a").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}) { return [...] + g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_error: [function({g, xx1, xx3, xx2}) { return g.addV("person").property("name","marko").as("a").addV("person").property("name","vadas").as("b").addE("knows").from_("a").to("b").property("created","Y").addE("knows").from_("b").to("a").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}) { [...] + g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_dynamic_override_sketchily_allowed: [function({g, xx1, xx3, xx2}) { return g.addV("person").property("name","marko").as("a").addV("person").property("name","vadas").as("b").addE("knows").from_("a").to("b").property("created","Y").addE("knows").from_("b").to("a").property("created","Y") }, function({g, xx1, xx3, xx2}) { return g.withSideEffect("x",xx2).mergeE(xx1).option(Merge.onCreate,__.select("x [...] g_V_hasXperson_name_marko_X_mergeEXlabel_self_out_vadas1_in_vadas1X: [function({g, xx1}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1}) { return g.V().has("person","name","marko").mergeE(xx1) }, function({g, xx1}) { return g.V() }, function({g, xx1}) { return g.E() }, function({g, xx1}) { return g.E().hasLabel("self").bothV().has("name","vadas") }], g_withSideEffectXc_created_YX_withSideEffectXm_matchedX_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_selectXcXX_optionXonMatch_selectXmXX_exists: [function({g, xx1, xx3, xx2}) { return g.addV("person").property("name","marko").as("a").addV("person").property("name","vadas").as("b").addE("knows").from_("a").to("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_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("name","marko").as("a").addV("person").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,__.select("m")) }, function({g, xx1, xx3 [...] @@ -909,10 +910,10 @@ const gremlins = { g_mergeE_with_outVinV_options_select: [function({g, xx1, vid2, vid1}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, vid2, vid1}) { return g.V(vid1).as("x").V(vid2).as("y").mergeE(xx1).option(Merge.outV,__.select("x")).option(Merge.inV,__.select("y")) }, function({g, xx1, vid2, vid1}) { return g.V() }, function({g, xx1, vid2, vid1}) { return g.V().has("name","marko").out("knows").has("name","vadas") }], g_mergeE_with_eid_specified_and_inheritance_1: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2) }, function({g, xx1, xx2}) { return g.V() }, function({g, xx1, xx2}) { return g.E() }, function({g, xx1, xx2}) { return g.E("201") }, function({g, xx1, xx2}) { return g.V().has("name","marko").out("knows").has("name","vadas") }], g_mergeE_with_eid_specified_and_inheritance_2: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2) }, function({g, xx1, xx2}) { return g.V() }, function({g, xx1, xx2}) { return g.E() }, function({g, xx1, xx2}) { return g.E("201") }, function({g, xx1, xx2}) { return g.V().has("name","marko").out("knows").has("name","vadas") }], - g_mergeE_outV_override_prohibited: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2) }], - g_mergeE_inV_override_prohibited: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2) }], - g_mergeE_label_override_prohibited: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2) }], - g_mergeE_id_override_prohibited: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.mergeE(xx1).option(Merge.onCreate,xx2) }], + g_mergeE_outV_dynamic_override_prohibited: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.withSideEffect("x",xx2).mergeE(xx1).option(Merge.onCreate,__.select("x")) }], + g_mergeE_inV_dynamic_override_prohibited: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.withSideEffect("x",xx2).mergeE(xx1).option(Merge.onCreate,__.select("x")) }], + g_mergeE_label_dynamic_override_prohibited: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.withSideEffect("x",xx2).mergeE(xx1).option(Merge.onCreate,__.select("x")) }], + g_mergeE_id_dynamic_override_prohibited: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx2}) { return g.withSideEffect("x",xx2).mergeE(xx1).option(Merge.onCreate,__.select("x")) }], g_mergeV_mergeE_combination_new_vertices: [function({g, xx1, xx3, xx2}) { return g.mergeV(xx1).as("outV").mergeV(xx2).as("inV").mergeE(xx3).option(Merge.outV,__.select("outV")).option(Merge.inV,__.select("inV")) }, function({g, xx1, xx3, xx2}) { return g.V() }, function({g, xx1, xx3, xx2}) { return g.E() }, function({g, xx1, xx3, xx2}) { return g.V().has("name","marko").out("knows").has("name","vadas") }], g_mergeV_mergeE_combination_existing_vertices: [function({g, xx1, xx3, xx2}) { return g.addV("person").property("name","marko").addV("person").property("name","vadas") }, function({g, xx1, xx3, xx2}) { return g.mergeV(xx1).as("outV").mergeV(xx2).as("inV").mergeE(xx3).option(Merge.outV,__.select("outV")).option(Merge.inV,__.select("inV")) }, function({g, xx1, xx3, xx2}) { return g.V() }, function({g, xx1, xx3, xx2}) { return g.E() }, function({g, xx1, xx3, xx2}) { return g.V().has("na [...] g_V_asXvX_mergeEXxx1X_optionXMerge_onMatch_xx2X_optionXMerge_outV_selectXvXX_optionXMerge_inV_selectXvXX: [function({g, xx1, xx2}) { return g.addV("person").property("name","marko").property("age",29) }, function({g, xx1, xx2}) { return g.V().as("v").mergeE(xx1).option(Merge.onMatch,xx2).option(Merge.outV,__.select("v")).option(Merge.inV,__.select("v")) }], @@ -960,8 +961,7 @@ const gremlins = { g_mergeV_onCreate_inheritance_existing: [function({g, xx1, xx2}) { return g.addV("person").property("name","mike").property(T.id,"1") }, function({g, xx1, xx2}) { return g.mergeV(xx1).option(Merge.onCreate,xx2) }, function({g, xx1, xx2}) { return g.V() }, function({g, xx1, xx2}) { return g.V("1").has("person","name","mike") }], g_mergeV_onCreate_inheritance_new_1: [function({g, xx1, xx2}) { return g.mergeV(xx1).option(Merge.onCreate,xx2) }, function({g, xx1, xx2}) { return g.V() }, function({g, xx1, xx2}) { return g.V("1").has("person","name","mike") }], g_mergeV_onCreate_inheritance_new_2: [function({g, xx1, xx2}) { return g.mergeV(xx1).option(Merge.onCreate,xx2) }, function({g, xx1, xx2}) { return g.V() }, function({g, xx1, xx2}) { return g.V("1").has("person","name","mike") }], - g_mergeV_label_override_prohibited: [function({g, xx1, xx2}) { return g.mergeV(xx1).option(Merge.onCreate,xx2) }], - g_mergeV_id_override_prohibited: [function({g, xx1, xx2}) { return g.mergeV(xx1).option(Merge.onCreate,xx2) }], + g_mergeV_label_dynamic_override_prohibited: [function({g, xx1, xx2}) { return g.withSideEffect("x",xx2).mergeV(xx1).option(Merge.onCreate,__.select("x")) }], g_mergeV_hidden_id_key_prohibited: [function({g, xx1}) { return g.mergeV(xx1) }], g_mergeV_hidden_label_key_prohibited: [function({g, xx1}) { return g.mergeV(xx1) }], g_mergeV_hidden_label_value_prohibited: [function({g, xx1}) { return g.mergeV(xx1) }], diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py b/gremlin-python/src/main/python/tests/feature/gremlin.py index 080755d0ed..d406fa7602 100644 --- a/gremlin-python/src/main/python/tests/feature/gremlin.py +++ b/gremlin-python/src/main/python/tests/feature/gremlin.py @@ -879,7 +879,8 @@ world.gremlins = { 'g_injectXlabel_knows_out_marko_in_vadasX_mergeE': [(lambda g, xx1=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None:g.inject(xx1).merge_e()), (lambda g, xx1=None:g.V().has('person','name','marko').out('knows').has('person','name','vadas'))], 'g_mergeEXlabel_knows_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated': [(lambda g, xx1=None,xx3=None,xx2=None:g.add_v('person').property('name','marko').as_('a').add_v('person').property('name','vadas').as_('b').add_e('knows').from_('a').to('b').property('created','Y').add_e('knows').from_('b').to('a').property('created','Y')), (lambda g, xx1=None,xx3=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2).option(Merge.on_match,xx3)), (lambda g, xx1=No [...] 'g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated': [(lambda g, xx1=None,xx3=None,xx2=None:g.add_v('person').property('name','marko').as_('a').add_v('person').property('name','vadas').as_('b').add_e('knows').from_('a').to('b').property('created','Y').add_e('knows').from_('b').to('a').property('created','Y')), (lambda g, xx1=None,xx3=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2).option(Merge.on_match,xx3)), (lambda g, xx1=N [...] - 'g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated': [(lambda g, xx1=None,xx3=None,xx2=None:g.add_v('person').property('name','marko').as_('a').add_v('person').property('name','vadas').as_('b').add_e('knows').from_('a').to('b').property('created','Y').add_e('knows').from_('b').to('a').property('created','Y')), (lambda g, xx1=None,xx3=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2).option(Merge.on_match,xx3)), (lambda g, xx1=None,xx3=None [...] + 'g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_error': [(lambda g, xx1=None,xx3=None,xx2=None:g.add_v('person').property('name','marko').as_('a').add_v('person').property('name','vadas').as_('b').add_e('knows').from_('a').to('b').property('created','Y').add_e('knows').from_('b').to('a').property('created','Y')), (lambda g, xx1=None,xx3=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2).option(Merge.on_match,xx3)), (lambda g, xx1=None,xx [...] + 'g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_dynamic_override_sketchily_allowed': [(lambda g, xx1=None,xx3=None,xx2=None:g.add_v('person').property('name','marko').as_('a').add_v('person').property('name','vadas').as_('b').add_e('knows').from_('a').to('b').property('created','Y').add_e('knows').from_('b').to('a').property('created','Y')), (lambda g, xx1=None,xx3=None,xx2=None:g.with_side_effect('x',xx2).merge_e(xx1).option(Merge.on_create,_ [...] 'g_V_hasXperson_name_marko_X_mergeEXlabel_self_out_vadas1_in_vadas1X': [(lambda g, xx1=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None:g.V().has('person','name','marko').merge_e(xx1)), (lambda g, xx1=None:g.V()), (lambda g, xx1=None:g.E()), (lambda g, xx1=None:g.E().has_label('self').both_v().has('name','vadas'))], 'g_withSideEffectXc_created_YX_withSideEffectXm_matchedX_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_selectXcXX_optionXonMatch_selectXmXX_exists': [(lambda g, xx1=None,xx3=None,xx2=None:g.add_v('person').property('name','marko').as_('a').add_v('person').property('name','vadas').as_('b').add_e('knows').from_('a').to('b')), (lambda g, xx1=None,xx3=None,xx2=None:g.with_side_effect('c',xx2).with_side_effect('m',xx3).merge_e(xx1).option(Merge.on_create,__.select('c')).option(Me [...] '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.add_v('person').property('name','marko').as_('a').add_v('person').property('name','vadas').as_('b')), (lambda g, xx1=None,xx3=None,xx2=None:g.with_side_effect('c',xx2).with_side_effect('m',xx3).merge_e(xx1).option(Merge.on_create,__.select('c')).option(Merge.on_match,__.select('m'))), (lambda g, [...] @@ -891,10 +892,10 @@ world.gremlins = { 'g_mergeE_with_outVinV_options_select': [(lambda g, xx1=None,vid2=None,vid1=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,vid2=None,vid1=None:g.V(vid1).as_('x').V(vid2).as_('y').merge_e(xx1).option(Merge.out_v,__.select('x')).option(Merge.in_v,__.select('y'))), (lambda g, xx1=None,vid2=None,vid1=None:g.V()), (lambda g, xx1=None,vid2=None,vid1=None:g.V().has('name','marko').out('knows').has('name','vadas'))], 'g_mergeE_with_eid_specified_and_inheritance_1': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2)), (lambda g, xx1=None,xx2=None:g.V()), (lambda g, xx1=None,xx2=None:g.E()), (lambda g, xx1=None,xx2=None:g.E('201')), (lambda g, xx1=None,xx2=None:g.V().has('name','marko').out('knows').has('name','vadas'))], 'g_mergeE_with_eid_specified_and_inheritance_2': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2)), (lambda g, xx1=None,xx2=None:g.V()), (lambda g, xx1=None,xx2=None:g.E()), (lambda g, xx1=None,xx2=None:g.E('201')), (lambda g, xx1=None,xx2=None:g.V().has('name','marko').out('knows').has('name','vadas'))], - 'g_mergeE_outV_override_prohibited': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2))], - 'g_mergeE_inV_override_prohibited': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2))], - 'g_mergeE_label_override_prohibited': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2))], - 'g_mergeE_id_override_prohibited': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.merge_e(xx1).option(Merge.on_create,xx2))], + 'g_mergeE_outV_dynamic_override_prohibited': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.with_side_effect('x',xx2).merge_e(xx1).option(Merge.on_create,__.select('x')))], + 'g_mergeE_inV_dynamic_override_prohibited': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.with_side_effect('x',xx2).merge_e(xx1).option(Merge.on_create,__.select('x')))], + 'g_mergeE_label_dynamic_override_prohibited': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.with_side_effect('x',xx2).merge_e(xx1).option(Merge.on_create,__.select('x')))], + 'g_mergeE_id_dynamic_override_prohibited': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx2=None:g.with_side_effect('x',xx2).merge_e(xx1).option(Merge.on_create,__.select('x')))], 'g_mergeV_mergeE_combination_new_vertices': [(lambda g, xx1=None,xx3=None,xx2=None:g.merge_v(xx1).as_('outV').merge_v(xx2).as_('inV').merge_e(xx3).option(Merge.out_v,__.select('outV')).option(Merge.in_v,__.select('inV'))), (lambda g, xx1=None,xx3=None,xx2=None:g.V()), (lambda g, xx1=None,xx3=None,xx2=None:g.E()), (lambda g, xx1=None,xx3=None,xx2=None:g.V().has('name','marko').out('knows').has('name','vadas'))], 'g_mergeV_mergeE_combination_existing_vertices': [(lambda g, xx1=None,xx3=None,xx2=None:g.add_v('person').property('name','marko').add_v('person').property('name','vadas')), (lambda g, xx1=None,xx3=None,xx2=None:g.merge_v(xx1).as_('outV').merge_v(xx2).as_('inV').merge_e(xx3).option(Merge.out_v,__.select('outV')).option(Merge.in_v,__.select('inV'))), (lambda g, xx1=None,xx3=None,xx2=None:g.V()), (lambda g, xx1=None,xx3=None,xx2=None:g.E()), (lambda g, xx1=None,xx3=None,xx2=None:g.V(). [...] 'g_V_asXvX_mergeEXxx1X_optionXMerge_onMatch_xx2X_optionXMerge_outV_selectXvXX_optionXMerge_inV_selectXvXX': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','marko').property('age',29)), (lambda g, xx1=None,xx2=None:g.V().as_('v').merge_e(xx1).option(Merge.on_match,xx2).option(Merge.out_v,__.select('v')).option(Merge.in_v,__.select('v')))], @@ -942,8 +943,7 @@ world.gremlins = { 'g_mergeV_onCreate_inheritance_existing': [(lambda g, xx1=None,xx2=None:g.add_v('person').property('name','mike').property(T.id_,'1')), (lambda g, xx1=None,xx2=None:g.merge_v(xx1).option(Merge.on_create,xx2)), (lambda g, xx1=None,xx2=None:g.V()), (lambda g, xx1=None,xx2=None:g.V('1').has('person','name','mike'))], 'g_mergeV_onCreate_inheritance_new_1': [(lambda g, xx1=None,xx2=None:g.merge_v(xx1).option(Merge.on_create,xx2)), (lambda g, xx1=None,xx2=None:g.V()), (lambda g, xx1=None,xx2=None:g.V('1').has('person','name','mike'))], 'g_mergeV_onCreate_inheritance_new_2': [(lambda g, xx1=None,xx2=None:g.merge_v(xx1).option(Merge.on_create,xx2)), (lambda g, xx1=None,xx2=None:g.V()), (lambda g, xx1=None,xx2=None:g.V('1').has('person','name','mike'))], - 'g_mergeV_label_override_prohibited': [(lambda g, xx1=None,xx2=None:g.merge_v(xx1).option(Merge.on_create,xx2))], - 'g_mergeV_id_override_prohibited': [(lambda g, xx1=None,xx2=None:g.merge_v(xx1).option(Merge.on_create,xx2))], + 'g_mergeV_label_dynamic_override_prohibited': [(lambda g, xx1=None,xx2=None:g.with_side_effect('x',xx2).merge_v(xx1).option(Merge.on_create,__.select('x')))], 'g_mergeV_hidden_id_key_prohibited': [(lambda g, xx1=None:g.merge_v(xx1))], 'g_mergeV_hidden_label_key_prohibited': [(lambda g, xx1=None:g.merge_v(xx1))], 'g_mergeV_hidden_label_value_prohibited': [(lambda g, xx1=None:g.merge_v(xx1))], 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 226c0f32ce..8d875ac587 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 @@ -39,6 +39,7 @@ import org.junit.runner.RunWith; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.select; import static org.apache.tinkerpop.gremlin.util.CollectionUtil.asMap; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.core.StringEndsWith.endsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -65,6 +66,18 @@ public abstract class MergeEdgeTest extends AbstractGremlinTest { public abstract Traversal<Edge, Edge> get_g_mergeE_with_outV_inV_options(); + public abstract Traversal<Edge, Edge> get_g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited(); + + public abstract Traversal<Edge, Edge> get_g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited(); + + public abstract Traversal<Edge, Edge> get_g_mergeE_outV_override_prohibited(); + + public abstract Traversal<Edge, Edge> get_g_mergeE_inV_override_prohibited(); + + public abstract Traversal<Edge, Edge> get_g_mergeE_label_override_prohibited(); + + public abstract Traversal<Edge, Edge> get_g_mergeE_id_override_prohibited(); + @Test @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) public void g_V_mergeEXlabel_self_weight_05X() { @@ -210,6 +223,79 @@ public abstract class MergeEdgeTest extends AbstractGremlinTest { assertEquals(1, IteratorUtils.count(g.V(1).out("knows").hasId(2))); } + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited() { + try { + get_g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited(); + fail("Should have thrown an error"); + } catch (Exception ise) { + assertThat(ise.getMessage(), containsString("option(onCreate) cannot override values from merge() argument")); + } + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited() { + try { + get_g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited(); + fail("Should have thrown an error"); + } catch (Exception ise) { + assertThat(ise.getMessage(), containsString("option(onCreate) cannot override values from merge() argument")); + } + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeE_outV_override_prohibited() { + try { + get_g_mergeE_outV_override_prohibited(); + fail("Should have thrown an error"); + } catch (Exception ise) { + assertThat(ise.getMessage(), containsString("option(onCreate) cannot override values from merge() argument")); + } + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeE_inV_override_prohibited() { + try { + get_g_mergeE_inV_override_prohibited(); + fail("Should have thrown an error"); + } catch (Exception ise) { + assertThat(ise.getMessage(), containsString("option(onCreate) cannot override values from merge() argument")); + } + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeE_label_override_prohibited() { + try { + get_g_mergeE_label_override_prohibited(); + fail("Should have thrown an error"); + } catch (Exception ise) { + assertThat(ise.getMessage(), containsString("option(onCreate) cannot override values from merge() argument")); + } + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeE_id_override_prohibited() { + try { + get_g_mergeE_id_override_prohibited(); + fail("Should have thrown an error"); + } catch (Exception ise) { + assertThat(ise.getMessage(), containsString("option(onCreate) cannot override values from merge() argument")); + } + } + public static class Traversals extends MergeEdgeTest { @Override @@ -267,5 +353,43 @@ public abstract class MergeEdgeTest extends AbstractGremlinTest { .option(Merge.outV, asMap(T.id, 1)) .option(Merge.inV, asMap(T.id, 2)); } + + @Override + public Traversal<Edge, Edge> get_g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited() { + return g.mergeE(asMap(T.label, "knows", Direction.OUT, new ReferenceVertex(101))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.OUT, new ReferenceVertex(100), Direction.IN, new ReferenceVertex(101), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal<Edge, Edge> get_g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited() { + return g.mergeE(asMap(Direction.OUT, new ReferenceVertex(101))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.OUT, new ReferenceVertex(100), Direction.IN, new ReferenceVertex(101), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal<Edge, Edge> get_g_mergeE_outV_override_prohibited() { + return g.mergeE(asMap(Direction.OUT, new ReferenceVertex(100), Direction.IN, new ReferenceVertex(101), T.label, "knows")). + option(Merge.onCreate, asMap(Direction.OUT, new ReferenceVertex(101))); + } + + @Override + public Traversal<Edge, Edge> get_g_mergeE_inV_override_prohibited() { + return g.mergeE(asMap(Direction.OUT, new ReferenceVertex(100), Direction.IN, new ReferenceVertex(101), T.label, "knows")). + option(Merge.onCreate, asMap(Direction.IN, new ReferenceVertex(100))); + } + + @Override + public Traversal<Edge, Edge> get_g_mergeE_label_override_prohibited() { + return g.mergeE(asMap(Direction.OUT, new ReferenceVertex(100), Direction.IN, new ReferenceVertex(101), T.label, "knows")). + option(Merge.onCreate, asMap(T.label, "likes")); + } + + @Override + public Traversal<Edge, Edge> get_g_mergeE_id_override_prohibited() { + return g.mergeE(asMap(Direction.OUT, new ReferenceVertex(100), Direction.IN, new ReferenceVertex(101), T.label, "knows", T.id, "101")). + option(Merge.onCreate, asMap(T.id, "201")); + } } } \ No newline at end of file diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexTest.java index 9185d3a41e..e3310dbff9 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexTest.java @@ -34,8 +34,11 @@ import org.junit.runner.RunWith; import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN; import static org.apache.tinkerpop.gremlin.util.CollectionUtil.asMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; @RunWith(GremlinProcessRunner.class) public abstract class MergeVertexTest extends AbstractGremlinTest { @@ -56,6 +59,10 @@ public abstract class MergeVertexTest extends AbstractGremlinTest { public abstract Traversal<Object, Vertex> get_g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option(); + public abstract Traversal<Vertex, Vertex> get_g_mergeV_label_override_prohibited(); + + public abstract Traversal<Vertex, Vertex> get_g_mergeV_id_override_prohibited(); + @Test @LoadGraphWith(MODERN) @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) @@ -173,6 +180,29 @@ public abstract class MergeVertexTest extends AbstractGremlinTest { assertEquals(6, IteratorUtils.count(g.V())); } + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + public void g_mergeV_label_override_prohibited() { + try { + get_g_mergeV_label_override_prohibited(); + fail("Should have thrown an error"); + } catch (Exception ise) { + assertThat(ise.getMessage(), containsString("option(onCreate) cannot override values from merge() argument")); + } + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_mergeV_id_override_prohibited() { + try { + get_g_mergeV_id_override_prohibited(); + fail("Should have thrown an error"); + } catch (Exception ise) { + assertThat(ise.getMessage(), containsString("option(onCreate) cannot override values from merge() argument")); + } + } + public static class Traversals extends MergeVertexTest { @Override @@ -218,5 +248,15 @@ public abstract class MergeVertexTest extends AbstractGremlinTest { withSideEffect("m", asMap("age", 19)). mergeV(__.select("c")).option(Merge.onMatch, __.select("m")); } + + @Override + public Traversal<Vertex, Vertex> get_g_mergeV_label_override_prohibited() { + return g.mergeV(asMap(T.label, "a")).option(Merge.onCreate, asMap(T.label, "b")); + } + + @Override + public Traversal<Vertex, Vertex> get_g_mergeV_id_override_prohibited() { + return g.mergeV(asMap(T.id, "1")).option(Merge.onCreate, asMap(T.id, "2")); + } } } \ No newline at end of file diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeEdge.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeEdge.feature index 2a8122083d..755456c7cb 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeEdge.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeEdge.feature @@ -456,8 +456,8 @@ Feature: Step - mergeE() addE("knows").from("a").to("b").property("created","Y"). addE("knows").from("b").to("a").property("created","Y") """ - And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[vadas]\"}]" - And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[marko]\", \"D[IN]\":\"v[vadas]\",\"created\":\"Y\"}]" + And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[vadas].id\"}]" + And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[IN]\":\"v[marko].id\", \"D[OUT]\":\"v[vadas].id\",\"created\":\"Y\"}]" And using the parameter xx3 defined as "m[{\"created\":\"N\"}]" And the traversal of """ @@ -470,7 +470,28 @@ Feature: Step - mergeE() And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"Y\")" And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"N\").outV().has(\"name\",\"vadas\")" - Scenario: g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated + # Tests the case where a edge match triggers onMatch, but still treats this as invalid Gremlin due to onCreate. + # as of 3.7.6, the error occurs on construction and can't be tested with gherkin +# Scenario: g_mergeEXlabel_knows_out_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited +# Given the empty graph +# And the graph initializer of +# """ +# g.addV("person").property("name", "marko").as("a"). +# addV("person").property("name", "vadas").as("b"). +# addE("knows").from("a").to("b").property("created","Y"). +# addE("knows").from("b").to("a").property("created","Y") +# """ +# And using the parameter xx1 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[vadas].id\"}]" +# And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[marko].id\", \"D[IN]\":\"v[vadas].id\",\"created\":\"Y\"}]" +# And using the parameter xx3 defined as "m[{\"created\":\"N\"}]" +# And the traversal of +# """ +# g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) +# """ +# When iterated to list +# Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + + Scenario: g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_error Given the empty graph And the graph initializer of """ @@ -479,8 +500,8 @@ Feature: Step - mergeE() addE("knows").from("a").to("b").property("created","Y"). addE("knows").from("b").to("a").property("created","Y") """ - And using the parameter xx1 defined as "m[{\"D[OUT]\":\"v[vadas]\"}]" - And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[marko]\", \"D[IN]\":\"v[vadas]\",\"created\":\"Y\"}]" + And using the parameter xx1 defined as "m[{\"D[OUT]\":\"v[vadas].id\"}]" + And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[IN]\":\"v[marko].id\", \"D[OUT]\":\"v[vadas].id\",\"created\":\"Y\"}]" And using the parameter xx3 defined as "m[{\"created\":\"N\"}]" And the traversal of """ @@ -493,6 +514,53 @@ Feature: Step - mergeE() And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"Y\")" And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"N\").outV().has(\"name\",\"vadas\")" + # Tests the case where a edge match triggers onMatch, but still treats this as invalid Gremlin due to onCreate. + # as of 3.7.6, the error occurs on construction and can't be tested with gherkin +# Scenario: g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_override_prohibited +# Given the empty graph +# And the graph initializer of +# """ +# g.addV("person").property("name", "marko").as("a"). +# addV("person").property("name", "vadas").as("b"). +# addE("knows").from("a").to("b").property("created","Y"). +# addE("knows").from("b").to("a").property("created","Y") +# """ +# And using the parameter xx1 defined as "m[{\"D[OUT]\":\"v[vadas].id\"}]" +# And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[marko].id\", \"D[IN]\":\"v[vadas].id\",\"created\":\"Y\"}]" +# And using the parameter xx3 defined as "m[{\"created\":\"N\"}]" +# And the traversal of +# """ +# g.mergeE(xx1).option(Merge.onCreate,xx2).option(Merge.onMatch,xx3) +# """ +# When iterated to list +# Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + + # Tests the case where a edge match triggers onMatch, so the validation for onCreate never kicks in because the + # Map for onCreate is dynamically assigned. we can't know until we resolve that there is a problem. this is a bit of + # a latent bug for a user, but they will get a failure at runtime whenever the onCreate branch executes + Scenario: g_mergeEXout_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated_dynamic_override_sketchily_allowed + Given the empty graph + And the graph initializer of + """ + g.addV("person").property("name", "marko").as("a"). + addV("person").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").property("created","Y"). + addE("knows").from("b").to("a").property("created","Y") + """ + And using the parameter xx1 defined as "m[{\"D[OUT]\":\"v[vadas].id\"}]" + And using the parameter xx2 defined as "m[{\"t[label]\": \"knows\", \"D[OUT]\":\"v[marko].id\", \"D[IN]\":\"v[vadas].id\",\"created\":\"Y\"}]" + And using the parameter xx3 defined as "m[{\"created\":\"N\"}]" + And the traversal of + """ + g.withSideEffect('x', xx2).mergeE(xx1).option(Merge.onCreate, select('x')).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 2 for count of "g.E()" + And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"Y\")" + And the graph should return 1 for count of "g.E().hasLabel(\"knows\").has(\"created\",\"N\").outV().has(\"name\",\"vadas\")" + Scenario: g_V_hasXperson_name_marko_X_mergeEXlabel_self_out_vadas1_in_vadas1X Given the empty graph And the graph initializer of @@ -721,7 +789,25 @@ Feature: Step - mergeE() And the graph should return 1 for count of "g.V().has(\"name\",\"marko\").out(\"knows\").has(\"name\",\"vadas\")" # cannot override Direction.OUT in onCreate - Scenario: g_mergeE_outV_override_prohibited + # as of 3.7.6, the error occurs on construction and can't be tested with gherkin +# Scenario: g_mergeE_outV_override_prohibited +# Given the empty graph +# And the graph initializer of +# """ +# g.addV("person").property("name", "marko"). +# addV("person").property("name", "vadas") +# """ +# And using the parameter xx1 defined as "m[{\"D[OUT]\" : \"v[marko].id\", \"D[IN]\" : \"v[vadas].id\", \"t[label]\": \"knows\"}]" +# And using the parameter xx2 defined as "m[{\"D[OUT]\" : \"v[vadas].id\"}]" +# And the traversal of +# """ +# g.mergeE(xx1).option(onCreate, xx2) +# """ +# When iterated to list +# Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + + # cannot override Direction.OUT in onCreate + Scenario: g_mergeE_outV_dynamic_override_prohibited Given the empty graph And the graph initializer of """ @@ -732,13 +818,31 @@ Feature: Step - mergeE() And using the parameter xx2 defined as "m[{\"D[OUT]\" : \"v[vadas].id\"}]" And the traversal of """ - g.mergeE(xx1).option(onCreate, xx2) + g.withSideEffect("x", xx2).mergeE(xx1).option(onCreate, select("x")) """ When iterated to list Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" # cannot override Direction.IN in onCreate - Scenario: g_mergeE_inV_override_prohibited + # as of 3.7.6, the error occurs on construction and can't be tested with gherkin +# Scenario: g_mergeE_inV_override_prohibited +# Given the empty graph +# And the graph initializer of +# """ +# g.addV("person").property("name", "marko"). +# addV("person").property("name", "vadas") +# """ +# And using the parameter xx1 defined as "m[{\"D[OUT]\" : \"v[marko].id\", \"D[IN]\" : \"v[vadas].id\", \"t[label]\": \"knows\"}]" +# And using the parameter xx2 defined as "m[{\"D[IN]\" : \"v[marko].id\"}]" +# And the traversal of +# """ +# g.mergeE(xx1).option(onCreate, xx2) +# """ +# When iterated to list +# Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + + # cannot override Direction.IN in onCreate + Scenario: g_mergeE_inV_dynamic_override_prohibited Given the empty graph And the graph initializer of """ @@ -749,13 +853,31 @@ Feature: Step - mergeE() And using the parameter xx2 defined as "m[{\"D[IN]\" : \"v[marko].id\"}]" And the traversal of """ - g.mergeE(xx1).option(onCreate, xx2) + g.withSideEffect("x", xx2).mergeE(xx1).option(onCreate, select("x")) """ When iterated to list Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" # cannot override T.label in onCreate - Scenario: g_mergeE_label_override_prohibited + # as of 3.7.6, the error occurs on construction and can't be tested with gherkin +# Scenario: g_mergeE_label_override_prohibited +# Given the empty graph +# And the graph initializer of +# """ +# g.addV("person").property("name", "marko"). +# addV("person").property("name", "vadas") +# """ +# And using the parameter xx1 defined as "m[{\"D[OUT]\" : \"v[marko].id\", \"D[IN]\" : \"v[vadas].id\", \"t[label]\": \"knows\"}]" +# And using the parameter xx2 defined as "m[{\"t[label]\": \"likes\"}]" +# And the traversal of +# """ +# g.mergeE(xx1).option(onCreate, xx2) +# """ +# When iterated to list +# Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + + # cannot override T.label in onCreate + Scenario: g_mergeE_label_dynamic_override_prohibited Given the empty graph And the graph initializer of """ @@ -766,14 +888,33 @@ Feature: Step - mergeE() And using the parameter xx2 defined as "m[{\"t[label]\": \"likes\"}]" And the traversal of """ - g.mergeE(xx1).option(onCreate, xx2) + g.withSideEffect("x", xx2).mergeE(xx1).option(onCreate, select("x")) """ When iterated to list Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + # cannot override T.id in onCreate + # as of 3.7.6, the error occurs on construction and can't be tested with gherkin +# @UserSuppliedEdgeIds +# Scenario: g_mergeE_id_override_prohibited +# Given the empty graph +# And the graph initializer of +# """ +# g.addV("person").property("name", "marko"). +# addV("person").property("name", "vadas") +# """ +# And using the parameter xx1 defined as "m[{\"D[OUT]\" : \"v[marko].id\", \"D[IN]\" : \"v[vadas].id\", \"t[label]\": \"knows\", \"t[id]\": \"101\"}]" +# And using the parameter xx2 defined as "m[{\"t[id]\": \"201\"}]" +# And the traversal of +# """ +# g.mergeE(xx1).option(onCreate, xx2) +# """ +# When iterated to list +# Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + # cannot override T.id in onCreate @UserSuppliedEdgeIds - Scenario: g_mergeE_id_override_prohibited + Scenario: g_mergeE_id_dynamic_override_prohibited Given the empty graph And the graph initializer of """ @@ -784,7 +925,7 @@ Feature: Step - mergeE() And using the parameter xx2 defined as "m[{\"t[id]\": \"201\"}]" And the traversal of """ - g.mergeE(xx1).option(onCreate, xx2) + g.withSideEffect("x", xx2).mergeE(xx1).option(onCreate, select("x")) """ When iterated to list Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature index 0b80e0a1e4..6c3d328947 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature @@ -705,30 +705,42 @@ Feature: Step - mergeV() And the graph should return 1 for count of "g.V()" And the graph should return 1 for count of "g.V(\"1\").has(\"person\",\"name\",\"mike\")" + # cannot override T.label in onCreate - as of 3.7.6, the error occurs on construction and can't be tested with gherkin +# Scenario: g_mergeV_label_override_prohibited +# Given the empty graph +# And using the parameter xx1 defined as "m[{\"t[label]\": \"a\"}]" +# And using the parameter xx2 defined as "m[{\"t[label]\": \"b\"}]" +# And the traversal of +# """ +# g.mergeV(xx1).option(onCreate, xx2) +# """ +# When iterated to list +# Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + # cannot override T.label in onCreate - Scenario: g_mergeV_label_override_prohibited + Scenario: g_mergeV_label_dynamic_override_prohibited Given the empty graph And using the parameter xx1 defined as "m[{\"t[label]\": \"a\"}]" And using the parameter xx2 defined as "m[{\"t[label]\": \"b\"}]" And the traversal of """ - g.mergeV(xx1).option(onCreate, xx2) + g.withSideEffect('x',xx2).mergeV(xx1).option(onCreate, select('x')) """ When iterated to list Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" - # cannot override T.id in onCreate - @UserSuppliedVertexIds - Scenario: g_mergeV_id_override_prohibited - Given the empty graph - And using the parameter xx1 defined as "m[{\"t[id]\": \"1\"}]" - And using the parameter xx2 defined as "m[{\"t[id]\": \"2\"}]" - And the traversal of - """ - g.mergeV(xx1).option(onCreate, xx2) - """ - When iterated to list - Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" + # cannot override T.id in onCreate - as of 3.7.6, the error occurs on construction and can't be tested with gherkin +# @UserSuppliedVertexIds +# Scenario: g_mergeV_id_override_prohibited +# Given the empty graph +# And using the parameter xx1 defined as "m[{\"t[id]\": \"1\"}]" +# And using the parameter xx2 defined as "m[{\"t[id]\": \"2\"}]" +# And the traversal of +# """ +# g.mergeV(xx1).option(onCreate, xx2) +# """ +# When iterated to list +# Then the traversal will raise an error with message containing text of "option(onCreate) cannot override values from merge() argument" # cannot use hidden namespace for id key Scenario: g_mergeV_hidden_id_key_prohibited
