Repository: tinkerpop Updated Branches: refs/heads/TINKERPOP-1456 [created] 4f723eed8
Added support for SubgraphStrategy.vertexProperties(). Added some test cases to verify proper functioning. Also, cleaned up Stream-stuff in SubgraphStrategy. Going to do some more cleanup there to make things clean and efficient. Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/4f723eed Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/4f723eed Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/4f723eed Branch: refs/heads/TINKERPOP-1456 Commit: 4f723eed82294b95f8409db5cc890f9f41ed84ae Parents: 54ed33d Author: Marko A. Rodriguez <okramma...@gmail.com> Authored: Mon Sep 19 13:52:33 2016 -0600 Committer: Marko A. Rodriguez <okramma...@gmail.com> Committed: Mon Sep 19 13:52:33 2016 -0600 ---------------------------------------------------------------------- .../strategy/decoration/SubgraphStrategy.java | 107 ++++++++++++++----- .../decoration/SubgraphStrategyProcessTest.java | 25 ++++- 2 files changed, 105 insertions(+), 27 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4f723eed/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java index 967a19b..f1a42cd 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java @@ -23,6 +23,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.LambdaFilterStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.OrStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TraversalFilterStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep; @@ -30,14 +32,19 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeOtherVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertyValueStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversal; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.PropertyType; import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; import java.util.ArrayList; import java.util.List; @@ -58,9 +65,10 @@ public final class SubgraphStrategy extends AbstractTraversalStrategy<TraversalS private final Traversal.Admin<Vertex, ?> vertexCriterion; private final Traversal.Admin<Edge, ?> edgeCriterion; + private final Traversal.Admin<VertexProperty, ?> vertexPropertyCriterion; private final String MARKER = Graph.Hidden.hide(UUID.randomUUID().toString()); - private SubgraphStrategy(final Traversal<Vertex, ?> vertexCriterion, final Traversal<Edge, ?> edgeCriterion) { + private SubgraphStrategy(final Traversal<Vertex, ?> vertexCriterion, final Traversal<Edge, ?> edgeCriterion, final Traversal<VertexProperty, ?> vertexPropertyCriterion) { this.vertexCriterion = null == vertexCriterion ? null : vertexCriterion.asAdmin(); // if there is no vertex predicate there is no need to test either side of the edge @@ -79,10 +87,14 @@ public final class SubgraphStrategy extends AbstractTraversalStrategy<TraversalS this.edgeCriterion = edgeCriterion.asAdmin().addStep(new TraversalFilterStep<>(edgeCriterion.asAdmin(), vertexPredicate)); } + this.vertexPropertyCriterion = null == vertexPropertyCriterion ? null : vertexPropertyCriterion.asAdmin(); + if (null != this.vertexCriterion) this.metadataLabelStartStep(this.vertexCriterion); if (null != this.edgeCriterion) this.metadataLabelStartStep(this.edgeCriterion); + if (null != this.vertexPropertyCriterion) + this.metadataLabelStartStep(this.vertexPropertyCriterion); } private final void metadataLabelStartStep(final Traversal.Admin<?, ?> traversal) { @@ -123,61 +135,101 @@ public final class SubgraphStrategy extends AbstractTraversalStrategy<TraversalS applyCriterion(edgeStepsToInsertFilterAfter, traversal, this.edgeCriterion); } - // explode g.V().out() to g.V().outE().inV() only if there is an edge predicate otherwise - vertexSteps.stream().filter(VertexStep::returnsVertex).forEach(step -> { + // turn g.V().out() to g.V().outE().inV() only if there is an edge predicate otherwise + for (final VertexStep<?> step : vertexSteps) { + if (step.returnsEdge()) + continue; if (null != this.vertexCriterion && null == edgeCriterion) { - TraversalHelper.insertAfterStep(new TraversalFilterStep<>(traversal, this.vertexCriterion.clone()), step, traversal); + TraversalHelper.insertAfterStep(new TraversalFilterStep<>(traversal, (Traversal) this.vertexCriterion.clone()), step, traversal); } else { - final VertexStep someEStep = new VertexStep<>(traversal, Edge.class, step.getDirection(), step.getEdgeLabels()); - final Step someVStep = step.getDirection() == Direction.BOTH ? + final VertexStep<Edge> someEStep = new VertexStep<>(traversal, Edge.class, step.getDirection(), step.getEdgeLabels()); + final Step<Edge, Vertex> someVStep = step.getDirection() == Direction.BOTH ? new EdgeOtherVertexStep(traversal) : new EdgeVertexStep(traversal, step.getDirection().opposite()); - // if step was labeled then propagate those labels to the new step that will return the vertex - transferLabels(step, someVStep); - - TraversalHelper.replaceStep(step, someEStep, traversal); + TraversalHelper.replaceStep((Step<Vertex, Edge>) step, someEStep, traversal); TraversalHelper.insertAfterStep(someVStep, someEStep, traversal); + // if step was labeled then propagate those labels to the new step that will return the vertex + for (final String label : step.getLabels()) { + step.removeLabel(label); + someVStep.addLabel(label); + } if (null != this.edgeCriterion) TraversalHelper.insertAfterStep(new TraversalFilterStep<>(traversal, this.edgeCriterion.clone()), someEStep, traversal); if (null != this.vertexCriterion) TraversalHelper.insertAfterStep(new TraversalFilterStep<>(traversal, this.vertexCriterion.clone()), someVStep, traversal); } - }); + } + + // turn g.V().properties() to g.V().properties().xxx + // turn g.V().values() to g.V().properties().xxx.value()\ + if (null != this.vertexPropertyCriterion) { + final OrStep<Object> wrappedCriterion = new OrStep<>(traversal, + new DefaultTraversal<>().addStep(new LambdaFilterStep<>(traversal, t -> !(t.get() instanceof VertexProperty))), + new DefaultTraversal<>().addStep(new TraversalFilterStep<>(traversal, this.vertexPropertyCriterion.clone()))); + for (final PropertiesStep<?> step : TraversalHelper.getStepsOfAssignableClass(PropertiesStep.class, traversal)) { + if (PropertyType.PROPERTY.equals(step.getReturnType())) { + // if the property step returns a property, then simply append the criterion + final OrStep<Object> clonedWrappedCriterion = (OrStep) wrappedCriterion.clone(); + TraversalHelper.insertAfterStep(clonedWrappedCriterion, (Step) step, traversal); + for (final String label : step.getLabels()) { + step.removeLabel(label); + clonedWrappedCriterion.addLabel(label); + } + } else { + // if the property step returns value, then replace it with a property step, append criterion, then append a value() step + final Step propertiesStep = new PropertiesStep(traversal, PropertyType.PROPERTY, step.getPropertyKeys()); + TraversalHelper.replaceStep(step, propertiesStep, traversal); + final Step filterStep = wrappedCriterion.clone(); + TraversalHelper.insertAfterStep(filterStep, propertiesStep, traversal); + final Step propertyValueStep = new PropertyValueStep(traversal); + TraversalHelper.insertAfterStep(propertyValueStep, filterStep, traversal); + // add labels to the value step after the filter has been applied + for (final String label : step.getLabels()) { + propertyValueStep.addLabel(label); + } + } + } + } } + public Traversal<Vertex, ?> getVertexCriterion() { - return vertexCriterion; + return this.vertexCriterion; } public Traversal<Edge, ?> getEdgeCriterion() { - return edgeCriterion; + return this.edgeCriterion; } + public Traversal<VertexProperty, ?> getVertexPropertyCriterion() { + return this.vertexPropertyCriterion; + } + + public static Builder build() { return new Builder(); } private void applyCriterion(final List<Step> stepsToApplyCriterionAfter, final Traversal.Admin traversal, final Traversal.Admin<? extends Element, ?> criterion) { - stepsToApplyCriterionAfter.forEach(s -> { + for (final Step<?, ?> step : stepsToApplyCriterionAfter) { // re-assign the step label to the criterion because the label should apply seamlessly after the filter final Step filter = new TraversalFilterStep<>(traversal, criterion.clone()); - transferLabels(s, filter); - TraversalHelper.insertAfterStep(filter, s, traversal); - }); - } - - private static void transferLabels(final Step from, final Step to) { - from.getLabels().forEach(label -> to.addLabel((String) label)); - to.getLabels().forEach(label -> from.removeLabel((String) label)); + for (final String label : step.getLabels()) { + step.removeLabel(label); + filter.addLabel(label); + } + TraversalHelper.insertAfterStep(filter, step, traversal); + } } public final static class Builder { private Traversal<Vertex, ?> vertexPredicate = null; private Traversal<Edge, ?> edgePredicate = null; + private Traversal<VertexProperty, ?> vertexPropertyPredicate = null; private Builder() { } @@ -192,6 +244,11 @@ public final class SubgraphStrategy extends AbstractTraversalStrategy<TraversalS return this; } + public Builder vertexProperties(final Traversal<VertexProperty, ?> vertexPropertyPredicate) { + this.vertexPropertyPredicate = vertexPropertyPredicate; + return this; + } + @Deprecated /** * @deprecated Since 3.2.2, use {@code Builder#vertices} instead. @@ -209,9 +266,9 @@ public final class SubgraphStrategy extends AbstractTraversalStrategy<TraversalS } public SubgraphStrategy create() { - if (null == this.edgePredicate && null == this.vertexPredicate) - throw new IllegalStateException("A subgraph must be filtered by an edge or vertex criterion"); - return new SubgraphStrategy(this.vertexPredicate, this.edgePredicate); + if (null == this.vertexPredicate && null == this.edgePredicate && null == this.vertexPropertyPredicate) + throw new IllegalStateException("A subgraph must be filtered by a vertex, edge, or vertex property criterion"); + return new SubgraphStrategy(this.vertexPredicate, this.edgePredicate, this.vertexPropertyPredicate); } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4f723eed/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategyProcessTest.java ---------------------------------------------------------------------- diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategyProcessTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategyProcessTest.java index 41a730a..1d49d92 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategyProcessTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategyProcessTest.java @@ -21,10 +21,8 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration; import org.apache.tinkerpop.gremlin.LoadGraphWith; import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest; import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner; -import org.apache.tinkerpop.gremlin.process.IgnoreEngine; 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.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.structure.Edge; @@ -36,6 +34,7 @@ import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; +import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.CREW; import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.both; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.bothE; @@ -366,6 +365,28 @@ public class SubgraphStrategyProcessTest extends AbstractGremlinProcessTest { sg.E(sg.E(convertToEdgeId("marko", "knows", "vadas")).next()).next(); } + @Test + @LoadGraphWith(CREW) + public void shouldFilterVertexProperties() throws Exception { + GraphTraversalSource sg = create(SubgraphStrategy.build().vertexProperties(has("startTime", P.gt(2005))).create()); + checkResults(Arrays.asList("purcellville", "baltimore", "oakland", "seattle", "aachen"), sg.V().properties("location").value()); + checkResults(Arrays.asList("purcellville", "baltimore", "oakland", "seattle", "aachen"), sg.V().values("location")); + // check to make sure edge properties are not analyzed + sg = create(SubgraphStrategy.build().vertexProperties(has("startTime", P.gt(2005))).create()); + checkResults(Arrays.asList("purcellville", "baltimore", "oakland", "seattle", "aachen"), sg.V().as("a").properties("location").as("b").select("a").outE().properties().select("b").value().dedup()); + checkResults(Arrays.asList("purcellville", "baltimore", "oakland", "seattle", "aachen"), sg.V().as("a").values("location").as("b").select("a").outE().properties().select("b").dedup()); + // + // + sg = create(SubgraphStrategy.build().vertices(has("name", P.neq("stephen"))).vertexProperties(has("startTime", P.gt(2005))).create()); + checkResults(Arrays.asList("baltimore", "oakland", "seattle", "aachen"), sg.V().properties("location").value()); + checkResults(Arrays.asList("baltimore", "oakland", "seattle", "aachen"), sg.V().values("location")); + // + sg = create(SubgraphStrategy.build().vertices(has("name", P.not(P.within("stephen", "daniel")))).vertexProperties(has("startTime", P.gt(2005))).create()); + checkResults(Arrays.asList("baltimore", "oakland", "seattle"), sg.V().properties("location").value()); + checkResults(Arrays.asList("baltimore", "oakland", "seattle"), sg.V().values("location")); + } + + private GraphTraversalSource create(final SubgraphStrategy strategy) { return graphProvider.traversal(graph, strategy); }