Skip to content

Commit

Permalink
TINKERPOP-3137 Allowed null as a Map value in mergeV/E
Browse files Browse the repository at this point in the history
  • Loading branch information
spmallette committed Feb 19, 2025
1 parent ab95d38 commit b668a09
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 12 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Fixed issue in `gremlin-console` where it couldn't accept plugin files that included empty lines or invalid plugin names.
* Modified grammar to make `none()` usage more consistent as a filter step where it can now be used to chain additional traversal steps and be used anonymously.
* Added missing anonymous support for `disjunct()` in Python and Javascript.
* Deprecated gremlin_python.process.__.has_key_ in favor of gremlin_python.process.__.has_key.
* Deprecated `gremlin_python.process.__.has_key_` in favor of `gremlin_python.process.__.has_key`.
* Allowed `mergeV()` and `mergeE()` to supply `null` in `Map` values.
[[release-3-7-3]]
=== TinkerPop 3.7.3 (October 23, 2024)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,17 +271,18 @@ protected static void validate(final Map map, final boolean ignoreTokens, final
final Object k = e.getKey();
final Object v = e.getValue();

if (v == null) {
throw new IllegalArgumentException(String.format("%s() does not allow null Map values - check: %s", op, k));
}

if (ignoreTokens) {
if (!(k instanceof String)) {
throw new IllegalArgumentException(String.format("option(onMatch) expects keys in Map to be of String - check: %s", k));
} else {
ElementHelper.validateProperty((String) k, v);
}
} else {
// don't allow null values for enums: T, Direction, Merge
if (k instanceof Enum && v == null) {
throw new IllegalArgumentException(String.format("%s() does not allow null Map values - check: %s", op, k));
}

if (!(k instanceof String) && !allowedTokens.contains(k)) {
throw new IllegalArgumentException(String.format(
"%s() and option(onCreate) args expect keys in Map to be either String or %s - check: %s",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,23 @@ public void shouldFailToValidateWithNullLabelValue() {
}

@Test(expected = IllegalArgumentException.class)
public void shouldFailToValidateWithObjectAsLabelValue() {
public void shouldFailToValidateWithNullIdValue() {
final Map<Object,Object> m = CollectionUtil.asMap("k", "v",
T.label, new Object());
T.id, null);
MergeVertexStep.validateMapInput(m, false);
}

@Test(expected = IllegalArgumentException.class)
public void shouldFailToValidateWithNullIdValue() {
public void shouldFailToValidateWithNullMergeValue() {
final Map<Object,Object> m = CollectionUtil.asMap("k", "v",
T.id, null);
Merge.inV, null);
MergeVertexStep.validateMapInput(m, false);
}

@Test(expected = IllegalArgumentException.class)
public void shouldFailToValidateWithObjectAsLabelValue() {
final Map<Object,Object> m = CollectionUtil.asMap("k", "v",
T.label, new Object());
MergeVertexStep.validateMapInput(m, false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,8 @@ private static IDictionary<string, List<Func<GraphTraversalSource, IDictionary<s
{"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonMatch_sideEffectXpropertyXweight_0XX_constantXemptyXX", 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").Property("weight",1).From("a").To("b"), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnMatch, (ITraversal) __.SideEffect(__.Property("weight",0)).Constant<object>(new Dictionary<object,object> {})), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows").Has("weight",1), (g,p) =>g.E().HasLabel("knows").Has("weight",0), (g,p) =>g.V().Has("weight")}},
{"g_injectXlist1_list2X_mergeEXlimitXlocal_1XX_optionXonCreate_rangeXlocal_1_2XX_optionXonMatch_tailXlocalXX_to_match", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko").AddV("person").Property("name","vadas").Property("age",27).As("vadas").AddV("software").Property("name","lop").Property("lang","java").As("lop").AddV("person").Property("name","josh").Property("age",32).As("josh").AddV("software").Property("name","ripple").Property("lang","java").As("ripple").AddV("person").Property("name","peter").Property("age",35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight",0.5).AddE("knows").From("marko").To("josh").Property("weight",1.0).AddE("created").From("marko").To("lop").Property("weight",0.4).AddE("created").From("josh").To("ripple").Property("weight",1.0).AddE("created").From("josh").To("lop").Property("weight",0.4).AddE("created").From("peter").To("lop").Property("weight",0.2), (g,p) =>g.Inject(p["xx1"],p["xx1"],p["xx2"]).Fold().MergeE((ITraversal) __.Limit<object>(Scope.Local,1)).Option(Merge.OnCreate, (ITraversal) __.Range<object>(Scope.Local,1,2)).Option(Merge.OnMatch, (ITraversal) __.Tail<object>(Scope.Local)), (g,p) =>g.V(), (g,p) =>g.E(), (g,p) =>g.E().Has("created","N"), (g,p) =>g.V().Has("person","name","marko").OutE("knows").Has("created","N").InV().Has("person","name","vadas")}},
{"g_injectXlist1_list2X_mergeEXlimitXlocal_1XX_optionXonCreate_rangeXlocal_1_2XX_optionXonMatch_tailXlocalXX_to_create", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko").AddV("person").Property("name","vadas").Property("age",27).As("vadas").AddV("software").Property("name","lop").Property("lang","java").As("lop").AddV("person").Property("name","josh").Property("age",32).As("josh").AddV("software").Property("name","ripple").Property("lang","java").As("ripple").AddV("person").Property("name","peter").Property("age",35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight",0.5).AddE("knows").From("marko").To("josh").Property("weight",1.0).AddE("created").From("marko").To("lop").Property("weight",0.4).AddE("created").From("josh").To("ripple").Property("weight",1.0).AddE("created").From("josh").To("lop").Property("weight",0.4).AddE("created").From("peter").To("lop").Property("weight",0.2), (g,p) =>g.Inject(p["xx1"],p["xx1"],p["xx2"]).Fold().MergeE((ITraversal) __.Limit<object>(Scope.Local,1)).Option(Merge.OnCreate, (ITraversal) __.Range<object>(Scope.Local,1,2)).Option(Merge.OnMatch, (ITraversal) __.Tail<object>(Scope.Local)), (g,p) =>g.V(), (g,p) =>g.E(), (g,p) =>g.E().HasNot("created"), (g,p) =>g.V().Has("person","name","marko").OutE("knows").HasNot("created").InV().Has("person","name","vadas"), (g,p) =>g.V().Has("person","name","vadas").OutE("self").HasNot("weight").InV().Has("person","name","vadas")}},
{"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonMatch_weight_nullX_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("weight",1.0), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows"), (g,p) =>g.E().HasLabel("knows").Has("weight",(object) null)}},
{"g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonMatch_weight_nullX", 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("weight",1.0), (g,p) =>g.MergeE((IDictionary<object,object>) p["xx1"]).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.E().HasLabel("knows"), (g,p) =>g.E().HasLabel("knows").Has("weight")}},
{"g_mergeVXemptyX_optionXonMatch_nullX", 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>) new Dictionary<object,object> {}).Option(Merge.OnMatch, (IDictionary<object,object>) null), (g,p) =>g.V().Has("person","name","marko").Has("age",29)}},
{"g_V_mergeVXemptyX_optionXonMatch_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (g,p) =>g.V().MergeV((IDictionary<object,object>) new Dictionary<object,object> {}).Option(Merge.OnMatch, (IDictionary<object,object>) null), (g,p) =>g.V().Has("person","name","marko").Has("age",29)}},
{"g_mergeVXnullX_optionXonCreate_label_null_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"])}},
Expand Down Expand Up @@ -982,6 +984,8 @@ private static IDictionary<string, List<Func<GraphTraversalSource, IDictionary<s
{"g_mergeV_hidden_label_key_onMatch_matched_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {}).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx1"])}},
{"g_injectXlist1_list2X_mergeVXlimitXlocal_1XX_optionXonCreate_rangeXlocal_1_2X_optionXonMatch_tailXlocalXX_to_match", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (g,p) =>g.Inject(p["xx1"],p["xx1"],p["xx2"]).Fold().MergeV((ITraversal) __.Limit<object>(Scope.Local,1)).Option(Merge.OnCreate, (ITraversal) __.Range<object>(Scope.Local,1,2)).Option(Merge.OnMatch, (ITraversal) __.Tail<object>(Scope.Local)), (g,p) =>g.V().Has("person","name","marko").Has("created","N"), (g,p) =>g.V()}},
{"g_injectXlist1_list2X_mergeVXlimitXlocal_1XX_optionXonCreate_rangeXlocal_1_2X_optionXonMatch_tailXlocalXX_to_create", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29), (g,p) =>g.Inject(p["xx1"],p["xx1"],p["xx2"]).Fold().MergeV((ITraversal) __.Limit<object>(Scope.Local,1)).Option(Merge.OnCreate, (ITraversal) __.Range<object>(Scope.Local,1,2)).Option(Merge.OnMatch, (ITraversal) __.Tail<object>(Scope.Local)), (g,p) =>g.V().Has("person","name","stephen").HasNot("created"), (g,p) =>g.V()}},
{"g_mergeVXlabel_person_name_marko_age_29X_optionXonMatch_age_nullX_allowed", 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.OnMatch, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.V().Has("person","name","marko").Has("age",(object) null)}},
{"g_mergeVXlabel_person_name_marko_age_29X_optionXonMatch_age_nullX", 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.OnMatch, (IDictionary<object,object>) p["xx2"]), (g,p) =>g.V(), (g,p) =>g.V().Has("person","name","marko").Has("age")}},
{"g_V_age_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Min<object>()}},
{"g_V_foo_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Min<object>()}},
{"g_V_name_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Min<object>()}},
Expand Down
Loading

0 comments on commit b668a09

Please sign in to comment.