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 67b8a334ebb312ebfafa404c39285d52e275ad2d Author: Stephen Mallette <[email protected]> AuthorDate: Wed Dec 13 11:44:05 2023 -0500 TINKERPOP-3022 Fixed JavaTranslator issues with has(String,null) Needed to better handle defaults for has(String,Traversal) on the case where reflection in JavaTranslator chose that method for has(String,null). CTR --- CHANGELOG.asciidoc | 1 + .../gremlin/process/traversal/dsl/graph/GraphTraversal.java | 12 ++++++++++++ .../test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 1 + gremlin-go/driver/cucumber/gremlin.go | 1 + .../javascript/gremlin-javascript/test/cucumber/gremlin.js | 1 + gremlin-python/src/main/python/radish/gremlin.py | 1 + .../tinkerpop/gremlin/test/features/filter/Has.feature | 11 ++++++++++- 7 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 60307513e1..fa3f2608fa 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima * Improved error message from `JavaTranslator` by including exception source. * Added tests for error handling for GLV's if tx.commit() is called remotely for graphs without transactions support. * Introduced multi-architecture AMD64/ARM64 docker images for gremlin-console. +* Fixed bug in `JavaTranslator` where `has(String, null)` could call `has(String, Traversal)` to generate an error. [[release-3-6-6]] === TinkerPop 3.6.6 (November 20, 2023) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java index d92d2e09e7..7fbfbd8a00 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java @@ -1564,6 +1564,11 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @since 3.0.0-incubating */ public default GraphTraversal<S, E> has(final String propertyKey, final P<?> predicate) { + // Groovy can get the overload wrong for has(T, null) which should probably go at has(T,Object). users could + // explicit cast but a redirect here makes this a bit more seamless + if (null == predicate) + return has(propertyKey, (Object) null); + this.asAdmin().getBytecode().addStep(Symbols.has, propertyKey, predicate); return TraversalHelper.addHasContainer(this.asAdmin(), new HasContainer(propertyKey, predicate)); } @@ -1710,6 +1715,13 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @since 3.0.0-incubating */ public default GraphTraversal<S, E> has(final String propertyKey, final Traversal<?, ?> propertyTraversal) { + // the translation here of null to has(String, Object) is likely what was intended. a null Traversal doesn't + // really make much sense. this should resolve issues with JavaTranslator grabbing this method when bytecode + // uses null as the second argument. we've taken this tactic for other overloads of has() as well, so just + // continuing with that pattern. + if (null == propertyTraversal) + return has(propertyKey, (Object) null); + this.asAdmin().getBytecode().addStep(Symbols.has, propertyKey, propertyTraversal); return this.asAdmin().addStep( new TraversalFilterStep<>(this.asAdmin(), propertyTraversal.asAdmin().addStep(0, diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 0044b6853d..ce4b3d75ab 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -226,6 +226,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_hasXlabel_personX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has(T.Label,"person")}}, {"g_V_hasXlabel_eqXpersonXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has(T.Label,P.Eq("person"))}}, {"g_V_hasXlabel_isXpersonXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has(T.Label,__.Is("person"))}}, + {"g_V_hasXname_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("name",(object) null)}}, {"g_V_hasIdXemptyX_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasId(p["xx1"]).Count()}}, {"g_V_hasIdXwithinXemptyXX_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasId(p["xx1"]).Count()}}, {"g_V_hasIdXwithoutXemptyXX_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasId(p["xx1"]).Count()}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index e8f4f9b7fb..3a9378226d 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -197,6 +197,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_hasXlabel_personX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has(gremlingo.T.Label, "person")}}, "g_V_hasXlabel_eqXpersonXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has(gremlingo.T.Label, gremlingo.P.Eq("person"))}}, "g_V_hasXlabel_isXpersonXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has(gremlingo.T.Label, gremlingo.T__.Is("person"))}}, + "g_V_hasXname_nullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", nil)}}, "g_V_hasIdXemptyX_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasId(p["xx1"]).Count()}}, "g_V_hasIdXwithinXemptyXX_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasId(p["xx1"]).Count()}}, "g_V_hasIdXwithoutXemptyXX_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasId(p["xx1"]).Count()}}, 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 fb8efe34c7..6b1fba4fb3 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 @@ -216,6 +216,7 @@ const gremlins = { g_V_hasXlabel_personX: [function({g}) { return g.V().has(T.label,"person") }], g_V_hasXlabel_eqXpersonXX: [function({g}) { return g.V().has(T.label,P.eq("person")) }], g_V_hasXlabel_isXpersonXX: [function({g}) { return g.V().has(T.label,__.is("person")) }], + g_V_hasXname_nullX: [function({g}) { return g.V().has("name",null) }], g_V_hasIdXemptyX_count: [function({g, xx1}) { return g.V().hasId(xx1).count() }], g_V_hasIdXwithinXemptyXX_count: [function({g, xx1}) { return g.V().hasId(xx1).count() }], g_V_hasIdXwithoutXemptyXX_count: [function({g, xx1}) { return g.V().hasId(xx1).count() }], diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index ba2c15fe97..84f373ca62 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -198,6 +198,7 @@ world.gremlins = { 'g_V_hasXlabel_personX': [(lambda g:g.V().has(T.label,'person'))], 'g_V_hasXlabel_eqXpersonXX': [(lambda g:g.V().has(T.label,P.eq('person')))], 'g_V_hasXlabel_isXpersonXX': [(lambda g:g.V().has(T.label,__.is_('person')))], + 'g_V_hasXname_nullX': [(lambda g:g.V().has('name',None))], 'g_V_hasIdXemptyX_count': [(lambda g, xx1=None:g.V().hasId(xx1).count())], 'g_V_hasIdXwithinXemptyXX_count': [(lambda g, xx1=None:g.V().hasId(xx1).count())], 'g_V_hasIdXwithoutXemptyXX_count': [(lambda g, xx1=None:g.V().hasId(xx1).count())], diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Has.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Has.feature index 1ae35324c6..aa71fcb4f0 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Has.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Has.feature @@ -611,4 +611,13 @@ Feature: Step - has() | v[marko] | | v[vadas] | | v[josh] | - | v[peter] | \ No newline at end of file + | v[peter] | + + Scenario: g_V_hasXname_nullX + Given the modern graph + And the traversal of + """ + g.V().has("name", null) + """ + When iterated to list + Then the result should be empty \ No newline at end of file
