This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch TINKERPOP-2613 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 1d2a8453e09fb53f721b076dc11fbb87d18bf244 Author: Stephen Mallette <stepm...@amazon.com> AuthorDate: Tue Nov 9 06:27:40 2021 -0500 TINKERPOP-2613 Improved behavior of V(null) Prevented NPE and instead filtered results. --- CHANGELOG.asciidoc | 1 + .../traversal/dsl/graph/GraphTraversal.java | 9 ++-- .../traversal/dsl/graph/GraphTraversalSource.java | 14 ++++-- .../gremlin/structure/util/ElementHelper.java | 4 +- .../translator/JavascriptTranslatorTest.java | 8 +++- .../Process/Traversal/GraphTraversal.cs | 11 ++++- .../Process/Traversal/GraphTraversalSource.cs | 22 +++++++-- .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 5 ++ gremlin-javascript/build/generate.groovy | 4 ++ .../gremlin-javascript/test/cucumber/gremlin.js | 5 ++ gremlin-python/src/main/python/radish/gremlin.py | 5 ++ gremlin-test/features/map/Vertex.feature | 54 ++++++++++++++++++++++ .../gremlin/tinkergraph/structure/TinkerGraph.java | 2 + 13 files changed, 126 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index d3fc76a..e3c1e7f 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -30,6 +30,7 @@ limitations under the License. * Created `gremlin-annotations` module where the `@GremlinDsl` annotation and related code has been moved. * Removed `groovy` and `groovy-json` dependencies from `gremlin-driver` as well as related `JsonBuilder` serialization support. * Replaced log4j usage with logback where builds rely on and packaged distributions now contain the latter. +* Improved behavior of `V()` when `null` is an argument producing a filtering behavior rather than an exception. * Prevented metrics computation unless the traversal is in a locked state. * Created a way to produce a corpus of Gremlin traversals via `FeatureReader` and `DocumentationReader` in `gremlin-language`. * Exposed Gherkin tests as part of the provider test suite. 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 9a45300..cd64f55 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 @@ -315,8 +315,10 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @since 3.1.0-incubating */ public default GraphTraversal<S, Vertex> V(final Object... vertexIdsOrElements) { - this.asAdmin().getBytecode().addStep(Symbols.V, vertexIdsOrElements); - return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), Vertex.class, false, vertexIdsOrElements)); + // a single null is [null] + final Object[] ids = null == vertexIdsOrElements ? new Object[] { null } : vertexIdsOrElements; + this.asAdmin().getBytecode().addStep(Symbols.V, ids); + return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), Vertex.class, false, ids)); } /** @@ -1606,8 +1608,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { public default GraphTraversal<S, E> hasId(final Object id, final Object... otherIds) { if (id instanceof P) { return this.hasId((P) id); - } - else { + } else { Object[] ids; if (id instanceof Object[]) { ids = (Object[]) id; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java index b7471b1..cae7e5c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java @@ -365,21 +365,25 @@ public class GraphTraversalSource implements TraversalSource { * unique identifier. */ public GraphTraversal<Vertex, Vertex> V(final Object... vertexIds) { + // a single null is [null] + final Object[] ids = null == vertexIds ? new Object[] { null } : vertexIds; final GraphTraversalSource clone = this.clone(); - clone.bytecode.addStep(GraphTraversal.Symbols.V, vertexIds); + clone.bytecode.addStep(GraphTraversal.Symbols.V, ids); final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone); - return traversal.addStep(new GraphStep<>(traversal, Vertex.class, true, vertexIds)); + return traversal.addStep(new GraphStep<>(traversal, Vertex.class, true, ids)); } /** * Spawns a {@link GraphTraversal} starting with all edges or some subset of edges as specified by their unique * identifier. */ - public GraphTraversal<Edge, Edge> E(final Object... edgesIds) { + public GraphTraversal<Edge, Edge> E(final Object... edgeIds) { + // a single null is [null] + final Object[] ids = null == edgeIds ? new Object[] { null } : edgeIds; final GraphTraversalSource clone = this.clone(); - clone.bytecode.addStep(GraphTraversal.Symbols.E, edgesIds); + clone.bytecode.addStep(GraphTraversal.Symbols.E, ids); final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone); - return traversal.addStep(new GraphStep<>(traversal, Edge.class, true, edgesIds)); + return traversal.addStep(new GraphStep<>(traversal, Edge.class, true, ids)); } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java index 48e8733..a742e7b 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java @@ -549,10 +549,10 @@ public final class ElementHelper { // it is OK to evaluate equality of ids via toString() now given that the toString() the test suite // enforces the value of id.()toString() to be a first class representation of the identifier if (1 == providedIds.length) { - return id.toString().equals(providedIds[0].toString()); + return id != null && providedIds[0] != null && id.toString().equals(providedIds[0].toString()); } else { for (final Object temp : providedIds) { - if (temp.toString().equals(id.toString())) + if (id != null && temp != null && temp.toString().equals(id.toString())) return true; } return false; diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java index 1a0f1c0..f2db89b 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java @@ -119,7 +119,13 @@ public class JavascriptTranslatorTest { @Test public void shouldHaveNull() { - translator.translate(g.inject(null, null).asAdmin().getBytecode()).getScript(); + assertEquals("g.inject(null,null)", translator.translate(g.inject(null, null).asAdmin().getBytecode()).getScript()); + assertEquals("g.V()", translator.translate(g.V().asAdmin().getBytecode()).getScript()); + assertEquals("g.V(null)", translator.translate(g.V(null).asAdmin().getBytecode()).getScript()); + assertEquals("g.V(null,null)", translator.translate(g.V(null, null).asAdmin().getBytecode()).getScript()); + assertEquals("g.E()", translator.translate(g.E().asAdmin().getBytecode()).getScript()); + assertEquals("g.E(null)", translator.translate(g.E(null).asAdmin().getBytecode()).getScript()); + assertEquals("g.E(null,null)", translator.translate(g.E(null, null).asAdmin().getBytecode()).getScript()); } @Test diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs index 2bc4de8..328e506 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs @@ -79,8 +79,15 @@ namespace Gremlin.Net.Process.Traversal /// </summary> public GraphTraversal<S, Vertex> V (params object[] vertexIdsOrElements) { - var args = new List<object>(0 + vertexIdsOrElements.Length) {}; - args.AddRange(vertexIdsOrElements); + var args = null == vertexIdsOrElements ? new List<object>(1) {} : new List<object>(0 + vertexIdsOrElements.Length) {}; + if (null == vertexIdsOrElements) + { + args.Add(null); + } + else + { + args.AddRange(vertexIdsOrElements); + } Bytecode.AddStep("V", args.ToArray()); return Wrap<S, Vertex>(this); } diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs index 2ab9d01..ae5b1d9 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs @@ -296,8 +296,15 @@ namespace Gremlin.Net.Process.Traversal public GraphTraversal<Edge, Edge> E(params object[] edgesIds) { var traversal = new GraphTraversal<Edge, Edge>(TraversalStrategies, new Bytecode(Bytecode)); - var args = new List<object>(0 + edgesIds.Length) {}; - args.AddRange(edgesIds); + var args = null == edgesIds ? new List<object>(1) {} : new List<object>(0 + edgesIds.Length) {}; + if (null == edgesIds) + { + args.Add(null); + } + else + { + args.AddRange(edgesIds); + } traversal.Bytecode.AddStep("E", args.ToArray()); return traversal; } @@ -309,8 +316,15 @@ namespace Gremlin.Net.Process.Traversal public GraphTraversal<Vertex, Vertex> V(params object[] vertexIds) { var traversal = new GraphTraversal<Vertex, Vertex>(TraversalStrategies, new Bytecode(Bytecode)); - var args = new List<object>(0 + vertexIds.Length) {}; - args.AddRange(vertexIds); + var args = null == vertexIds ? new List<object>(1) {} : new List<object>(0 + vertexIds.Length) {}; + if (null == vertexIds) + { + args.Add(null); + } + else + { + args.AddRange(vertexIds); + } traversal.Bytecode.AddStep("V", args.ToArray()); return traversal; } diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 2c8d242..8cd8876 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -646,6 +646,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Filter(__.OutE("created")).ValueMap<object,object>().With("~tinkerpop.valueMap.tokens")}}, {"g_VX1X_valueMapXname_locationX_byXunfoldX_by", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).ValueMap<object,object>("name","location").By(__.Unfold<object>()).By()}}, {"g_V_valueMapXname_age_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object,object>("name","age",null)}}, + {"g_VXnullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(null)}}, + {"g_VXlistXnullXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["xx1"])}}, + {"g_VX1_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"],null)}}, {"g_VXlistX1_2_3XX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["xx1"]).Values<object>("name")}}, {"g_VXlistXv1_v2_v3XX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["xx1"]).Values<object>("name")}}, {"g_V", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V()}}, @@ -686,6 +689,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_hasLabelXpersonX_V_hasLabelXsoftwareX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").V().HasLabel("software").Values<object>("name")}}, {"g_V_hasLabelXloopsX_bothEXselfX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("loops").BothE("self")}}, {"g_V_hasLabelXloopsX_bothXselfX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("loops").Both("self")}}, + {"g_injectX1X_VXnullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(1).V(null)}}, + {"g_injectX1X_VX1_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(1).V(p["vid1"],null)}}, {"Primitives_Number_eqXintX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Unfold<object>().Where(__.Is(p["xx2"]))}}, {"g_V_valueXnameX_aggregateXxX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Aggregate("x").Cap<object>("x")}}, {"g_V_valueXnameX_aggregateXglobal_xX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Aggregate(Scope.Global,"x").Cap<object>("x")}}, diff --git a/gremlin-javascript/build/generate.groovy b/gremlin-javascript/build/generate.groovy index 75e7087..572d29c 100644 --- a/gremlin-javascript/build/generate.groovy +++ b/gremlin-javascript/build/generate.groovy @@ -99,6 +99,10 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer -> // solution may become necessary as testing of nulls expands. def staticTranslate = [ g_injectXnull_nullX: " g_injectXnull_nullX: [function({g}) { return g.inject(null,null) }], ", + g_V_hasIdXnullX: " g_V_hasIdXnullX: [function({g}) { return g.V().hasId(null) }], ", + g_V_hasIdX1_nullX: " g_V_hasIdX1_nullX: [function({g, vid1}) { return g.V().hasId(vid1,null) }], ", + g_injectX1X_VXnullX: " g_injectX1X_VXnullX: [function({g}) { return g.inject(1).V(null) }], ", + g_injectX1X_VX1_nullX: " g_injectX1X_VX1_nullX: [function({g, vid1}) { return g.inject(1).V(vid1,null) }], ", g_VX1X_valuesXageX_injectXnull_nullX: " g_VX1X_valuesXageX_injectXnull_nullX: [function({g, xx1}) { return g.V(xx1).values(\"age\").inject(null,null) }], " ] 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 8507405..a993219 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 @@ -635,6 +635,9 @@ const gremlins = { g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX: [function({g}) { return g.V().hasLabel("person").filter(__.outE("created")).valueMap().with_("~tinkerpop.valueMap.tokens") }], g_VX1X_valueMapXname_locationX_byXunfoldX_by: [function({g, vid1}) { return g.V(vid1).valueMap("name","location").by(__.unfold()).by() }], g_V_valueMapXname_age_nullX: [function({g}) { return g.V().valueMap("name","age",null) }], + g_VXnullX: [function({g}) { return g.V(null) }], + g_VXlistXnullXX: [function({g, xx1}) { return g.V(xx1) }], + g_VX1_nullX: [function({g, vid1}) { return g.V(vid1,null) }], g_VXlistX1_2_3XX_name: [function({g, xx1}) { return g.V(xx1).values("name") }], g_VXlistXv1_v2_v3XX_name: [function({g, xx1}) { return g.V(xx1).values("name") }], g_V: [function({g}) { return g.V() }], @@ -675,6 +678,8 @@ const gremlins = { g_V_hasLabelXpersonX_V_hasLabelXsoftwareX_name: [function({g}) { return g.V().hasLabel("person").V().hasLabel("software").values("name") }], g_V_hasLabelXloopsX_bothEXselfX: [function({g}) { return g.V().hasLabel("loops").bothE("self") }], g_V_hasLabelXloopsX_bothXselfX: [function({g}) { return g.V().hasLabel("loops").both("self") }], + g_injectX1X_VXnullX: [function({g}) { return g.inject(1).V(null) }], + g_injectX1X_VX1_nullX: [function({g, vid1}) { return g.inject(1).V(vid1,null) }], Primitives_Number_eqXintX: [function({g, xx1, xx2}) { return g.inject(xx1).unfold().where(__.is(xx2)) }], g_V_valueXnameX_aggregateXxX_capXxX: [function({g}) { return g.V().values("name").aggregate("x").cap("x") }], g_V_valueXnameX_aggregateXglobal_xX_capXxX: [function({g}) { return g.V().values("name").aggregate(Scope.global,"x").cap("x") }], diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 5f9535a..e4b187f 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -620,6 +620,9 @@ world.gremlins = { 'g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX': [(lambda g:g.V().hasLabel('person').filter(__.outE('created')).valueMap().with_('~tinkerpop.valueMap.tokens'))], 'g_VX1X_valueMapXname_locationX_byXunfoldX_by': [(lambda g, vid1=None:g.V(vid1).valueMap('name','location').by(__.unfold()).by())], 'g_V_valueMapXname_age_nullX': [(lambda g:g.V().valueMap('name','age',None))], + 'g_VXnullX': [(lambda g:g.V(None))], + 'g_VXlistXnullXX': [(lambda g, xx1=None:g.V(xx1))], + 'g_VX1_nullX': [(lambda g, vid1=None:g.V(vid1,None))], 'g_VXlistX1_2_3XX_name': [(lambda g, xx1=None:g.V(xx1).name)], 'g_VXlistXv1_v2_v3XX_name': [(lambda g, xx1=None:g.V(xx1).name)], 'g_V': [(lambda g:g.V())], @@ -660,6 +663,8 @@ world.gremlins = { 'g_V_hasLabelXpersonX_V_hasLabelXsoftwareX_name': [(lambda g:g.V().hasLabel('person').V().hasLabel('software').name)], 'g_V_hasLabelXloopsX_bothEXselfX': [(lambda g:g.V().hasLabel('loops').bothE('self'))], 'g_V_hasLabelXloopsX_bothXselfX': [(lambda g:g.V().hasLabel('loops').both('self'))], + 'g_injectX1X_VXnullX': [(lambda g:g.inject(1).V(None))], + 'g_injectX1X_VX1_nullX': [(lambda g, vid1=None:g.inject(1).V(vid1,None))], 'Primitives_Number_eqXintX': [(lambda g, xx1=None,xx2=None:g.inject(xx1).unfold().where(__.is_(xx2)))], 'g_V_valueXnameX_aggregateXxX_capXxX': [(lambda g:g.V().name.aggregate('x').cap('x'))], 'g_V_valueXnameX_aggregateXglobal_xX_capXxX': [(lambda g:g.V().name.aggregate(Scope.global_,'x').cap('x'))], diff --git a/gremlin-test/features/map/Vertex.feature b/gremlin-test/features/map/Vertex.feature index 37738bb..3e78c82 100644 --- a/gremlin-test/features/map/Vertex.feature +++ b/gremlin-test/features/map/Vertex.feature @@ -18,6 +18,37 @@ @StepClassMap @StepVertex Feature: Step - V(), E(), out(), in(), both(), inE(), outE(), bothE() + Scenario: g_VXnullX + Given the modern graph + And the traversal of + """ + g.V(null) + """ + When iterated to list + Then the result should be empty + + Scenario: g_VXlistXnullXX + Given the modern graph + And using the parameter xx1 defined as "l[null]" + And the traversal of + """ + g.V(xx1) + """ + When iterated to list + Then the result should be empty + + Scenario: g_VX1_nullX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1,null) + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | + Scenario: g_VXlistX1_2_3XX_name Given the modern graph And using the parameter xx1 defined as "l[v[marko].id,v[vadas].id,v[lop].id]" @@ -579,3 +610,26 @@ Feature: Step - V(), E(), out(), in(), both(), inE(), outE(), bothE() | result | | v[loop] | | v[loop] | + + @GraphComputerVerificationInjectionNotSupported @GraphComputerVerificationMidVNotSupported + Scenario: g_injectX1X_VXnullX + Given the modern graph + And the traversal of + """ + g.inject(1).V(null) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationInjectionNotSupported @GraphComputerVerificationMidVNotSupported + Scenario: g_injectX1X_VX1_nullX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.inject(1).V(vid1,null) + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | \ No newline at end of file diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java index 0343302..b9dda16 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java @@ -316,6 +316,8 @@ public final class TinkerGraph implements Graph { // the assumption is that if it's already an Element, its identifier must be valid to the Graph and to // its associated IdManager. All other objects are passed to the IdManager for conversion. return new TinkerGraphIterator<>(IteratorUtils.filter(IteratorUtils.map(idList, id -> { + // ids cant be null so all of those filter out + if (null == id) return null; final Object iid = clazz.isAssignableFrom(id.getClass()) ? clazz.cast(id).id() : idManager.convert(id); return elements.get(idManager.convert(iid)); }).iterator(), Objects::nonNull));