Prune path work.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/e4e693e4 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/e4e693e4 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/e4e693e4 Branch: refs/heads/TINKERPOP-1254-final Commit: e4e693e4655358cb94be6909751d2094bb1bc521 Parents: a138d55 Author: Ted Wilmes <twil...@gmail.com> Authored: Thu Jul 7 12:59:23 2016 -0500 Committer: Ted Wilmes <twil...@gmail.com> Committed: Thu Jul 7 12:59:23 2016 -0500 ---------------------------------------------------------------------- .../gremlin/process/traversal/Path.java | 8 + .../process/traversal/TraversalStrategies.java | 4 +- .../gremlin/process/traversal/Traverser.java | 6 + .../process/traversal/step/PathProcessor.java | 17 ++ .../traversal/step/filter/DedupGlobalStep.java | 16 ++ .../step/filter/WherePredicateStep.java | 32 ++- .../step/filter/WhereTraversalStep.java | 15 + .../process/traversal/step/map/MatchStep.java | 159 ++++++++++- .../process/traversal/step/map/PathStep.java | 16 ++ .../traversal/step/map/SelectOneStep.java | 18 ++ .../process/traversal/step/map/SelectStep.java | 16 ++ .../process/traversal/step/map/TreeStep.java | 9 + .../step/sideEffect/TreeSideEffectStep.java | 9 + .../process/traversal/step/util/EmptyPath.java | 3 + .../traversal/step/util/ImmutablePath.java | 204 +++++++++++++ .../traversal/step/util/ImmutablePathUtil.java | 37 +++ .../traversal/step/util/MutablePath.java | 37 ++- .../optimization/PrunePathStrategy.java | 285 +++++++++++++++++++ .../traverser/B_LP_O_S_SE_SL_Traverser.java | 42 +++ .../traverser/LP_O_OB_P_S_SE_SL_Traverser.java | 26 ++ .../traverser/util/AbstractTraverser.java | 16 ++ .../traverser/util/EmptyTraverser.java | 15 + .../process/traversal/util/PathUtil.java | 80 ++++++ .../gremlin/process/traversal/PathTest.java | 12 + .../traversal/step/map/MatchStepTest.java | 46 +++ .../optimization/PrunePathStrategyTest.java | 120 ++++++++ .../groovy/loaders/SugarLoaderTest.groovy | 20 +- .../structure/TinkerGraphPlayTest.java | 126 +++++++- 28 files changed, 1378 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java index a302165..c4bc347 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java @@ -66,6 +66,14 @@ public interface Path extends Cloneable, Iterable<Object> { public Path extend(final Set<String> labels); /** + * Remove labels from path. + * + * @param labels the labels to remove + * @return the path with removed labels + */ + public Path retract(final Set<String> labels); + + /** * Get the object associated with the particular label of the path. * If the path as multiple labels of the type, then return a {@link List} of those objects. * http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java index b093676..850db9f 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java @@ -29,6 +29,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Inci import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.MatchPredicateStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.OrderLimitStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PrunePathStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RangeByIsCountStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy; @@ -197,7 +198,8 @@ public interface TraversalStrategies extends Serializable, Cloneable { RepeatUnrollStrategy.instance(), RangeByIsCountStrategy.instance(), ProfileStrategy.instance(), - StandardVerificationStrategy.instance()); + StandardVerificationStrategy.instance(), + PrunePathStrategy.instance()); GRAPH_CACHE.put(Graph.class, graphStrategies); GRAPH_CACHE.put(EmptyGraph.class, new DefaultTraversalStrategies()); http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java index 0a36b2c..0c37a34 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java @@ -183,6 +183,12 @@ public interface Traverser<T> extends Serializable, Comparable<Traverser<T>>, Cl public void addLabels(final Set<String> labels); + public void keepLabels(final Set<String> labels); + + public void dropLabels(final Set<String> labels); + + public void dropPath(); + /** * Set the current object location of the traverser. * http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PathProcessor.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PathProcessor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PathProcessor.java index 48a9cc3..9282571 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PathProcessor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PathProcessor.java @@ -19,11 +19,14 @@ package org.apache.tinkerpop.gremlin.process.traversal.step; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.lambda.ElementValueTraversal; import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal; import org.apache.tinkerpop.gremlin.process.traversal.lambda.TokenTraversal; import org.apache.tinkerpop.gremlin.structure.T; +import java.util.Set; + /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ @@ -56,4 +59,18 @@ public interface PathProcessor { } return max; } + + void setKeepLabels(final Set<String> labels); + + static void keepLabels(final Traverser traverser, final Set<String> labels) { + if(labels == null) { + return; + } else { +// final Set<String> keepers = traverser.asAdmin().getTags(); +// keepers.addAll(labels); + traverser.asAdmin().keepLabels(labels); + } + } + + Set<String> getKeepLabels(); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java ---------------------------------------------------------------------- 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 9336a1a..ab5e687 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 @@ -56,6 +56,7 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal private Set<Object> duplicateSet = new HashSet<>(); private boolean onGraphComputer = false; private final Set<String> dedupLabels; + private Set<String> keepLabels; public DedupGlobalStep(final Traversal.Admin traversal, final String... dedupLabels) { super(traversal); @@ -81,6 +82,18 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal } @Override + public void setKeepLabels(Set<String> labels) { + this.keepLabels = labels; + } + + @Override + protected Traverser.Admin<S> processNextStart() { + final Traverser.Admin<S> traverser = super.processNextStart(); + PathProcessor.keepLabels(traverser, keepLabels); + return traverser; + } + + @Override public List<Traversal<S, Object>> getLocalChildren() { return null == this.dedupTraversal ? Collections.emptyList() : Collections.singletonList(this.dedupTraversal); } @@ -193,4 +206,7 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal public MemoryComputeKey<Map<Object, Traverser.Admin<S>>> getMemoryComputeKey() { return MemoryComputeKey.of(this.getId(), (BinaryOperator) Operator.addAll, false, true); } + + @Override + public Set<String> getKeepLabels() { return this.keepLabels; } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java ---------------------------------------------------------------------- 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 6fea7d6..4162716 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 @@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.P; 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.PathProcessor; import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP; @@ -33,12 +34,13 @@ import java.util.*; /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public final class WherePredicateStep<S> extends FilterStep<S> implements Scoping { +public final class WherePredicateStep<S> extends FilterStep<S> implements Scoping, PathProcessor { protected String startKey; protected List<String> selectKeys; protected P<Object> predicate; protected final Set<String> scopeKeys = new HashSet<>(); + protected Set<String> keepLabels; public WherePredicateStep(final Traversal.Admin traversal, final Optional<String> startKey, final P<String> predicate) { super(traversal); @@ -115,4 +117,32 @@ public final class WherePredicateStep<S> extends FilterStep<S> implements Scopin TYPICAL_GLOBAL_REQUIREMENTS : TYPICAL_LOCAL_REQUIREMENTS; } + + @Override + protected Traverser.Admin<S> processNextStart() { + final Traverser.Admin<S> traverser = super.processNextStart(); + // add traverser tags in +// Set<String> labels = new HashSet<>(); +// labels.addAll(traverser.getTags()); +// if(keepLabels != null ) labels.addAll(keepLabels); +// if(keepLabels != null) System.out.println(labels); +// System.out.println(traverser); +// System.out.println("Before: " + traverser.path().labels()); +// System.out.println("Keeping: " + keepLabels); +// System.out.println("IN: " + traverser.path().labels()); + PathProcessor.keepLabels(traverser, keepLabels); +// System.out.println("OUT: " + traverser.path().labels()); +// System.out.println("After: " + traverser.path().labels()); + return traverser; + } + + @Override + public void setKeepLabels(Set<String> labels) { + this.keepLabels = labels; + } + + @Override + public Set<String> getKeepLabels() { + return this.keepLabels; + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java ---------------------------------------------------------------------- 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 388104d..5ba7004 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 @@ -46,6 +46,10 @@ public final class WhereTraversalStep<S> extends FilterStep<S> implements Traver protected Traversal.Admin<?, ?> whereTraversal; protected final Set<String> scopeKeys = new HashSet<>(); + protected Set<String> keepLabels; + + @Override + public Set<String> getKeepLabels() { return this.keepLabels; } public WhereTraversalStep(final Traversal.Admin traversal, final Traversal<?, ?> whereTraversal) { super(traversal); @@ -88,6 +92,17 @@ public final class WhereTraversalStep<S> extends FilterStep<S> implements Traver ElementRequirement.ID; } + @Override + public void setKeepLabels(Set<String> keepLabels) { + this.keepLabels = keepLabels; + } + + @Override + protected Traverser.Admin<S> processNextStart() { + final Traverser.Admin<S> traverser = super.processNextStart(); + PathProcessor.keepLabels(traverser, keepLabels); + return traverser; + } @Override protected boolean filter(final Traverser.Admin<S> traverser) { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java index c6e3bbe..5f262bd 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java @@ -19,6 +19,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map; import org.apache.tinkerpop.gremlin.process.traversal.*; +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.step.filter.*; @@ -44,7 +45,7 @@ import java.util.stream.Stream; /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> implements TraversalParent, Scoping { +public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> implements TraversalParent, Scoping, PathProcessor { public enum TraversalType {WHERE_PREDICATE, WHERE_TRAVERSAL, MATCH_TRAVERSAL} @@ -60,6 +61,7 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> private Set<List<Object>> dedups = null; private Set<String> dedupLabels = null; + private Set<String> keepLabels = null; public MatchStep(final Traversal.Admin traversal, final ConnectiveStep.Connective connective, final Traversal... matchTraversals) { super(traversal); @@ -135,6 +137,8 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> } } + + public ConnectiveStep.Connective getConnective() { return this.connective; } @@ -318,11 +322,14 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> if (this.connective == ConnectiveStep.Connective.AND) { final Traversal.Admin<Object, Object> matchTraversal = this.getMatchAlgorithm().apply(traverser); + // drop any labels that we can +// dropUnnecessaryLabels(traverser, matchTraversal); traverser.getTags().add(matchTraversal.getStartStep().getId()); matchTraversal.addStart(traverser); // determine which sub-pattern the traverser should try next } else { // OR for (final Traversal.Admin<?, ?> matchTraversal : this.matchTraversals) { final Traverser.Admin split = traverser.split(); +// dropUnnecessaryLabels(traverser, matchTraversal); split.getTags().add(matchTraversal.getStartStep().getId()); matchTraversal.addStart(split); } @@ -331,6 +338,28 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> } } + private void dropUnnecessaryLabels(final Traverser.Admin<Object> traverser, + final Traversal.Admin matchTraversal) { + if(keepLabels == null) { + return; + } + final List<Traversal.Admin> remainingTraversals = getRemainingTraversals(traverser); + remainingTraversals.add(matchTraversal); + HashSet<String> requiredLabels = new HashSet<>(); + remainingTraversals.stream().forEach(trav -> { + getRequiredLabels(trav, requiredLabels); + }); + requiredLabels.addAll(keepLabels); + if(dedupLabels != null) { + requiredLabels.addAll(dedupLabels); + } + requiredLabels.add(matchTraversal.getStartStep().getId()); +// System.out.println(requiredLabels); +// System.out.println("Before:\t" + traverser.path().labels() + "\t" + traverser.path().objects()); + traverser.keepLabels(requiredLabels); +// System.out.println("After:\t" + traverser.path().labels() + "\t" + traverser.path().objects()); + } + @Override protected Iterator<Traverser.Admin<Map<String, E>>> computerAlgorithm() throws NoSuchElementException { while (true) { @@ -353,11 +382,13 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> final Traversal.Admin<Object, Object> matchTraversal = this.getMatchAlgorithm().apply(traverser); // determine which sub-pattern the traverser should try next traverser.getTags().add(matchTraversal.getStartStep().getId()); traverser.setStepId(matchTraversal.getStartStep().getId()); // go down the traversal match sub-pattern +// dropUnnecessaryLabels(traverser, matchTraversal); return IteratorUtils.of(traverser); } else { // OR final List<Traverser.Admin<Map<String, E>>> traversers = new ArrayList<>(this.matchTraversals.size()); for (final Traversal.Admin<?, ?> matchTraversal : this.matchTraversals) { final Traverser.Admin split = traverser.split(); +// dropUnnecessaryLabels(traverser, matchTraversal); split.getTags().add(matchTraversal.getStartStep().getId()); split.setStepId(matchTraversal.getStartStep().getId()); traversers.add(split); @@ -368,6 +399,57 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> } } + protected List<Traversal.Admin> getRemainingTraversals(final Traverser.Admin traverser) { + + final Set<String> tags = traverser.getTags(); + final List<Traversal.Admin> remainingTraversals = new ArrayList<>(); + for(final Traversal.Admin matchTraversal : matchTraversals) { + if(!tags.contains(matchTraversal.getStartStep().getId())) { + remainingTraversals.add(matchTraversal); + } else { + // see if we're currently in it + for(Object s : matchTraversal.getSteps()) { + if(((Step)s).getId().equals(traverser.getStepId())) { + remainingTraversals.add(matchTraversal); + break; + } + } +// matchTraversal.getSteps().stream().forEach(step -> { +// +// if(((Step)step).getId().equals(traverser.getStepId())) { +// found = true; +// } +// if(found) { +// remainingTraversals.add(matchTraversal); +// } +// }); + } + } +// System.out.println("Remaining: " + remainingTraversals); + return remainingTraversals; + } + + private void getRequiredLabels(Traversal.Admin trav, Set<String> requiredLabels) { + trav.getSteps().stream().forEach(step -> { +// if(step instanceof Scoping) { +// requiredLabels.addAll(((Scoping) step).getScopeKeys()); +// } + if(step instanceof PathProcessor) { + //Set<String> keep = ((PathProcessor) step).getKeepLabels(); + //if(keep != null) requiredLabels.addAll(keep); + } else if(step instanceof Scoping) { + Set<String> keep = ((Scoping)step).getScopeKeys(); + if(keep != null) requiredLabels.addAll(keep); + } + if(step instanceof TraversalParent) { + TraversalParent parent = (TraversalParent) step; + List<Traversal.Admin<Object, Object>> children = new ArrayList<>(parent.getGlobalChildren()); + children.addAll(parent.getLocalChildren()); + children.stream().forEach(child -> getRequiredLabels(child, requiredLabels)); + } + }); + } + @Override public int hashCode() { int result = super.hashCode() ^ this.connective.hashCode(); @@ -382,6 +464,44 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> return this.getSelfAndChildRequirements(TraverserRequirement.LABELED_PATH, TraverserRequirement.SIDE_EFFECTS); } + @Override + protected Traverser.Admin<Map<String, E>> processNextStart() throws NoSuchElementException { + final Traverser.Admin<Map<String, E>> traverser = super.processNextStart(); + // remove labels that we don't need +// System.out.println("KEEPERS: " + keepLabels); + if(keepLabels != null) { + // add traverser tags in + Set<String> labels = new HashSet<>(); + // determine which tags to keep vs. drop by looking at which labels still exist in the path + // + +// labels.addAll(getMatchStartLabels()); +// labels.addAll(getMatchEndLabels()); +// if (keepLabels != null) System.out.println(labels); + List<Traversal.Admin> remainingTraversals = getRemainingTraversals(traverser); +// System.out.println("REMAINING: " + remainingTraversals.size()); +// if(remainingTraversals.size() == 1) { +// // also remove tags... +// System.out.println("Before droppppping: " + traverser.path().labels()); +// traverser.dropLabels(traverser.getTags()); +// System.out.println("DROPPPPPING TAGS: " + traverser.path().labels()); +// } +// System.out.println("none"); + for (Traversal.Admin trav : remainingTraversals) { + getRequiredLabels(trav, labels); + } +// labels.addAll(traverser.getTags()); + if (keepLabels != null) labels.addAll(keepLabels); +// System.out.println(labels); +// System.out.println("Before: " + traverser.path().labels()); +// System.out.println("Keepers: " + labels); + PathProcessor.keepLabels(traverser, labels); +// System.out.println("After: " + traverser.path().labels()); +// } + } + return traverser; + } + ////////////////////////////// public static class MatchStartStep extends AbstractStep<Object, Object> implements Scoping { @@ -517,7 +637,14 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> } public static boolean hasExecutedTraversal(final Traverser.Admin<Object> traverser, final Traversal.Admin<Object, Object> traversal) { - return traverser.getTags().contains(traversal.getStartStep().getId()); + final boolean hasExecuted = traverser.getTags().contains(traversal.getStartStep().getId()); + if(hasExecuted) { + String traversalId = traversal.getStartStep().getId(); +// System.out.println("Tag drop: " + traverser.path().labels()); + traverser.dropLabels(Collections.singleton(traversalId)); +// System.out.println(traverser.path().labels()); + } + return hasExecuted; } public static TraversalType getTraversalType(final Traversal.Admin<Object, Object> traversal) { @@ -678,4 +805,32 @@ public final class MatchStep<S, E> extends ComputerAwareStep<S, Map<String, E>> } } } + + @Override + public void setKeepLabels(Set<String> labels) { +// System.out.println("MATCH KEEP: " + labels); + if(this.keepLabels != null) { + this.keepLabels.addAll(labels); + } else { + this.keepLabels = new HashSet<>(labels); + } + } + + @Override + public Set<String> getKeepLabels() { + // add in start and end labels for this match b/c it's children may need to keep them + HashSet<String> keepThese = new HashSet<>(); + if(keepLabels != null) { + keepThese.addAll(this.keepLabels); + } + keepThese.addAll(this.getMatchStartLabels()); + keepThese.addAll(this.getMatchEndLabels()); + return keepThese; + } + + public Set<String> getMatchEndLabels() { + return this.matchEndLabels; + } + + public Set<String> getMatchStartLabels() { return this.matchStartLabels; } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java index 039c2a9..885afc1 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java @@ -39,6 +39,7 @@ import java.util.Set; public final class PathStep<S> extends MapStep<S, Path> implements TraversalParent, PathProcessor, ByModulating { private TraversalRing<Object, Object> traversalRing; + private Set<String> keepLabels; public PathStep(final Traversal.Admin traversal) { super(traversal); @@ -101,4 +102,19 @@ public final class PathStep<S> extends MapStep<S, Path> implements TraversalPare public Set<TraverserRequirement> getRequirements() { return this.getSelfAndChildRequirements(TraverserRequirement.PATH); } + + @Override + public void setKeepLabels(Set<String> labels) { + this.keepLabels = labels; + } + + @Override + protected Traverser.Admin<Path> processNextStart() { + final Traverser.Admin<Path> traverser = super.processNextStart(); + PathProcessor.keepLabels(traverser, keepLabels); + return traverser; + } + + @Override + public Set<String> getKeepLabels() { return this.keepLabels; } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java ---------------------------------------------------------------------- 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 fa38903..3bd5c1d 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 @@ -42,6 +42,7 @@ public final class SelectOneStep<S, E> extends MapStep<S, E> implements Traversa private final Pop pop; private final String selectKey; private Traversal.Admin<S, E> selectTraversal = null; + private Set<String> keepLabels; public SelectOneStep(final Traversal.Admin traversal, Pop pop, final String selectKey) { super(traversal); @@ -115,6 +116,23 @@ public final class SelectOneStep<S, E> extends MapStep<S, E> implements Traversa public Pop getPop() { return this.pop; } + + @Override + public void setKeepLabels(Set<String> labels) { + this.keepLabels = labels; + } + + @Override + public Set<String> getKeepLabels() { return this.keepLabels; } + + @Override + protected Traverser.Admin<E> processNextStart() { + final Traverser.Admin<E> traverser = super.processNextStart(); + if(!(this.getTraversal().getParent() instanceof MatchStep)) { + PathProcessor.keepLabels(traverser, keepLabels); + } + return traverser; + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java ---------------------------------------------------------------------- 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 af8cfdb..ecd5581 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 @@ -49,6 +49,7 @@ public final class SelectStep<S, E> extends MapStep<S, Map<String, E>> implement private final Pop pop; private final List<String> selectKeys; private final Set<String> selectKeysSet; + private Set<String> keepLabels; public SelectStep(final Traversal.Admin traversal, final Pop pop, final String... selectKeys) { super(traversal); @@ -141,4 +142,19 @@ public final class SelectStep<S, E> extends MapStep<S, Map<String, E>> implement public Pop getPop() { return this.pop; } + + @Override + public void setKeepLabels(Set<String> labels) { + this.keepLabels = labels; + } + + @Override + public Set<String> getKeepLabels() { return this.keepLabels; } + + @Override + protected Traverser.Admin<Map<String, E>> processNextStart() { + final Traverser.Admin<Map<String, E>> traverser = super.processNextStart(); + PathProcessor.keepLabels(traverser, keepLabels); + return traverser; + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java index 3573b98..a3fb054 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java @@ -44,6 +44,7 @@ import java.util.function.Supplier; public final class TreeStep<S> extends ReducingBarrierStep<S, Tree> implements TraversalParent, ByModulating, PathProcessor { private TraversalRing<Object, Object> traversalRing = new TraversalRing<>(); + private Set<String> keepLabels; public TreeStep(final Traversal.Admin traversal) { super(traversal); @@ -111,6 +112,14 @@ public final class TreeStep<S> extends ReducingBarrierStep<S, Tree> implements T this.traversalRing.reset(); } + @Override + public void setKeepLabels(Set<String> labels) { + this.keepLabels = labels; + } + + @Override + public Set<String> getKeepLabels() { return this.keepLabels; } + /////////// public static final class TreeBiOperator implements BinaryOperator<Tree>, Serializable { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java index 18a4798..6351455 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java @@ -44,6 +44,7 @@ public final class TreeSideEffectStep<S> extends SideEffectStep<S> implements Si private TraversalRing<Object, Object> traversalRing; private String sideEffectKey; + private Set<String> keepLabels; public TreeSideEffectStep(final Traversal.Admin traversal, final String sideEffectKey) { super(traversal); @@ -115,4 +116,12 @@ public final class TreeSideEffectStep<S> extends SideEffectStep<S> implements Si public Set<TraverserRequirement> getRequirements() { return this.getSelfAndChildRequirements(TraverserRequirement.PATH, TraverserRequirement.SIDE_EFFECTS); } + + @Override + public void setKeepLabels(Set<String> labels) { + this.keepLabels = labels; + } + + @Override + public Set<String> getKeepLabels() { return this.keepLabels; } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/EmptyPath.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/EmptyPath.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/EmptyPath.java index c40f228..0c6827e 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/EmptyPath.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/EmptyPath.java @@ -52,6 +52,9 @@ public final class EmptyPath implements Path, Serializable { } @Override + public Path retract(final Set<String> labels) { return this; } + + @Override public <A> A get(final String label) { throw Path.Exceptions.stepWithProvidedLabelDoesNotExist(label); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePath.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePath.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePath.java index 1e936c8..156cd61 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePath.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePath.java @@ -22,10 +22,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.Pop; import java.io.Serializable; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Queue; import java.util.Set; /** @@ -75,6 +78,201 @@ public class ImmutablePath implements Path, ImmutablePathImpl, Serializable, Clo return new ImmutablePath(this.previousPath, this.currentObject, temp); } +// @Override + public Path retract3(final Set<String> retractLabels) { + if(retractLabels == null || retractLabels.isEmpty()) { + return this; + } + + List<Set<String>> labelList = this.labels(); + boolean found = false; + for(Set<String> labelSet : labelList) { + if (!Collections.disjoint(labelSet, retractLabels)) { + found = true; + break; + } + } + if(!found) { + return this; + } + return this; + } + + private Path retract(final Set<String> labels, final List<Path> pathTrail) { + + Path current = pathTrail.get(0); + if (current instanceof TailPath) { + // return head + return pathTrail.get(pathTrail.size() - 1); + } else { + ImmutablePath immutablePath = (ImmutablePath) current; + if(!Collections.disjoint(labels, immutablePath.currentLabels)) { + // clone all the way back up and replace parents + ImmutablePath parentPath = null; + for(int i = pathTrail.size() - 1; i > 0; i--) { + ImmutablePath clonedPath = cloneImmutablePath((ImmutablePath)pathTrail.get(i)); + if(parentPath != null) { + parentPath.previousPath = clonedPath; + } + parentPath = clonedPath; + pathTrail.set(i, clonedPath); + } + ImmutablePath clonedCurrent = cloneImmutablePath(immutablePath); + clonedCurrent.currentLabels.removeAll(labels); + if(clonedCurrent.currentLabels.isEmpty()) { + if(pathTrail.size() > 1) { + ((ImmutablePath) pathTrail.get(1)).previousPath = clonedCurrent.previousPath; + } else { + pathTrail.remove(0); + } + } + pathTrail.add(clonedCurrent.previousPath); + } else { + pathTrail.add(((ImmutablePath) pathTrail.get(0)).previousPath); + } + retract(labels, pathTrail); + } + return null; + } + + @Override + public Path retract(final Set<String> labels) { + if(labels == null || labels.isEmpty()) { + return this; + } + +// System.out.println("RETRACTING: " + labels); +// +// labels.removeIf(val -> (val.indexOf("0") >= 0)); +// +// System.out.println("AFTER LABELS: " + labels); + + ImmutablePath parent; + ImmutablePath child; + + // first see if the labels in the set are even present in this path + boolean found = false; + for(final Set<String> labelSet : labels()) { + if(!Collections.disjoint(labelSet, labels)) { + found = true; + break; + } + } + + if(!found) { + // nothing to do here... + return this; + } + + if(this.previousPath instanceof TailPath) { + ImmutablePath clone = cloneImmutablePath(this); + clone.currentLabels.removeAll(labels); + clone.previousPath = TailPath.instance(); + if(clone.currentLabels.isEmpty()) { + // return the previous tail path because this path segment can be dropped + return clone.previousPath; + } + return clone; + } + + if(this.previousPath != null) { + parent = this; + child = (ImmutablePath)this.previousPath; + } else { + parent = (ImmutablePath)this.previousPath; + child = this; + } + + if(!Collections.disjoint(parent.currentLabels, labels)) { + ImmutablePath clonedParent = cloneImmutablePath(parent); + clonedParent.currentLabels.removeAll(labels); + if(clonedParent.currentLabels.isEmpty()) { + parent = (ImmutablePath) parent.previousPath; + } else { + parent = clonedParent; + } + } + + // store the head and return it at the end of this + ImmutablePath head = parent; + + // parents can be a mixture of ImmutablePaths and collapsed + // cloned ImmutablePaths that are a result of branching + List<Object> parents = new ArrayList<>(); + parents.add(parent); + + while(true) { + + if(!Collections.disjoint(child.currentLabels, labels)) { +// child.currentLabels.removeAll(labels); + } else { + parents.add(child); + if(child.previousPath instanceof TailPath) { + // nothing to see...we're done here + break; + } + child = (ImmutablePath)child.previousPath; + continue; + } + // split path + // clone child + ImmutablePath clone = cloneImmutablePath(child); + clone.currentLabels.removeAll(labels); + if(clone.currentLabels.isEmpty()) { + clone.currentObject = null; + } + + + // walk back up and build parent clones or reuse + // other previously cloned paths + boolean headFound = false; + if(parents.size() > 0) { + boolean first = true; + // construct parents up to this point + ImmutablePath newPath = cloneImmutablePath((ImmutablePath)parents.get(0)); + // replace the previous + ImmutablePath prevPath = newPath; + ImmutablePath lastPath = prevPath; + if(!headFound) { + head = newPath; + } + for(int i = 1; i < parents.size(); i++) { + ImmutablePath clonedPrev = cloneImmutablePath((ImmutablePath) parents.get(i)); + prevPath.previousPath = clonedPrev; + lastPath = clonedPrev; + prevPath = clonedPrev; + } + +// if(previous == null) { + if(clone.currentLabels.isEmpty()) { + lastPath.previousPath = clone.previousPath; + } else { + lastPath.previousPath = clone; + } + // hack! +// break; +// } + + parents = new ArrayList<>(); + headFound = true; + parents.add(lastPath); + + if(child.previousPath instanceof TailPath) { + // nothing to see...we're done here + break; + } + + child = (ImmutablePath)child.previousPath; + } + } + + return head; + } + + private static ImmutablePath cloneImmutablePath(final ImmutablePath path) { + return new ImmutablePath(path.previousPath, path.currentObject, new HashSet<>(path.currentLabels)); + } + @Override public <A> A get(final int index) { return (this.size() - 1) == index ? (A) this.currentObject : this.previousPath.get(index); @@ -195,6 +393,12 @@ public class ImmutablePath implements Path, ImmutablePathImpl, Serializable, Clo @Override public Path extend(final Set<String> labels) { + if(labels.size() == 0) { return this; } + throw new UnsupportedOperationException("A head path can not have labels added to it"); + } + + @Override + public Path retract(final Set<String> labels) { throw new UnsupportedOperationException("A head path can not have labels added to it"); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePathUtil.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePathUtil.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePathUtil.java new file mode 100644 index 0000000..4b7259b --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ImmutablePathUtil.java @@ -0,0 +1,37 @@ +/* + * 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.step.util; + +import org.apache.tinkerpop.gremlin.process.traversal.Pop; + +import java.util.Set; + +/** + * Created by twilmes on 4/11/16. + */ +public class ImmutablePathUtil { + + public static ImmutablePath retract(final ImmutablePath path, final Set<String> labels) { + // walk back through immutable path. Drop labels that are our list + for(final String label : labels) { + path.get(Pop.first, label); + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/MutablePath.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/MutablePath.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/MutablePath.java index 0a35f90..ba1a6ca 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/MutablePath.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/MutablePath.java @@ -24,10 +24,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.Pop; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; /** * @author Marko A. Rodriguez (http://markorodriguez.com) @@ -80,7 +83,39 @@ public class MutablePath implements Path, Serializable { @Override public Path extend(final Set<String> labels) { - this.labels.get(this.labels.size() - 1).addAll(labels); + if(labels.size() > 0) { + this.labels.get(this.labels.size() - 1).addAll(labels); + } + return this; + } + + @Override + public Path retract(final Set<String> removeLabels) { + +// removeLabels.removeIf(val -> (val.indexOf("0") >= 0)); + +// System.out.println("Removing: " + removeLabels); + + for (int i = this.labels.size() - 1; i >= 0; i--) { + for (final String label : removeLabels) { + synchronized (this.labels.get(i)) { + if (this.labels().get(i).contains(label)) { + this.labels.get(i).remove(label); +// System.out.println("REMOVED: " + label); + boolean empty = false; + if (this.labels.get(i).size() == 0) { + this.labels.remove(i); + this.objects.remove(i); + empty = true; + } + // label was found, so break out + if(empty) { + break; + } + } + } + } + } return this; } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PrunePathStrategy.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PrunePathStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PrunePathStrategy.java new file mode 100644 index 0000000..5581463 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PrunePathStrategy.java @@ -0,0 +1,285 @@ +/* + * 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.strategy.optimization; + +import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing; +import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; +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.step.map.MatchStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.PathStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.util.PathUtil; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Ted Wilmes (http://twilmes.org) + */ +public final class PrunePathStrategy extends AbstractTraversalStrategy<TraversalStrategy.OptimizationStrategy> implements TraversalStrategy.OptimizationStrategy { + + private static final PrunePathStrategy INSTANCE = new PrunePathStrategy(); + private static final Set<Class<? extends OptimizationStrategy>> PRIORS = new HashSet<>(); + + static { + PRIORS.add(PathProcessorStrategy.class); + } + + private PrunePathStrategy() { + } + + public static PrunePathStrategy instance() { + return INSTANCE; + } + + private void setKeepLabels(final Traversal.Admin<?, ?> traversal, final Set<String> keepLabels) { + final List<Step> steps = traversal.getSteps(); + for(int i = steps.size() - 1; i >= 0; i--) { + final Step step = steps.get(i); + if (step instanceof PathProcessor) { +// System.out.println("Keeping: " + keepLabels); + if(keepLabels == null) { + ((PathProcessor) step).setKeepLabels(null); + } else { + ((PathProcessor) step).setKeepLabels(new HashSet<>(keepLabels)); + } + } + Set<String> referencedLabels = PathUtil.getReferencedLabels(step); + if(step instanceof MatchStep && step.getNextStep() instanceof EmptyStep) { + ((PathProcessor) step).setKeepLabels(referencedLabels); + } else if (step instanceof MatchStep) { + keepLabels.addAll(referencedLabels); + } + + if(step instanceof TraversalParent) { + TraversalParent traversalParent = ((TraversalParent) step); + final List<Traversal.Admin<?, ?>> subTraversals = new ArrayList<>(); + subTraversals.addAll(traversalParent.getLocalChildren()); + subTraversals.addAll(traversalParent.getGlobalChildren()); + HashSet<String> keepers = new HashSet<>(); + for(final Traversal.Admin<?, ?> subTraversal : subTraversals) { + if(keepLabels != null) { + keepers.addAll(keepLabels); + keepers.addAll(referencedLabels); + if(step instanceof MatchStep) { +// System.out.println(step); + keepers.addAll(((MatchStep)step).getMatchStartLabels()); + keepers.addAll(((MatchStep)step).getMatchEndLabels()); + } + setKeepLabels(subTraversal, keepers); + } else { + setKeepLabels(subTraversal, null); + } + } + } + if(keepLabels != null) { + keepLabels.addAll(referencedLabels); + } + } + } + +// @Override + public void apply3(final Traversal.Admin<?, ?> traversal) { + boolean hasPathStep = false; + final List<PathStep> pathSteps = TraversalHelper.getStepsOfAssignableClassRecursively(PathStep.class, traversal); + final List<SubgraphStep> subgraphSteps = TraversalHelper.getStepsOfAssignableClassRecursively(SubgraphStep.class, traversal); + if(!pathSteps.isEmpty() || !subgraphSteps.isEmpty()) { + hasPathStep = true; + } + + // This is a bit weird at this point because the strategy application already happens in a recursive fashion + // but we shortcut that for this strategy because we want to be able to collect all of the referenced labels + // at all levels of the traversal and then set the keep labels accordingly for each PathProcessor + if(!(traversal.getParent() instanceof EmptyStep)) { + return; + } + + Set<String> keepLabels = null; + if(!hasPathStep) { + keepLabels = new HashSet<>(); + } + + setKeepLabels(traversal, keepLabels); + } + + protected Set<String> getParentReferencedLabels(final Traversal.Admin<?, ?> traversal) { + if(traversal.getParent().equals(EmptyStep.instance())) { + return Collections.EMPTY_SET; + } + Step<?, ?> parent = traversal.getParent().asStep(); + Set<String> referencedLabels = new HashSet<>(); + // get referenced labels from this traversal + referencedLabels.addAll(PathUtil.getReferencedLabels(traversal)); + Set<String> topLevelLabels = new HashSet<>(); + while(true) { + // is this parent the top level? If so, walk forwards and gather labels from that point forward + Step<?, ?> step; + boolean topLevelParent = false; + if(parent.getTraversal().getParent().equals(EmptyStep.instance())) { + step = parent; + topLevelParent = true; + } else { + // start at the beginning of the traversal + step = parent.getTraversal().getStartStep(); + } + do { + Set<String> labels = PathUtil.getReferencedLabels(step); +// System.out.println("Adding referenced labels." + labels); + if(topLevelParent) { + topLevelLabels.addAll(labels); + } else { + referencedLabels.addAll(labels); + } + step = step.getNextStep(); +// System.out.println(step); + } while(!(step.equals(EmptyStep.instance()))); + if(topLevelParent) { + step = parent; + do { + if(step instanceof PathProcessor) { + ((PathProcessor) step).getKeepLabels().addAll(referencedLabels); +// System.out.println("Added keep labels to: " + step); + } + step = step.getPreviousStep(); + } while (!(step.equals(EmptyStep.instance()))); + break; + } else { + parent = parent.getTraversal().getParent().asStep(); + } + } + referencedLabels.addAll(topLevelLabels); + return referencedLabels; + } + + @Override + public void apply(final Traversal.Admin<?, ?> traversal) { + TraversalParent parent = traversal.getParent(); + + Set<String> foundLabels = new HashSet<>(); + Set<String> keepLabels = new HashSet<>(); + + // If this traversal has a parent, it will need to inherit its + // parent's keep labels. If its direct parent is not a PathProcessor, + // walk back up to the top level traversal and work forwards to determine which labels + // must be kept. + if(!parent.equals(EmptyStep.instance())) { + // start with parents keep labels + if(parent instanceof PathProcessor) { + PathProcessor parentPathProcess = (PathProcessor) parent; + if(parentPathProcess.getKeepLabels() != null) keepLabels.addAll(parentPathProcess.getKeepLabels()); + } else { + Set<String> labels = getParentReferencedLabels(traversal); + keepLabels.addAll(labels); + } + } + + // check if the traversal contains any path or subgraph steps and + boolean hasPathStep = false; + final List<PathStep> pathSteps = TraversalHelper.getStepsOfAssignableClassRecursively(PathStep.class, traversal); + final List<SubgraphStep> subgraphSteps = TraversalHelper.getStepsOfAssignableClassRecursively(SubgraphStep.class, traversal); + if(!pathSteps.isEmpty() || !subgraphSteps.isEmpty()) { + hasPathStep = true; + } + + final List<Step> steps = traversal.getSteps(); + for(int i = steps.size() - 1; i >= 0; i--) { + Step currentStep = steps.get(i); + if(!hasPathStep) { + // maintain our list of labels to keep, repeatedly adding labels that were found during + // the last iteration + keepLabels.addAll(foundLabels); + + final Set<String> labels = PathUtil.getReferencedLabels(currentStep); + for(final String label : labels) { + if(foundLabels.contains(label)) { + keepLabels.add(label); + } else { + foundLabels.add(label); + } + } + +// if (currentStep instanceof Parameterizing) { +// Parameters parameters = ((Parameterizing) currentStep).getParameters(); +// for (Traversal.Admin trav : parameters.getTraversals()) { +// for (Object ss : trav.getSteps()) { +// if (ss instanceof Scoping) { +// Set<String> labels = ((Scoping) ss).getScopeKeys(); +// for (String label : labels) { +// if (foundLabels.contains(label)) { +// keepLabels.add(label); +// } else { +// // it'll get added later, b/c we can drop after this step +// foundLabels.add(label); +// } +// } +// } +// } +// } +// } +// +// if (currentStep instanceof Scoping) { +// Set<String> labels = new HashSet<>(((Scoping) currentStep).getScopeKeys()); +// if (currentStep instanceof MatchStep) { +// // if this is the last step, keep everything, else just add founds +// if (currentStep.getNextStep() instanceof EmptyStep) { +// labels.addAll(((MatchStep) currentStep).getMatchEndLabels()); +// labels.addAll(((MatchStep) currentStep).getMatchStartLabels()); +// } +// } +// for (final String label : labels) { +// if (foundLabels.contains(label)) { +// keepLabels.add(label); +// } else { +// foundLabels.add(label); +// } +// } +// } + if (currentStep instanceof PathProcessor) { +// if (i != traversal.getSteps().size()) { +// // add in all match labels +// ((PathProcessor) currentStep).setKeepLabels(new HashSet<>(foundLabels)); +// } + ((PathProcessor) currentStep).setKeepLabels(new HashSet<>(keepLabels)); + } + } else { + // if there is a PathStep or SubgraphStep in the traversal, do not drop labels + if (currentStep instanceof PathProcessor) { + // set keep labels to null so that no labels are dropped + ((PathProcessor) currentStep).setKeepLabels(null); + } + } + } + } + + @Override + public Set<Class<? extends OptimizationStrategy>> applyPrior() { + return PRIORS; + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java index b32126b..793ead6 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java @@ -23,8 +23,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.Pop; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.util.ImmutablePath; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.MutablePath; import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceFactory; +import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -85,6 +88,45 @@ public class B_LP_O_S_SE_SL_Traverser<T> extends B_O_S_SE_SL_Traverser<T> { } @Override + public void keepLabels(final Set<String> labels) { + if (!labels.isEmpty()) { + Set<String> retractLabels = new HashSet<>(); + List<Set<String>> existingLabels = this.path.labels(); + for(Set<String> labelSet : existingLabels) { + for(String l : labelSet) { + if(labels.contains(l) == false) { retractLabels.add(l); }; + } + } + this.path = this.path.retract(retractLabels); + } else if (labels.isEmpty()) { + Set<String> retractLabels = new HashSet<>(); + List<Set<String>> existingLabels = this.path.labels(); + for(Set<String> labelSet : existingLabels) { + retractLabels.addAll(labelSet); + } + if(!retractLabels.isEmpty()) { + this.path = this.path.retract(retractLabels); + } + } + } + + @Override + public void dropLabels(final Set<String> labels) { + if(!labels.isEmpty()) { + this.path = this.path.retract(labels); + } + } + + @Override + public void dropPath() { + if(path instanceof ImmutablePath) { + this.path = ImmutablePath.make(); + } else { + this.path = MutablePath.make(); + } + } + + @Override public int hashCode() { return super.hashCode() + this.path.hashCode(); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_P_S_SE_SL_Traverser.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_P_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_P_S_SE_SL_Traverser.java index 353d1ef..87cfd21 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_P_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_P_S_SE_SL_Traverser.java @@ -25,6 +25,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.util.ImmutablePath; import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceFactory; +import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -81,6 +83,30 @@ public class LP_O_OB_P_S_SE_SL_Traverser<T> extends O_OB_S_SE_SL_Traverser<T> { } @Override + public void keepLabels(final Set<String> labels) { + if (!labels.isEmpty()) { + Set<String> retractLabels = new HashSet<>(); + List<Set<String>> existingLabels = this.path.labels(); + for(Set<String> labelSet : existingLabels) { + for(String l : labelSet) { + if(labels.contains(l) == false) { retractLabels.add(l); }; + } + } + this.path = this.path.retract(retractLabels); + } + } + + @Override + public void dropLabels(final Set<String> labels) { + this.path = path.retract(new HashSet(labels)); + } + + @Override + public void dropPath() { + this.path = ImmutablePath.make(); + } + + @Override public int hashCode() { return super.hashCode() + this.path.hashCode(); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java index 1ced9ef..f23ac6e 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java @@ -78,6 +78,22 @@ public abstract class AbstractTraverser<T> implements Traverser<T>, Traverser.Ad } @Override + public void keepLabels(final Set<String> labels) { + + } + + @Override + public void dropLabels(final Set<String> labesl) { + + } + + @Override + public void dropPath() { + + } + + + @Override public void set(final T t) { this.t = t; } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java index da391f5..7c99cb5 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java @@ -50,6 +50,21 @@ public final class EmptyTraverser<T> implements Traverser<T>, Traverser.Admin<T> } @Override + public void keepLabels(final Set<String> labels) { + + } + + @Override + public void dropLabels(final Set<String> labels) { + + } + + @Override + public void dropPath() { + + } + + @Override public void set(final T t) { } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/PathUtil.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/PathUtil.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/PathUtil.java new file mode 100644 index 0000000..c38a5be --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/PathUtil.java @@ -0,0 +1,80 @@ +/* + * 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.util; + +import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing; +import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +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.step.map.MatchStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Ted Wilmes (http://twilmes.org) + */ +public class PathUtil { + + public static Set<String> getReferencedLabels(Traversal.Admin<?, ?> traversal) { + final Set<String> referencedLabels = new HashSet<>(); + for(final Step<?, ?> step : traversal.getSteps()) { + referencedLabels.addAll(getReferencedLabels(step)); + } + return referencedLabels; + } + + public static Set<String> getReferencedLabels(Step step) { + final Set<String> referencedLabels = new HashSet<>(); + + if (step instanceof Parameterizing) { + Parameters parameters = ((Parameterizing) step).getParameters(); + for (Traversal.Admin trav : parameters.getTraversals()) { + for (Object ss : trav.getSteps()) { + if (ss instanceof Scoping) { + Set<String> labels = ((Scoping) ss).getScopeKeys(); + for (String label : labels) { + referencedLabels.add(label); + } + } + } + } + } + + if (step instanceof Scoping) { + Set<String> labels = new HashSet<>(((Scoping) step).getScopeKeys()); + if (step instanceof MatchStep) { + // if this is the last step, keep everything, else just add founds + if (step.getNextStep() instanceof EmptyStep) { + labels.addAll(((MatchStep) step).getMatchEndLabels()); + labels.addAll(((MatchStep) step).getMatchStartLabels()); + } + } + referencedLabels.addAll(labels); + + } + + return referencedLabels; + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java index 66c1a4d..db60497 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java @@ -76,6 +76,18 @@ public class PathTest { assertEquals(Integer.valueOf(2), path.get(1)); assertEquals(Integer.valueOf(3), path.get(2)); assertEquals(Integer.valueOf(3), path.get(3)); + Path retractedPath = path.retract(Collections.singleton("f")); + assertFalse(path.hasLabel("f")); + assertEquals(retractedPath, path); + path = path.retract(Collections.singleton("b")); + assertFalse(path.hasLabel("b")); + path = path.retract(Collections.singleton("a")); + assertFalse(path.hasLabel("a")); + assertTrue(path.hasLabel("d")); + path = path.retract(new HashSet<>(Arrays.asList("c", "d"))); + assertFalse(path.hasLabel("c")); + assertFalse(path.hasLabel("d")); + // todo add object assert to check retract behavior }); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java index 37e70bf..e47ab4b 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStepTest.java @@ -21,17 +21,23 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor; import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.CoinStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.ConnectiveStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WherePredicateStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WhereTraversalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PrunePathStrategy; import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_LP_O_P_S_SE_SL_TraverserGenerator; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser; +import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies; import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; import org.junit.Test; import java.util.Arrays; @@ -418,4 +424,44 @@ public class MatchStepTest extends StepTest { as("b").in("created").count().is(P.gt(1))).asAdmin(); assertEquals("a", MatchStep.Helper.computeStartLabel(((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren())); } + + @Test + public void testGetRemainingTraversals() { + Traverser.Admin traverser = B_LP_O_P_S_SE_SL_TraverserGenerator.instance().generate(1, EmptyStep.instance(), 1l); + traverser.addLabels(Collections.singleton("a")); + + Traversal<Object, Vertex> mTraversal1 = as("a").out().as("b"); + Traversal<Object, Vertex> mTraversal2 = as("b").out().as("c"); + Traversal<Object, Vertex> mTraversal3 = as("a").out().as("d"); + Traversal<Object, Vertex> mTraversal4 = as("c").out().as("e"); + Traversal<Object, Vertex> mTraversal5 = as("c").out().as("f"); + + + Traversal.Admin<?, ?> traversal = __.match( + mTraversal1, + mTraversal2, + mTraversal3, + mTraversal4, + mTraversal5).asAdmin(); + + final TraversalStrategies strategies = new DefaultTraversalStrategies(); + strategies.addStrategies(PrunePathStrategy.instance()); + traversal.asAdmin().setStrategies(strategies); + traversal.asAdmin().applyStrategies(); + + MatchStep matchStep = (MatchStep) traversal.getStartStep(); + assertEquals(new HashSet<>(Arrays.asList("a", "b", "c", "d", "e", "f")), matchStep.getKeepLabels()); + + traverser.getTags().add(mTraversal1.asAdmin().getStartStep().getId()); + traverser.setStepId(mTraversal2.asAdmin().getStartStep().getId()); + + List<Traversal.Admin<?, ?>> remainingTraversals = matchStep.getRemainingTraversals(traverser); + assertEquals(Arrays.asList(mTraversal2, mTraversal3, mTraversal4, mTraversal5), remainingTraversals); + + traverser.getTags().add(mTraversal2.asAdmin().getStartStep().getId()); + traverser.setStepId(mTraversal3.asAdmin().getStartStep().getId()); + + remainingTraversals = matchStep.getRemainingTraversals(traverser); + assertEquals(Arrays.asList(mTraversal3, mTraversal4, mTraversal5), remainingTraversals); + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e4e693e4/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PrunePathStrategyTest.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PrunePathStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PrunePathStrategyTest.java new file mode 100644 index 0000000..3d13ad7 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PrunePathStrategyTest.java @@ -0,0 +1,120 @@ +/* + * 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.strategy.optimization; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; +import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; + +import static org.apache.tinkerpop.gremlin.process.traversal.P.gte; +import static org.apache.tinkerpop.gremlin.process.traversal.P.neq; +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.as; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * @author Ted Wilmes (http://twilmes.org) + */ +@RunWith(Parameterized.class) +public class PrunePathStrategyTest { + + @Parameterized.Parameter(value = 0) + public Traversal.Admin traversal; + + @Parameterized.Parameter(value = 1) + public List<Set<String>> labels; + + void applyPrunePathStrategy(final Traversal traversal) { + final TraversalStrategies strategies = new DefaultTraversalStrategies(); + strategies.addStrategies(PrunePathStrategy.instance()); + traversal.asAdmin().setStrategies(strategies); + traversal.asAdmin().applyStrategies(); + } + + @Test + public void doTest() { + applyPrunePathStrategy(traversal); + // get all path processors + List<Object> keepLabels = getKeepLabels(traversal); + + assertEquals(labels, keepLabels); + } + + private List<Object> getKeepLabels(Traversal.Admin traversal) { + List<Object> keepLabels = new ArrayList<>(); + for(Step step : (List<Step>)traversal.getSteps()) { + if(step instanceof PathProcessor) { + final Set<String> keepers = ((PathProcessor) step).getKeepLabels(); + if(keepers != null) { + keepLabels.add(keepers); + } + } + if(step instanceof TraversalParent) { + TraversalParent parent = (TraversalParent) step; + List<Traversal.Admin<?, ?>> children = new ArrayList<>(); + children.addAll(parent.getGlobalChildren()); + children.addAll(parent.getLocalChildren()); + for(Traversal.Admin<?, ?> child : children) { + List<Object> childLabels = getKeepLabels(child); + if(childLabels.size() > 0) { + keepLabels.add(childLabels); + } + } + } + } + return keepLabels; + } + + @Parameterized.Parameters(name = "{0}") + public static Iterable<Object[]> generateTestParameters() { + + return Arrays.asList(new Object[][]{ + {__.V().as("a").out().as("b").where(neq("a")).out(), Arrays.asList(Collections.EMPTY_SET)}, + {__.V().as("a").out().where(neq("a")).out().select("a"), Arrays.asList(Collections.singleton("a"), Collections.EMPTY_SET)}, + {__.V().match(__.as("a").out().as("b")), Arrays.asList(new HashSet<>(Arrays.asList("a", "b")))}, + {__.V().match(__.as("a").out().as("b")).select("a"), Arrays.asList(new HashSet<>(Arrays.asList("a", "b")), Collections.EMPTY_SET)}, + {__.V().out().out().match( + as("a").in("created").as("b"), + as("b").in("knows").as("c")).select("c").out("created").where(neq("a")).values("name"), + Arrays.asList(new HashSet<>(Arrays.asList("a", "b", "c")), Collections.singleton("a"), Collections.EMPTY_SET)}, + {__.V().as("a").out().select("a").path(), Arrays.asList()}, + {__.V().as("a").out().select("a").subgraph("b"), Arrays.asList()}, + {__.V().out().as("a").where(neq("a")).out().where(neq("a")), Arrays.asList(Collections.singleton("a"), Collections.EMPTY_SET)}, + {__.V().out().as("a").where(__.out().select("a").values("prop").count().is(gte(1))).out().where(neq("a")), Arrays.asList(Arrays.asList(Collections.singleton("a")), Collections.EMPTY_SET)}, + {__.outE().inV().group().by(__.inE().outV().groupCount().by(__.both().count().is(P.gt(2)))), Arrays.asList()}, + {__.V().as("a").repeat(__.out().where(neq("a"))).emit().select("a").values("test"), Arrays.asList(Arrays.asList(Collections.singleton("a")), Collections.EMPTY_SET)} + }); + } +}