This is an automated email from the ASF dual-hosted git repository. dkuppitz pushed a commit to branch TINKERPOP-1084 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 6730d431bb635d6da8ad15747eda9e3200c5cd19 Author: Daniel Kuppitz <daniel_kupp...@hotmail.com> AuthorDate: Mon Jun 10 12:45:16 2019 -0700 TINKERPOP-1084 Allow predicates and traversals to be used as options in `BranchStep`. --- CHANGELOG.asciidoc | 1 + docs/src/upgrade/release-3.3.x.asciidoc | 30 +++++ .../traversal/lambda/PredicateTraversal.java | 68 ++++++++++ .../process/traversal/step/branch/BranchStep.java | 139 +++++++++++---------- .../process/traversal/step/branch/ChooseStep.java | 10 +- .../process/traversal/step/branch/UnionStep.java | 2 +- gremlin-test/features/branch/Branch.feature | 37 +++++- .../process/traversal/step/ComplexTest.java | 46 ++++++- .../process/traversal/step/branch/BranchTest.java | 47 +++++++ .../tinkergraph/structure/TinkerGraphPlayTest.java | 26 +++- 10 files changed, 323 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 88e8a44..0cd6c8a 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -35,6 +35,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima * Fixed bug in `MatchStep` where the correct was not properly determined. * Fixed bug where client/server exception mismatch when server throw StackOverflowError * Prevent exception when closing a session that doesn't exist +* Allow predicates and traversals to be used as options in `BranchStep`. [[release-3-3-7]] === TinkerPop 3.3.7 (Release Date: May 28, 2019) diff --git a/docs/src/upgrade/release-3.3.x.asciidoc b/docs/src/upgrade/release-3.3.x.asciidoc index 51da497..d0fdfb4 100644 --- a/docs/src/upgrade/release-3.3.x.asciidoc +++ b/docs/src/upgrade/release-3.3.x.asciidoc @@ -27,6 +27,36 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima Please see the link:https://github.com/apache/tinkerpop/blob/3.3.8/CHANGELOG.asciidoc#release-3-3-8[changelog] for a complete list of all the modifications that are part of this release. +== Upgrading for Users + +==== Branch Steps accept Predicates and Traversals + +Prior to this version, branch steps (in particular `BranchStep` and `ChooseStep`) could only handle constant values and `Pick` tokens. Starting in this version, these steps will also accept +predicates and traversals as show in the example below. + +[source,text] +---- +gremlin> g = TinkerFactory.createModern().traversal() +==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard] +gremlin> g.V().hasLabel("person"). +......1> group(). +......2> by("name"). +......3> by(branch(values("age")). +......4> option(29, constant("almost old")). +......5> option(__.is(32), constant("looks like josh")). +......6> option(lt(29), constant("pretty young")). +......7> option(lt(35), constant("younger than peter")). +......8> option(gte(30), constant("pretty old")). +......9> option(none, constant("mysterious")).fold()). +.....10> unfold() +==>peter=[pretty old] +==>vadas=[pretty young, younger than peter] +==>josh=[looks like josh, younger than peter, pretty old] +==>marko=[almost old, younger than peter] +---- + +See: link:https://issues.apache.org/jira/browse/TINKERPOP-1084[TINKERPOP-1084] + == TinkerPop 3.3.7 diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/PredicateTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/PredicateTraversal.java new file mode 100644 index 0000000..603ac50 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/PredicateTraversal.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.process.traversal.lambda; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException; + +import java.util.function.Predicate; + +/** + * @author Daniel Kuppitz (http://gremlin.guru) + */ +public final class PredicateTraversal<S> extends AbstractLambdaTraversal<S, S> { + + private final Predicate predicate; + private S s; + private boolean pass; + + public PredicateTraversal(final Object value) { + this.predicate = value instanceof Predicate ? (Predicate) value : P.eq(value); + } + + @Override + public S next() { + if (this.pass) + return this.s; + throw FastNoSuchElementException.instance(); + } + + @Override + public boolean hasNext() { + return this.pass; + } + + @Override + public void addStart(final Traverser.Admin<S> start) { + //noinspection unchecked + this.pass = this.predicate.test(this.s = start.get()); + } + + @Override + public String toString() { + return "(" + this.predicate.toString() + ")"; + } + + @Override + public int hashCode() { + return this.getClass().hashCode() ^ this.predicate.hashCode(); + } +} + diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java index e35d51e..d92ce2d 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java @@ -18,8 +18,10 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.branch; +import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.lambda.PredicateTraversal; import org.apache.tinkerpop.gremlin.process.traversal.step.Barrier; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IdentityStep; @@ -30,7 +32,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementExce import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -import org.apache.tinkerpop.gremlin.util.NumberHelper; +import org.javatuples.Pair; import java.util.ArrayList; import java.util.Collections; @@ -39,17 +41,22 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Marko A. Rodriguez (http://markorodriguez.com) + * @author Daniel Kuppitz (http://gremlin.guru) */ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements TraversalOptionParent<M, S, E> { protected Traversal.Admin<S, M> branchTraversal; - protected Map<Object, List<Traversal.Admin<S, E>>> traversalOptions = new HashMap<>(); + protected Map<Pick, List<Traversal.Admin<S, E>>> traversalPickOptions = new HashMap<>(); + protected List<Pair<Traversal.Admin, Traversal.Admin<S, E>>> traversalOptions = new ArrayList<>(); + private boolean first = true; - private boolean hasBarrier = false; + private boolean hasBarrier; public BranchStep(final Traversal.Admin traversal) { super(traversal); @@ -61,12 +68,20 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav @Override public void addGlobalChildOption(final M pickToken, final Traversal.Admin<S, E> traversalOption) { - final Object pickTokenKey = PickTokenKey.make(pickToken); - if (this.traversalOptions.containsKey(pickTokenKey)) - this.traversalOptions.get(pickTokenKey).add(traversalOption); - else - this.traversalOptions.put(pickTokenKey, new ArrayList<>(Collections.singletonList(traversalOption))); - + if (pickToken instanceof Pick) { + if (this.traversalPickOptions.containsKey(pickToken)) + this.traversalPickOptions.get(pickToken).add(traversalOption); + else + this.traversalPickOptions.put((Pick) pickToken, new ArrayList<>(Collections.singletonList(traversalOption))); + } else { + final Traversal.Admin pickOptionTraversal; + if (pickToken instanceof Traversal) { + pickOptionTraversal = ((Traversal) pickToken).asAdmin(); + } else { + pickOptionTraversal = new PredicateTraversal(pickToken); + } + this.traversalOptions.add(Pair.with(pickOptionTraversal, traversalOption)); + } // adding an IdentityStep acts as a placeholder when reducing barriers get in the way - see the // standardAlgorithm() method for more information. if (TraversalHelper.hasStepOfAssignableClass(ReducingBarrierStep.class, traversalOption)) @@ -85,9 +100,9 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav @Override public List<Traversal.Admin<S, E>> getGlobalChildren() { - return Collections.unmodifiableList(this.traversalOptions.values().stream() - .flatMap(List::stream) - .collect(Collectors.toList())); + return Collections.unmodifiableList(Stream.concat( + this.traversalPickOptions.values().stream().flatMap(List::stream), + this.traversalOptions.stream().map(Pair::getValue1)).collect(Collectors.toList())); } @Override @@ -107,11 +122,9 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav // traverser as part of the condition for using that traversal option in the output. This is necessary // because barriers like fold(), max(), etc. will always return true for hasNext() even if a traverser // was not seeded in applyCurrentTraverser(). - for (final List<Traversal.Admin<S, E>> options : this.traversalOptions.values()) { - for (final Traversal.Admin<S, E> option : options) { - if (option.getStartStep().hasNext() && option.hasNext()) - return option.getEndStep(); - } + for (final Traversal.Admin<S, E> option : getGlobalChildren()) { + if (option.getStartStep().hasNext() && option.hasNext()) + return option.getEndStep(); } } @@ -138,17 +151,16 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav private void applyCurrentTraverser(final Traverser.Admin<S> start) { // first get the value of the choice based on the current traverser and use that to select the right traversal // option to which that traverser should be routed - final Object choice = PickTokenKey.make(TraversalUtil.apply(start, this.branchTraversal)); - final List<Traversal.Admin<S, E>> branch = this.traversalOptions.containsKey(choice) ? - this.traversalOptions.get(choice) : this.traversalOptions.get(Pick.none); + final Object choice = TraversalUtil.apply(start, this.branchTraversal); + final List<Traversal.Admin<S, E>> branches = pickBranches(choice); // if a branch is identified, then split the traverser and add it to the start of the option so that when // that option is iterated (in the calling method) that value can be applied. - if (null != branch) - branch.forEach(traversal -> traversal.addStart(start.split())); + if (null != branches) + branches.forEach(traversal -> traversal.addStart(start.split())); if (choice != Pick.any) { - final List<Traversal.Admin<S, E>> anyBranch = this.traversalOptions.get(Pick.any); + final List<Traversal.Admin<S, E>> anyBranch = this.traversalPickOptions.get(Pick.any); if (null != anyBranch) anyBranch.forEach(traversal -> traversal.addStart(start.split())); } @@ -158,10 +170,10 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav protected Iterator<Traverser.Admin<E>> computerAlgorithm() { final List<Traverser.Admin<E>> ends = new ArrayList<>(); final Traverser.Admin<S> start = this.starts.next(); - final Object choice = PickTokenKey.make(TraversalUtil.apply(start, this.branchTraversal)); - final List<Traversal.Admin<S, E>> branch = this.traversalOptions.containsKey(choice) ? this.traversalOptions.get(choice) : this.traversalOptions.get(Pick.none); - if (null != branch) { - branch.forEach(traversal -> { + final Object choice = TraversalUtil.apply(start, this.branchTraversal); + final List<Traversal.Admin<S, E>> branches = pickBranches(choice); + if (null != branches) { + branches.forEach(traversal -> { final Traverser.Admin<E> split = (Traverser.Admin<E>) start.split(); split.setStepId(traversal.getStartStep().getId()); //split.addLabels(this.labels); @@ -169,7 +181,7 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav }); } if (choice != Pick.any) { - final List<Traversal.Admin<S, E>> anyBranch = this.traversalOptions.get(Pick.any); + final List<Traversal.Admin<S, E>> anyBranch = this.traversalPickOptions.get(Pick.any); if (null != anyBranch) { anyBranch.forEach(traversal -> { final Traverser.Admin<E> split = (Traverser.Admin<E>) start.split(); @@ -182,20 +194,39 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav return ends.iterator(); } + private List<Traversal.Admin<S, E>> pickBranches(final Object choice) { + final List<Traversal.Admin<S, E>> branches = new ArrayList<>(); + if (choice instanceof Pick) { + if (this.traversalPickOptions.containsKey(choice)) { + branches.addAll(this.traversalPickOptions.get(choice)); + } + } + for (final Pair<Traversal.Admin, Traversal.Admin<S, E>> p : this.traversalOptions) { + if (TraversalUtil.test(choice, p.getValue0())) { + branches.add(p.getValue1()); + } + } + return branches.isEmpty() ? this.traversalPickOptions.get(Pick.none) : branches; + } + @Override public BranchStep<S, E, M> clone() { final BranchStep<S, E, M> clone = (BranchStep<S, E, M>) super.clone(); - clone.traversalOptions = new HashMap<>(this.traversalOptions.size()); - for (final Map.Entry<Object, List<Traversal.Admin<S, E>>> entry : this.traversalOptions.entrySet()) { + clone.traversalPickOptions = new HashMap<>(this.traversalPickOptions.size()); + clone.traversalOptions = new ArrayList<>(this.traversalOptions.size()); + for (final Map.Entry<Pick, List<Traversal.Admin<S, E>>> entry : this.traversalPickOptions.entrySet()) { final List<Traversal.Admin<S, E>> traversals = entry.getValue(); if (traversals.size() > 0) { - final List<Traversal.Admin<S, E>> clonedTraversals = clone.traversalOptions.compute(entry.getKey(), (k, v) -> + final List<Traversal.Admin<S, E>> clonedTraversals = clone.traversalPickOptions.compute(entry.getKey(), (k, v) -> (v == null) ? new ArrayList<>(traversals.size()) : v); for (final Traversal.Admin<S, E> traversal : traversals) { clonedTraversals.add(traversal.clone()); } } } + for (final Pair<Traversal.Admin, Traversal.Admin<S, E>> pair : this.traversalOptions) { + clone.traversalOptions.add(Pair.with(pair.getValue0().clone(), pair.getValue1().clone())); + } clone.branchTraversal = this.branchTraversal.clone(); return clone; } @@ -204,12 +235,14 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) { super.setTraversal(parentTraversal); this.integrateChild(this.branchTraversal); - this.traversalOptions.values().stream().flatMap(List::stream).forEach(this::integrateChild); + this.getGlobalChildren().forEach(this::integrateChild); } @Override public int hashCode() { int result = super.hashCode(); + if (this.traversalPickOptions != null) + result ^= this.traversalPickOptions.hashCode(); if (this.traversalOptions != null) result ^= this.traversalOptions.hashCode(); if (this.branchTraversal != null) @@ -219,7 +252,10 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav @Override public String toString() { - return StringFactory.stepString(this, this.branchTraversal, this.traversalOptions); + final List<Pair> combinedOptions = Stream.concat( + this.traversalPickOptions.entrySet().stream().map(e -> Pair.with(e.getKey(), e.getValue())), + this.traversalOptions.stream()).collect(Collectors.toList()); + return StringFactory.stepString(this, this.branchTraversal, combinedOptions); } @Override @@ -228,39 +264,4 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav this.getGlobalChildren().forEach(Traversal.Admin::reset); this.first = true; } - - /** - * PickTokenKey is basically a wrapper for numbers that are used as a PickToken. This is - * required in order to treat equal numbers of different data types as a match. - */ - private static class PickTokenKey { - - final Number number; - - private PickTokenKey(final Number number) { - this.number = number; - } - - static Object make(final Object value) { - return value instanceof Number ? new PickTokenKey((Number) value) : value; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final PickTokenKey other = (PickTokenKey) o; - return 0 == NumberHelper.compare(number, other.number); - } - - @Override - public int hashCode() { - return number.hashCode(); - } - - @Override - public String toString() { - return number.toString(); - } - } -} +} \ No newline at end of file diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java index e699f22..a804d8f 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java @@ -42,10 +42,12 @@ public final class ChooseStep<S, E, M> extends BranchStep<S, E, M> { @Override public void addGlobalChildOption(final M pickToken, final Traversal.Admin<S, E> traversalOption) { - if (Pick.any.equals(pickToken)) - throw new IllegalArgumentException("Choose step can not have an any-option as only one option per traverser is allowed"); - if (this.traversalOptions.containsKey(pickToken)) - throw new IllegalArgumentException("Choose step can only have one traversal per pick token: " + pickToken); + if (pickToken instanceof Pick) { + if (Pick.any.equals(pickToken)) + throw new IllegalArgumentException("Choose step can not have an any-option as only one option per traverser is allowed"); + if (this.traversalPickOptions.containsKey(pickToken)) + throw new IllegalArgumentException("Choose step can only have one traversal per pick token: " + pickToken); + } super.addGlobalChildOption(pickToken, traversalOption); } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/UnionStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/UnionStep.java index 85f3645..de70479 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/UnionStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/UnionStep.java @@ -47,6 +47,6 @@ public final class UnionStep<S, E> extends BranchStep<S, E, TraversalOptionParen @Override public String toString() { - return StringFactory.stepString(this, this.traversalOptions.getOrDefault(Pick.any, Collections.emptyList())); + return StringFactory.stepString(this, this.traversalPickOptions.getOrDefault(Pick.any, Collections.emptyList())); } } diff --git a/gremlin-test/features/branch/Branch.feature b/gremlin-test/features/branch/Branch.feature index 6ee261f..e160fc8 100644 --- a/gremlin-test/features/branch/Branch.feature +++ b/gremlin-test/features/branch/Branch.feature @@ -89,4 +89,39 @@ Feature: Step - branch() | person | | person | | software | - | software | \ No newline at end of file + | software | + + Scenario: g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX + Given the modern graph + And the traversal of + """ + g.V().hasLabel("person"). + branch(__.values("age")). + option(P.lt(30), __.constant("young")). + option(P.gt(30), __.constant("old")). + option(Pick.none, __.constant("on the edge")) + """ + When iterated to list + Then the result should be unordered + | result | + | young | + | young | + | old | + | old | + + Scenario: g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX + Given the modern graph + And the traversal of + """ + g.V(). + branch(__.identity()). + option(__.hasLabel("software"), __.in("created").values("name").order().fold()). + option(__.has("name","vadas"), __.values("age")). + option(P.neq(123), __.bothE().count()) + """ + When iterated to list + Then the result should be unordered + | result | + | l[josh,josh,marko,peter] | + | d[27].i | + | d[12].l | diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/ComplexTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/ComplexTest.java index 75dd0f5..0110d44 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/ComplexTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/ComplexTest.java @@ -44,6 +44,7 @@ import java.util.Map; import static java.util.Collections.emptyList; import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN; import static org.apache.tinkerpop.gremlin.process.traversal.P.eq; +import static org.apache.tinkerpop.gremlin.process.traversal.P.gte; import static org.apache.tinkerpop.gremlin.process.traversal.P.lt; import static org.apache.tinkerpop.gremlin.process.traversal.P.neq; import static org.apache.tinkerpop.gremlin.process.traversal.Pop.all; @@ -51,6 +52,7 @@ import static org.apache.tinkerpop.gremlin.process.traversal.Pop.first; import static org.apache.tinkerpop.gremlin.process.traversal.Pop.last; import static org.apache.tinkerpop.gremlin.process.traversal.Scope.local; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.both; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.branch; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.constant; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.count; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.group; @@ -86,6 +88,8 @@ public abstract class ComplexTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, Map<String, List<String>>> getPlaylistPaths(); + public abstract Traversal<Vertex, Map<String, List<String>>> getAgeComments(); + /** * Checks the result of both coworkerSummary tests, which is expected to look as follows: * <p> @@ -243,6 +247,32 @@ public abstract class ComplexTest extends AbstractGremlinProcessTest { assertTrue(map.get("artists").contains("Grateful_Dead")); } + @Test + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + public void ageComments() { + final Traversal<Vertex, Map<String, List<String>>> traversal = getAgeComments(); + printTraversalForm(traversal); + assertTrue(traversal.hasNext()); + Map<String, List<String>> map = traversal.next(); + assertEquals(4, map.size()); + assertTrue(map.containsKey("marko")); + assertEquals(2, map.get("marko").size()); + assertTrue(map.get("marko").contains("almost old")); + assertTrue(map.get("marko").contains("younger than peter")); + assertTrue(map.containsKey("vadas")); + assertEquals(2, map.get("vadas").size()); + assertTrue(map.get("vadas").contains("pretty young")); + assertTrue(map.get("vadas").contains("younger than peter")); + assertTrue(map.containsKey("josh")); + assertEquals(3, map.get("josh").size()); + assertTrue(map.get("josh").contains("younger than peter")); + assertTrue(map.get("josh").contains("pretty old")); + assertTrue(map.get("josh").contains("looks like josh")); + assertTrue(map.containsKey("peter")); + assertEquals(1, map.get("peter").size()); + assertTrue(map.get("peter").contains("pretty old")); + } + public static class Traversals extends ComplexTest { @Override @@ -323,6 +353,18 @@ public abstract class ComplexTest extends AbstractGremlinProcessTest { by("name"). by(__.coalesce(out("sungBy", "writtenBy").dedup().values("name"), constant("Unknown")).fold()); } - } -} + @Override + public Traversal<Vertex, Map<String, List<String>>> getAgeComments() { + return g.V().hasLabel("person") + .<String, List<String>> group() + .by("name") + .by(branch(values("age")) + .option(29, constant("almost old")) + .option(__.is(32), constant("looks like josh")) + .option(lt(29), constant("pretty young")) + .option(lt(35), constant("younger than peter")) + .option(gte(30), constant("pretty old")).fold()); + } + } +} \ No newline at end of file diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchTest.java index 44a63bd..ed4b2e7 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchTest.java @@ -29,9 +29,19 @@ import org.junit.runner.RunWith; import java.util.Arrays; import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN; +import static org.apache.tinkerpop.gremlin.process.traversal.P.gt; +import static org.apache.tinkerpop.gremlin.process.traversal.P.lt; +import static org.apache.tinkerpop.gremlin.process.traversal.P.neq; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.bothE; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.constant; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.has; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.hasLabel; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.identity; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.label; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.values; import static org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick.any; +import static org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick.none; /** * @author Marko A. Rodriguez (http://markorodriguez.com) @@ -45,6 +55,10 @@ public abstract class BranchTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, Object> get_g_V_branchXlabel_isXpersonX_countX_optionX1__ageX_optionX0__langX_optionX0__nameX_optionXany__labelX(); + public abstract Traversal<Vertex, Object> get_g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX(); + + public abstract Traversal<Vertex, Object> get_g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX(); + @Test @LoadGraphWith(MODERN) public void g_V_branchXlabel_eq_person__a_bX_optionXa__ageX_optionXb__langX_optionXb__nameX() { @@ -69,6 +83,22 @@ public abstract class BranchTest extends AbstractGremlinProcessTest { checkResults(Arrays.asList("java", "java", "lop", "ripple", 29, 27, 32, 35, "person", "person", "person", "person", "software", "software"), traversal); } + @Test + @LoadGraphWith(MODERN) + public void g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX() { + final Traversal<Vertex, Object> traversal = get_g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX(); + printTraversalForm(traversal); + checkResults(Arrays.asList("young", "young", "old", "old"), traversal); + } + + @Test + @LoadGraphWith(MODERN) + public void g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX() { + final Traversal<Vertex, Object> traversal = get_g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX(); + printTraversalForm(traversal); + checkResults(Arrays.asList(Arrays.asList("josh", "josh", "marko", "peter"), 27, 12L), traversal); + } + public static class Traversals extends BranchTest { @Override @@ -95,5 +125,22 @@ public abstract class BranchTest extends AbstractGremlinProcessTest { .option(0L, values("name")) .option(any, label()); } + + @Override + public Traversal<Vertex, Object> get_g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX() { + return g.V().hasLabel("person") + .branch(values("age")) + .option(lt(30), constant("young")) + .option(gt(30), constant("old")) + .option(none, constant("on the edge")); + } + + @Override + public Traversal<Vertex, Object> get_g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX() { + return g.V().branch(identity()) + .option(hasLabel("software"), in("created").values("name").order().fold()) + .option(has("name","vadas"), values("age")) + .option(neq(123), bothE().count()); + } } } \ No newline at end of file diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java index 746689a..31c332b 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java @@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.T; @@ -41,9 +42,14 @@ import java.util.function.BiFunction; import java.util.function.Supplier; import static org.apache.tinkerpop.gremlin.process.traversal.Operator.sum; +import static org.apache.tinkerpop.gremlin.process.traversal.P.between; +import static org.apache.tinkerpop.gremlin.process.traversal.P.gt; +import static org.apache.tinkerpop.gremlin.process.traversal.P.gte; +import static org.apache.tinkerpop.gremlin.process.traversal.P.lt; import static org.apache.tinkerpop.gremlin.process.traversal.P.neq; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.as; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.both; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.branch; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.choose; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.constant; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.has; @@ -53,6 +59,7 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.sack; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.select; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.union; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.valueMap; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.values; /** * @author Stephen Mallette (http://stephen.genoprime.com) @@ -130,12 +137,19 @@ public class TinkerGraphPlayTest { @Ignore public void testPlayDK() throws Exception { - final GraphTraversalSource g = TinkerFactory.createModern().traversal(); - g.V().match( - __.as("b").out("created").as("c"), - __.as("a").hasLabel("person"), - __.as("b").hasLabel("person"), - __.as("a").out("knows").as("b")).forEachRemaining(System.out::println); + GraphTraversalSource g = TinkerFactory.createModern().traversal(); + System.out.println(g./*withComputer().*/V().hasLabel("person") + .project("name", "age", "comments") + .by("name") + .by("age") + .by(branch(values("age")) + .option(TraversalOptionParent.Pick.any, constant("foo")) + .option(29, constant("almost old")) + .option(__.is(32), constant("looks like josh")) + .option(lt(29), constant("pretty young")) + .option(lt(35), constant("younger than peter")) + .option(gte(30), constant("pretty old")).fold()).explain()); + //.forEachRemaining(System.out::println); } @Test