This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch TINKERPOP-2235 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit b15437c6fc10b14ddf03389c154f3509e2eb0d86 Author: stephen <[email protected]> AuthorDate: Wed Nov 20 08:39:09 2019 -0500 TINKERPOP-2235 Refactored Scoping interface Pulled back all functionality that grabs scope values to Scoping. In this way we have just one place for this logic. Also decided to use exceptions to deal with keys not being found. Not thinking the performance costs of the exception will matter here much as we won't be generating exceptions repeatedly for purpose of flow control - we really just generate them when there is actual error in the traversal syntax and the traversal is headed for exception anyway. if we find that this is [...] --- .../gremlin/process/traversal/step/Scoping.java | 83 +++++++++++++++++----- .../traversal/step/filter/DedupGlobalStep.java | 4 +- .../traversal/step/filter/WherePredicateStep.java | 4 +- .../traversal/step/filter/WhereTraversalStep.java | 4 +- .../process/traversal/step/map/SelectOneStep.java | 31 ++------ .../process/traversal/step/map/SelectStep.java | 15 ++-- .../traversal/step/map/TraversalSelectStep.java | 51 ++++++------- .../Gherkin/TraversalEvaluation/TraversalParser.cs | 3 +- gremlin-test/features/sideEffect/Inject.feature | 14 +++- .../traversal/step/sideEffect/InjectTest.java | 17 +++++ 10 files changed, 135 insertions(+), 91 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java index dee401c..e2c0e17 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java @@ -24,7 +24,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import java.util.Map; -import java.util.Optional; import java.util.Set; /** @@ -110,28 +109,54 @@ public interface Scoping { public enum Variable {START, END} - public default <S> S getScopeValue(final Pop pop, final String key, final Traverser.Admin<?> traverser) throws IllegalArgumentException { - return Optional.<S>ofNullable(getNullableScopeValue(pop, key, traverser)).orElseThrow( - () -> new IllegalArgumentException("Neither the sideEffects, map, nor path has a " + key + "-key: " + this)); + /** + * Finds the object with the specified key for the current traverser and throws an exception if the key cannot + * be found. + * + * @throws KeyNotFoundException if the key does not exist + */ + public default <S> S getScopeValue(final Pop pop, final Object key, final Traverser.Admin<?> traverser) throws KeyNotFoundException { + final Object object = traverser.get(); + if (object instanceof Map && ((Map) object).containsKey(key)) + return (S) ((Map) object).get(key); + + if (key instanceof String) { + final String k = (String) key; + if (traverser.getSideEffects().exists(k)) + return traverser.getSideEffects().get(k); + + final Path path = traverser.path(); + if (path.hasLabel(k)) + return null == pop ? path.get(k) : path.get(pop, k); + } + + throw new KeyNotFoundException(key, this); } - public default <S> S getNullableScopeValue(final Pop pop, final String key, final Traverser.Admin<?> traverser) { - return getScopeValue(pop, key, traverser, null); + /** + * Calls {@link #getScopeValue(Pop, Object, Traverser.Admin)} but throws an unchecked {@code IllegalStateException} + * if the key cannot be found. + */ + public default <S> S getSafeScopeValue(final Pop pop, final Object key, final Traverser.Admin<?> traverser) { + try { + return getScopeValue(pop, key, traverser); + } catch (KeyNotFoundException nfe) { + throw new IllegalArgumentException(nfe.getMessage(), nfe); + } } - public default <S> S getScopeValue(final Pop pop, final String key, final Traverser.Admin<?> traverser, final S valueIfNotFound) { - final Object object = traverser.get(); - if (object instanceof Map && ((Map<String, S>) object).containsKey(key)) - return ((Map<String, S>) object).get(key); - /// - if (traverser.getSideEffects().exists(key)) - return traverser.getSideEffects().get(key); - /// - final Path path = traverser.path(); - if (path.hasLabel(key)) - return path.get(pop, key); - /// - return valueIfNotFound; + /** + * Calls {@link #getScopeValue(Pop, Object, Traverser.Admin)} and returns {@code null} if the key is not found. + * Use this method with caution as {@code null} has one of two meanings as a return value. It could be that the + * key was found and its value was {@code null} or it might mean that the key was not found and {@code null} was + * simply returned. + */ + public default <S> S getNullableScopeValue(final Pop pop, final String key, final Traverser.Admin<?> traverser) { + try { + return getScopeValue(pop, key, traverser); + } catch (KeyNotFoundException nfe) { + return null; + } } /** @@ -140,4 +165,24 @@ public interface Scoping { * @return the accessed labels of the scoping step */ public Set<String> getScopeKeys(); + + public static class KeyNotFoundException extends Exception { + + private final Object key; + private final Scoping step; + + public KeyNotFoundException(final Object key, final Scoping current) { + super("Neither the map, sideEffects, nor path has a " + key + "-key: " + current); + this.key = key; + this.step = current; + } + + public Object getKey() { + return key; + } + + public Scoping getStep() { + return step; + } + } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java index b4f70d9..911d2eb 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java @@ -74,7 +74,7 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal return this.duplicateSet.add(TraversalUtil.applyNullable(traverser, this.dedupTraversal)); } else { final List<Object> objects = new ArrayList<>(this.dedupLabels.size()); - this.dedupLabels.forEach(label -> objects.add(TraversalUtil.applyNullable((S) this.getScopeValue(Pop.last, label, traverser), this.dedupTraversal))); + this.dedupLabels.forEach(label -> objects.add(TraversalUtil.applyNullable((S) this.getSafeScopeValue(Pop.last, label, traverser), this.dedupTraversal))); return this.duplicateSet.add(objects); } } @@ -189,7 +189,7 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal if (null != this.dedupLabels) { object = new ArrayList<>(this.dedupLabels.size()); for (final String label : this.dedupLabels) { - ((List) object).add(TraversalUtil.applyNullable((S) this.getScopeValue(Pop.last, label, traverser), this.dedupTraversal)); + ((List) object).add(TraversalUtil.applyNullable((S) this.getSafeScopeValue(Pop.last, label, traverser), this.dedupTraversal)); } } else { object = TraversalUtil.applyNullable(traverser, this.dedupTraversal); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java index 240a4cc..62a88f4 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java @@ -79,7 +79,7 @@ public final class WherePredicateStep<S> extends FilterStep<S> implements Scopin if (predicate instanceof ConnectiveP) ((ConnectiveP<Object>) predicate).getPredicates().forEach(p -> this.setPredicateValues(p, traverser, selectKeysIterator)); else - predicate.setValue(TraversalUtil.applyNullable((S) this.getScopeValue(Pop.last, selectKeysIterator.next(), traverser), this.traversalRing.next())); + predicate.setValue(TraversalUtil.applyNullable((S) this.getSafeScopeValue(Pop.last, selectKeysIterator.next(), traverser), this.traversalRing.next())); } public Optional<P<?>> getPredicate() { @@ -99,7 +99,7 @@ public final class WherePredicateStep<S> extends FilterStep<S> implements Scopin protected boolean filter(final Traverser.Admin<S> traverser) { final Object value = null == this.startKey ? TraversalUtil.applyNullable(traverser, this.traversalRing.next()) : - TraversalUtil.applyNullable((S) this.getScopeValue(Pop.last, this.startKey, traverser), this.traversalRing.next()); + TraversalUtil.applyNullable((S) this.getSafeScopeValue(Pop.last, this.startKey, traverser), this.traversalRing.next()); this.setPredicateValues(this.predicate, traverser, this.selectKeys.iterator()); this.traversalRing.reset(); return this.predicate.test(value); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java index 8c701a9..264575d 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java @@ -164,7 +164,7 @@ public final class WhereTraversalStep<S> extends FilterStep<S> implements Traver ((WhereEndStep) this.getTraversal().getEndStep()).processStartTraverser(traverser); else if (this.getTraversal().getEndStep() instanceof ProfileStep && this.getTraversal().getEndStep().getPreviousStep() instanceof WhereEndStep) // TOTAL SUCKY HACK! ((WhereEndStep) this.getTraversal().getEndStep().getPreviousStep()).processStartTraverser(traverser); - return null == this.selectKey ? traverser.get() : this.getScopeValue(Pop.last, this.selectKey, traverser); + return null == this.selectKey ? traverser.get() : this.getSafeScopeValue(Pop.last, this.selectKey, traverser); } @Override @@ -199,7 +199,7 @@ public final class WhereTraversalStep<S> extends FilterStep<S> implements Traver public void processStartTraverser(final Traverser.Admin traverser) { if (null != this.matchKey) - this.matchValue = this.getScopeValue(Pop.last, this.matchKey, traverser); + this.matchValue = this.getSafeScopeValue(Pop.last, this.matchKey, traverser); } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java index 44e22fe..1f3ebdc 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java @@ -18,7 +18,6 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; -import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.Pop; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; @@ -33,9 +32,7 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; @@ -59,36 +56,16 @@ public final class SelectOneStep<S, E> extends MapStep<S, E> implements Traversa protected Traverser.Admin<E> processNextStart() throws NoSuchElementException { final Traverser.Admin<S> traverser = this.starts.next(); - final Object object = traverser.get(); - if (object instanceof Map && ((Map<String, S>) object).containsKey(this.selectKey)) { - final S o = ((Map<String, S>) object).get(this.selectKey); + try { + final S o = getScopeValue(pop, selectKey, traverser); if (null == o) return traverser.split(null, this); final Traverser.Admin<E> outTraverser = traverser.split(TraversalUtil.applyNullable(o, this.selectTraversal), this); if (!(this.getTraversal().getParent() instanceof MatchStep)) PathProcessor.processTraverserPathLabels(outTraverser, this.keepLabels); return outTraverser; + } catch (KeyNotFoundException nfe) { + return EmptyTraverser.instance(); } - - if (traverser.getSideEffects().exists(this.selectKey)) { - final S o = (S) traverser.getSideEffects().get(this.selectKey); - if (null == o) return traverser.split(null, this); - final Traverser.Admin<E> outTraverser = traverser.split(TraversalUtil.applyNullable(o, this.selectTraversal), this); - if (!(this.getTraversal().getParent() instanceof MatchStep)) - PathProcessor.processTraverserPathLabels(outTraverser, this.keepLabels); - return outTraverser; - } - - final Path path = traverser.path(); - if (path.hasLabel(this.selectKey)) { - final S o = (S) path.get(pop, this.selectKey); - if (null == o) return traverser.split(null, this); - final Traverser.Admin<E> outTraverser = traverser.split(TraversalUtil.applyNullable(o, this.selectTraversal), this); - if (!(this.getTraversal().getParent() instanceof MatchStep)) - PathProcessor.processTraverserPathLabels(outTraverser, this.keepLabels); - return outTraverser; - } - - return EmptyTraverser.instance(); } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java index 2fa3e1f..5034c84 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java @@ -66,16 +66,17 @@ public final class SelectStep<S, E> extends MapStep<S, Map<String, E>> implement protected Traverser.Admin<Map<String, E>> processNextStart() throws NoSuchElementException { final Traverser.Admin<S> traverser = this.starts.next(); final Map<String, E> bindings = new LinkedHashMap<>(this.selectKeys.size(), 1.0f); - for (final String selectKey : this.selectKeys) { - final E end = this.getNullableScopeValue(this.pop, selectKey, traverser); - if (null != end) + try { + for (final String selectKey : this.selectKeys) { + final E end = this.getScopeValue(this.pop, selectKey, traverser); bindings.put(selectKey, TraversalUtil.applyNullable(end, this.traversalRing.next())); - else { - this.traversalRing.reset(); - return EmptyTraverser.instance(); } + } catch (KeyNotFoundException nfe) { + return EmptyTraverser.instance(); + } finally { + this.traversalRing.reset(); } - this.traversalRing.reset(); + return PathProcessor.processTraverserPathLabels(traverser.split(bindings, this), this.keepLabels); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java index b1b1263..3813454 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java @@ -18,12 +18,12 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; -import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.Pop; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating; import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor; +import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser; @@ -34,14 +34,12 @@ import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Set; - /** * @author Daniel Kuppitz (http://gremlin.guru) */ -public final class TraversalSelectStep<S, E> extends MapStep<S, E> implements TraversalParent, PathProcessor, ByModulating { +public final class TraversalSelectStep<S, E> extends MapStep<S, E> implements TraversalParent, PathProcessor, ByModulating, Scoping { private final Pop pop; private Traversal.Admin<S, E> keyTraversal; @@ -57,43 +55,36 @@ public final class TraversalSelectStep<S, E> extends MapStep<S, E> implements Tr @Override protected Traverser.Admin<E> processNextStart() { final Traverser.Admin<S> traverser = this.starts.next(); - - boolean found = false; - E end = null; final Iterator<E> keyIterator = TraversalUtil.applyAll(traverser, this.keyTraversal); if (keyIterator.hasNext()) { final E key = keyIterator.next(); - final Object object = traverser.get(); - if (object instanceof Map && ((Map) object).containsKey(key)) { - end = (E) ((Map) object).get(key); - found = true; - } else if (key instanceof String) { - final String skey = (String) key; - if (traverser.getSideEffects().exists(skey)) { - end = traverser.getSideEffects().get((String) key); - found = true; - } else { - final Path path = traverser.path(); - if (path.hasLabel(skey)) { - end = null == pop ? path.get(skey) : path.get(pop, skey); - found = true; - } + try { + final E end = getScopeValue(pop, key, traverser); + final Traverser.Admin<E> outTraverser = traverser.split(null == end ? null : TraversalUtil.applyNullable(end, this.selectTraversal), this); + if (!(this.getTraversal().getParent() instanceof MatchStep)) { + PathProcessor.processTraverserPathLabels(outTraverser, this.keepLabels); } + return outTraverser; + } catch (KeyNotFoundException nfe) { + return EmptyTraverser.instance(); } - } - - if (found) { - final Traverser.Admin<E> outTraverser = traverser.split(null == end ? null : TraversalUtil.applyNullable(end, this.selectTraversal), this); - if (!(this.getTraversal().getParent() instanceof MatchStep)) { - PathProcessor.processTraverserPathLabels(outTraverser, this.keepLabels); - } - return outTraverser; } else { return EmptyTraverser.instance(); } } @Override + public Set<String> getScopeKeys() { + // can't return scope keys here because they aren't known prior to traversal execution and this method is + // used at strategy application time. not getting any test failures as a result of returning empty. assuming + // that strategies don't use Scoping in a way that requires the keys to be known and if they aren't doesn't + // hose the whole traversal. in the worst case, strategies will hopefully just leave steps alone rather than + // make their own assumptions that the step is not selecting anything. if that is happening somehow we might + // need to modify Scoping to better suite this runtime evaluation of the key. + return Collections.emptySet(); + } + + @Override public String toString() { return StringFactory.stepString(this, this.pop, this.keyTraversal, this.selectTraversal); } diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs index e81e00e..b747200 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs @@ -38,7 +38,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { { "g.V().fold().count(Scope.local)", g => g.V().Fold().Count(Scope.Local)}, { "g.inject(10,20,null,20,10,10).groupCount(\"x\").dedup().as(\"y\").project(\"a\",\"b\").by().by(__.select(\"x\").select(__.select(\"y\")))", - g => g.Inject<object>(10,20,null,20,10,10).GroupCount("x").Dedup().As("y").Project<object>("a","b").By().By(__.Select<int>("x").Select<object>(__.Select<int>("y")))} + g => g.Inject<object>(10,20,null,20,10,10).GroupCount("x").Dedup().As("y").Project<object>("a","b").By().By(__.Select<int>("x").Select<object>(__.Select<int>("y")))}, + { "g.inject(m).select(\"name\",\"age\")", g => g.Inject(new Dictionary<string,object>() {{"name", "marko"}, {"age", null}}).Select<object>("name", "age") } }; private static readonly Regex RegexNumeric = diff --git a/gremlin-test/features/sideEffect/Inject.feature b/gremlin-test/features/sideEffect/Inject.feature index 7e4983d..20d6bd2 100644 --- a/gremlin-test/features/sideEffect/Inject.feature +++ b/gremlin-test/features/sideEffect/Inject.feature @@ -92,4 +92,16 @@ Feature: Step - inject() | result | | m[{"a":"d[10].i", "b":"d[3].l"}] | | m[{"a":"d[20].i", "b":"d[2].l"}] | - | m[{"a":null, "b":"d[1].l"}] | \ No newline at end of file + | m[{"a":null, "b":"d[1].l"}] | + + Scenario: g_injectXname_marko_age_nullX_selectXname_ageX + Given the empty graph + And using the parameter m defined as "m[{\"name\":\"marko\", \"age\":null}]" + And the traversal of + """ + g.inject(m).select("name","age") + """ + When iterated to list + Then the result should be unordered + | result | + | m[{"name":"marko", "age":null}] | \ No newline at end of file diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectTest.java index 6ce50c9..fd752d2 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectTest.java @@ -53,6 +53,8 @@ public abstract class InjectTest extends AbstractGremlinProcessTest { public abstract Traversal<Integer, Map<String, Object>> get_g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX(); + public abstract Traversal<Map<String,Object>, Map<String, Object>> get_g_injectXname_marko_age_nullX_selectXname_ageX(); + @Test @LoadGraphWith(MODERN) public void g_VX1X_out_injectXv2X_name() { @@ -107,6 +109,13 @@ public abstract class InjectTest extends AbstractGremlinProcessTest { } @Test + public void g_injectXname_marko_age_nullX_selectXname_ageX() { + final Traversal<Map<String, Object>, Map<String, Object>> traversal = get_g_injectXname_marko_age_nullX_selectXname_ageX(); + printTraversalForm(traversal); + checkResults(makeMapList(2, "name", "marko", "age", null), traversal); + } + + @Test @LoadGraphWith(MODERN) public void g_VX1X_injectXg_VX4XX_out_name() { final Traversal<Vertex, String> traversal = get_g_VX1X_injectXg_VX4XX_out_name(convertToVertexId("marko"), convertToVertexId("josh")); @@ -144,5 +153,13 @@ public abstract class InjectTest extends AbstractGremlinProcessTest { by(). by(__.select("x").select(__.select("y"))); } + + @Override + public Traversal<Map<String, Object>, Map<String, Object>> get_g_injectXname_marko_age_nullX_selectXname_ageX() { + final Map<String,Object> m = new HashMap<>(); + m.put("name", "marko"); + m.put("age", null); + return g.inject(m).select("name","age"); + } } }
