This is an automated email from the ASF dual-hosted git repository. colegreer pushed a commit to branch 3.6-dev in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit d98357ceac9134f8989989ace3f4b5f24b6ead2d Author: Boxuan Li <[email protected]> AuthorDate: Thu Jul 27 16:14:41 2023 -0700 [TINKERPOP-2973] Enable vertex properties filtering for GraphComputer (#2164) GraphComputer has vertexFilter and edgeFilter, but not yet a filter for vertex properties. Many vertex programs don't need many if not all vertex properties. PageRank, for example, does not need any vertex properties at all. Having a vertex property filter can greatly reduce memory/disk usage. Also related to TINKERPOP-1403 --- .../gremlin/process/computer/GraphComputer.java | 11 +++ .../gremlin/process/computer/GraphFilter.java | 99 ++++++++++++++++++---- .../gremlin/structure/util/star/StarGraph.java | 8 ++ .../gremlin/process/TraversalStrategiesTest.java | 6 ++ .../process/computer/GraphComputerTest.java | 29 ++++++- .../pagerank/PageRankVertexProgramTest.java | 6 +- .../computer/AbstractHadoopGraphComputer.java | 8 +- .../gremlin/spark/structure/SparkTest.java | 6 +- .../process/computer/TinkerGraphComputer.java | 7 ++ .../process/computer/TinkerGraphComputerView.java | 23 ++++- 10 files changed, 176 insertions(+), 27 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputer.java index 5e8fdab5cd..2baba4cb78 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputer.java @@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.computer.util.DefaultComputerResult; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; import java.util.concurrent.Future; @@ -135,6 +136,16 @@ public interface GraphComputer { */ public GraphComputer edges(final Traversal<Vertex, Edge> edgeFilter) throws IllegalArgumentException; + /** + * Add a filter that will limit which vertex properties are loaded from the graph source. The loaded vertices + * will only have those properties that pass through the provided filter. To drop all vertex properties, + * provide a traversal like __.properties("dummy") where "dummy" is not a valid vertex property. + * + * @param vertexPropertyFilter the traversal that determines which vertex properties are loaded for each vertex + * @return the updated GraphComputer with newly set vertex property filter + */ + public GraphComputer vertexProperties(final Traversal<Vertex, ? extends Property<?>> vertexPropertyFilter); + /** * Set an arbitrary configuration key/value for the underlying {@code Configuration} in the {@link GraphComputer}. * Typically, the other fluent methods in {@link GraphComputer} should be used to configure the computation. diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilter.java index 2f01dcf28a..6daa95278b 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilter.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilter.java @@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; import java.io.Serializable; @@ -35,6 +36,7 @@ import java.util.EnumMap; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -81,6 +83,7 @@ public final class GraphFilter implements Cloneable, Serializable { private Traversal.Admin<Vertex, Vertex> vertexFilter = null; private Traversal.Admin<Vertex, Edge> edgeFilter = null; + private Traversal.Admin<Vertex, ? extends Property<?>> vertexPropertyFilter = null; private Map<Direction, Map<String, Legal>> edgeLegality = new EnumMap<>(Direction.class); private boolean allowNoEdges = false; @@ -182,6 +185,16 @@ public final class GraphFilter implements Cloneable, Serializable { } } + /** + * Set the filter for selecting vertex properties from the vertices. + * + * @param vertexPropertyFilter The {@link Traversal} that will either let the vertex property pass or not. + */ + public void setVertexPropertyFilter(final Traversal<Vertex, ? extends Property<?>> vertexPropertyFilter) { + this.vertexPropertyFilter = vertexPropertyFilter.asAdmin().clone(); + } + + /** * Returns true if the provided vertex meets the vertex-filter criteria. * If no vertex filter is provided, then the vertex is considered legal. @@ -206,6 +219,19 @@ public final class GraphFilter implements Cloneable, Serializable { TraversalUtil.applyAll(vertex, this.edgeFilter); } + /** + * Returns an iterator of legal vertex properties. If no vertex property + * filter is provided, then all vertex properties are returned. + * + * @param vertex the vertex whose legal vertex properties are to be accessed. + * @return an iterator of vertex properties that are {@link Legal#YES}. + */ + public Iterator<? extends Property<?>> legalVertexProperties(final Vertex vertex) { + return null == this.vertexPropertyFilter ? + vertex.properties() : + TraversalUtil.applyAll(vertex, this.vertexPropertyFilter); + } + /** * Get the vertex filter associated with this graph filter. * @@ -224,13 +250,22 @@ public final class GraphFilter implements Cloneable, Serializable { return this.edgeFilter; } + /** + * Get the vertex property filter associated with this graph filter. + * + * @return the vertex property filter or null if not provided. + */ + public Traversal.Admin<Vertex, ? extends Property<?>> getVertexPropertyFilter() { + return this.vertexPropertyFilter; + } + /** * Whether filters have been defined. * - * @return true if either a vertex or edge filter has been provided. + * @return true if either a vertex, edge filter, or vertex property filter has been provided. */ public boolean hasFilter() { - return this.vertexFilter != null || this.edgeFilter != null; + return this.vertexFilter != null || this.edgeFilter != null || this.vertexPropertyFilter != null; } /** @@ -251,6 +286,15 @@ public final class GraphFilter implements Cloneable, Serializable { return this.vertexFilter != null; } + /** + * Whether a vertex property filter has been defined. + * + * @return true if a vertex property filter was provided. + */ + public boolean hasVertexPropertyFilter() { + return this.vertexPropertyFilter != null; + } + /** * For a particular edge directionality, get all the {@link Legal#YES} or {@link Legal#MAYBE} edge labels. * If the label set contains {@code null}, then all edge labels for that direction are positively legal. @@ -312,19 +356,22 @@ public final class GraphFilter implements Cloneable, Serializable { @Override public int hashCode() { - return (null == this.edgeFilter ? 111 : this.edgeFilter.hashCode()) ^ (null == this.vertexFilter ? 222 : this.vertexFilter.hashCode()); + int result = vertexFilter != null ? vertexFilter.hashCode() : 0; + result = 31 * result + (edgeFilter != null ? edgeFilter.hashCode() : 0); + result = 31 * result + (vertexPropertyFilter != null ? vertexPropertyFilter.hashCode() : 0); + return result; } @Override - public boolean equals(final Object object) { - if (!(object instanceof GraphFilter)) - return false; - else if (((GraphFilter) object).hasVertexFilter() && !((GraphFilter) object).vertexFilter.equals(this.vertexFilter)) - return false; - else if (((GraphFilter) object).hasEdgeFilter() && !((GraphFilter) object).edgeFilter.equals(this.edgeFilter)) - return false; - else - return true; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GraphFilter that = (GraphFilter) o; + + if (!Objects.equals(vertexFilter, that.vertexFilter)) return false; + if (!Objects.equals(edgeFilter, that.edgeFilter)) return false; + return Objects.equals(vertexPropertyFilter, that.vertexPropertyFilter); } @Override @@ -335,6 +382,8 @@ public final class GraphFilter implements Cloneable, Serializable { clone.vertexFilter = this.vertexFilter.clone(); if (null != this.edgeFilter) clone.edgeFilter = this.edgeFilter.clone(); + if (null != this.vertexPropertyFilter) + clone.vertexPropertyFilter = this.vertexPropertyFilter.clone(); return clone; } catch (final CloneNotSupportedException e) { throw new IllegalStateException(e.getMessage(), e); @@ -345,11 +394,25 @@ public final class GraphFilter implements Cloneable, Serializable { public String toString() { if (!this.hasFilter()) return "graphfilter[none]"; - else if (this.hasVertexFilter() && this.hasEdgeFilter()) - return "graphfilter[" + this.vertexFilter + "," + this.edgeFilter + "]"; - else if (this.hasVertexFilter()) - return "graphfilter[" + this.vertexFilter + "]"; - else - return "graphfilter[" + this.edgeFilter + "]"; + StringBuilder builder = new StringBuilder("graphfilter["); + boolean needDelimiter = false; + if (this.hasVertexFilter()) { + builder.append(this.vertexFilter); + needDelimiter = true; + } + if (this.hasEdgeFilter()) { + if (needDelimiter) { + builder.append(","); + } + builder.append(this.edgeFilter); + needDelimiter = true; + } + if (this.hasVertexPropertyFilter()) { + if (needDelimiter) { + builder.append(","); + } + builder.append(this.vertexPropertyFilter); + } + return builder.append("]").toString(); } } \ No newline at end of file diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java index 3bcdb7db39..0c47d78caa 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java @@ -47,6 +47,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.Set; import java.util.stream.Stream; /** @@ -575,6 +576,13 @@ public final class StarGraph implements Graph, Serializable { this.inEdges = inEdges; } } + if (graphFilter.hasVertexPropertyFilter()) { + Set<String> retainSet = new HashSet<>(); + graphFilter.legalVertexProperties(this).forEachRemaining(property -> { + retainSet.add(property.key()); + }); + this.vertexProperties.keySet().removeIf(key -> !retainSet.contains(key)); + } return Optional.of(this); } else { return Optional.empty(); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java index 1247986361..07abfa8da4 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/TraversalStrategiesTest.java @@ -30,6 +30,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Transaction; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.junit.Test; @@ -168,6 +169,11 @@ public class TraversalStrategiesTest { return this; } + @Override + public GraphComputer vertexProperties(Traversal<Vertex, ? extends Property<?>> vertexPropertyFilter) { + return this; + } + @Override public Future<ComputerResult> submit() { return new CompletableFuture<>(); diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java index ce039e8ea0..d489a434d1 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java @@ -155,6 +155,12 @@ public class GraphComputerTest extends AbstractGremlinProcessTest { } catch (final UnsupportedOperationException e) { assertEquals(GraphComputer.Exceptions.graphFilterNotSupported().getMessage(), e.getMessage()); } + try { + new BadGraphComputer().vertexProperties(__.properties("type")); + fail("Should throw an unsupported operation exception"); + } catch (final UnsupportedOperationException e) { + assertEquals(GraphComputer.Exceptions.graphFilterNotSupported().getMessage(), e.getMessage()); + } } else { fail("Should not support graph filter: " + BadGraphComputer.class); } @@ -197,6 +203,11 @@ public class GraphComputerTest extends AbstractGremlinProcessTest { throw GraphComputer.Exceptions.graphFilterNotSupported(); } + @Override + public GraphComputer vertexProperties(Traversal<Vertex, ? extends Property<?>> vertexPropertyFilter) { + throw GraphComputer.Exceptions.graphFilterNotSupported(); + } + @Override public GraphComputer configure(final String key, final Object value) { return null; @@ -1756,13 +1767,19 @@ public class GraphComputerTest extends AbstractGremlinProcessTest { } catch (final UnsupportedOperationException e) { assertEquals(GraphComputer.Exceptions.graphFilterNotSupported().getMessage(), e.getMessage()); } + try { + graphProvider.getGraphComputer(graph).vertexProperties(__.properties("prop1", "prop2")); + fail("Should throw an unsupported operation exception"); + } catch (final UnsupportedOperationException e) { + assertEquals(GraphComputer.Exceptions.graphFilterNotSupported().getMessage(), e.getMessage()); + } return; } /// VERTEX PROGRAM - graphProvider.getGraphComputer(graph).vertices(__.hasLabel("software")).program(new VertexProgramM(VertexProgramM.SOFTWARE_ONLY)).submit().get(); + graphProvider.getGraphComputer(graph).vertices(__.hasLabel("software")).vertexProperties(__.properties("dummy")).program(new VertexProgramM(VertexProgramM.SOFTWARE_ONLY)).submit().get(); graphProvider.getGraphComputer(graph).vertices(__.hasLabel("person")).program(new VertexProgramM(VertexProgramM.PEOPLE_ONLY)).submit().get(); graphProvider.getGraphComputer(graph).edges(__.bothE("knows")).program(new VertexProgramM(VertexProgramM.KNOWS_ONLY)).submit().get(); - graphProvider.getGraphComputer(graph).vertices(__.hasLabel("person")).edges(__.bothE("knows")).program(new VertexProgramM(VertexProgramM.PEOPLE_KNOWS_ONLY)).submit().get(); + graphProvider.getGraphComputer(graph).vertices(__.hasLabel("person")).vertexProperties(__.properties("name")).edges(__.bothE("knows")).program(new VertexProgramM(VertexProgramM.PEOPLE_KNOWS_ONLY)).submit().get(); graphProvider.getGraphComputer(graph).vertices(__.hasLabel("person")).edges(__.<Vertex>bothE("knows").has("weight", P.gt(0.5f))).program(new VertexProgramM(VertexProgramM.PEOPLE_KNOWS_WELL_ONLY)).submit().get(); graphProvider.getGraphComputer(graph).edges(__.<Vertex>bothE().limit(0)).program(new VertexProgramM(VertexProgramM.VERTICES_ONLY)).submit().get(); graphProvider.getGraphComputer(graph).edges(__.<Vertex>outE().limit(1)).program(new VertexProgramM(VertexProgramM.ONE_OUT_EDGE_ONLY)).submit().get(); @@ -1801,6 +1818,14 @@ public class GraphComputerTest extends AbstractGremlinProcessTest { } catch (final IllegalArgumentException e) { assertEquals(e.getMessage(), GraphComputer.Exceptions.edgeFilterAccessesAdjacentVertices(__.<Vertex>out().outE()).getMessage()); } + try { + // VertexProgramM.PEOPLE_KNOWS_ONLY needs name property + graphProvider.getGraphComputer(graph) + .vertices(__.hasLabel("person")).edges(__.bothE("knows")).vertexProperties(__.properties("dummy")) + .program(new VertexProgramM(VertexProgramM.PEOPLE_KNOWS_ONLY)).submit().get(); + } catch (final ExecutionException e) { + assertTrue(e.getMessage().contains("The property does not exist as the key has no associated value for the provided element")); + } } public static class VertexProgramM implements VertexProgram { diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/ranking/pagerank/PageRankVertexProgramTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/ranking/pagerank/PageRankVertexProgramTest.java index 6550a325b6..5c6087f9e8 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/ranking/pagerank/PageRankVertexProgramTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/ranking/pagerank/PageRankVertexProgramTest.java @@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.LoadGraphWith; import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest; import org.apache.tinkerpop.gremlin.process.computer.ComputerResult; import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; import org.junit.Test; @@ -73,8 +74,9 @@ public class PageRankVertexProgramTest extends AbstractGremlinProcessTest { @LoadGraphWith(MODERN) public void shouldExecutePageRankWithEpsilonBreak() throws Exception { if (graphProvider.getGraphComputer(graph).features().supportsResultGraphPersistCombination(GraphComputer.ResultGraph.NEW, GraphComputer.Persist.VERTEX_PROPERTIES)) { - final ComputerResult result = graph.compute(graphProvider.getGraphComputer(graph).getClass()). - program(PageRankVertexProgram.build().epsilon(0.00001d).iterations(30).create(graph)).submit().get(); // by using epsilon 0.00001, we should get iterations 11 + final ComputerResult result = graph.compute(graphProvider.getGraphComputer(graph).getClass()) + .vertexProperties(__.properties("name", "age", "lang")) + .program(PageRankVertexProgram.build().epsilon(0.00001d).iterations(30).create(graph)).submit().get(); // by using epsilon 0.00001, we should get iterations 11 result.graph().traversal().V().forEachRemaining(v -> { assertEquals(3, v.keys().size()); // name, age/lang, pageRank assertTrue(v.keys().contains("name")); diff --git a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/process/computer/AbstractHadoopGraphComputer.java b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/process/computer/AbstractHadoopGraphComputer.java index 278fc507b7..6c37def4d7 100644 --- a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/process/computer/AbstractHadoopGraphComputer.java +++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/process/computer/AbstractHadoopGraphComputer.java @@ -34,8 +34,8 @@ import org.apache.tinkerpop.gremlin.process.computer.VertexProgram; import org.apache.tinkerpop.gremlin.process.computer.util.GraphComputerHelper; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.io.Storage; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import org.apache.tinkerpop.gremlin.util.Gremlin; import org.slf4j.Logger; @@ -88,6 +88,12 @@ public abstract class AbstractHadoopGraphComputer implements GraphComputer { return this; } + @Override + public GraphComputer vertexProperties(final Traversal<Vertex, ? extends Property<?>> vertexPropertyFilter) { + this.graphFilter.setVertexPropertyFilter(vertexPropertyFilter); + return this; + } + @Override public GraphComputer result(final ResultGraph resultGraph) { this.resultGraph = resultGraph; diff --git a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/structure/SparkTest.java b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/structure/SparkTest.java index 17ef28811b..57857b3b49 100644 --- a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/structure/SparkTest.java +++ b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/structure/SparkTest.java @@ -29,6 +29,7 @@ import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph; import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoInputFormat; import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; import org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgram; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.spark.AbstractSparkTest; import org.apache.tinkerpop.gremlin.spark.process.computer.SparkGraphComputer; import org.apache.tinkerpop.gremlin.spark.process.computer.SparkHadoopGraphProvider; @@ -71,7 +72,10 @@ public class SparkTest extends AbstractSparkTest { assertEquals(i, Spark.getRDDs().size()); configuration.setProperty(Constants.GREMLIN_HADOOP_OUTPUT_LOCATION, prefix + i); Graph graph = GraphFactory.open(configuration); - graph.compute(SparkGraphComputer.class).persist(GraphComputer.Persist.VERTEX_PROPERTIES).program(PageRankVertexProgram.build().iterations(1).create(graph)).submit().get(); + graph.compute(SparkGraphComputer.class) + .persist(GraphComputer.Persist.VERTEX_PROPERTIES) + .vertexProperties(__.properties("dummy")) + .program(PageRankVertexProgram.build().iterations(1).create(graph)).submit().get(); assertNotNull(Spark.getRDD(graphRDDName)); assertEquals(i + 1, Spark.getRDDs().size()); } diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputer.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputer.java index bf6f594166..f406847c10 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputer.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputer.java @@ -33,6 +33,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph; @@ -128,6 +129,12 @@ public final class TinkerGraphComputer implements GraphComputer { return this; } + @Override + public GraphComputer vertexProperties(Traversal<Vertex, ? extends Property<?>> vertexPropertyFilter) { + this.graphFilter.setVertexPropertyFilter(vertexPropertyFilter); + return this; + } + @Override public Future<ComputerResult> submit() { // a graph computer can only be executed once diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java index 43998fb292..40a497ba03 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java @@ -21,6 +21,8 @@ package org.apache.tinkerpop.gremlin.tinkergraph.process.computer; import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; import org.apache.tinkerpop.gremlin.process.computer.GraphFilter; import org.apache.tinkerpop.gremlin.process.computer.VertexComputeKey; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Graph; @@ -36,9 +38,11 @@ import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertexProperty; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -55,6 +59,7 @@ public final class TinkerGraphComputerView { private final Set<Object> legalVertices = new HashSet<>(); private final Map<Object, Set<Object>> legalEdges = new HashMap<>(); private final GraphFilter graphFilter; + private final Set<String> retainVertexProperties; public TinkerGraphComputerView(final TinkerGraph graph, final GraphFilter graphFilter, final Set<VertexComputeKey> computeKeys) { this.graph = graph; @@ -76,6 +81,11 @@ public final class TinkerGraphComputerView { } }); } + if (this.graphFilter.hasVertexPropertyFilter()) { + retainVertexProperties = new HashSet<>(Arrays.asList(((PropertiesStep) graphFilter.getVertexPropertyFilter().getStartStep()).getPropertyKeys())); + } else { + retainVertexProperties = null; + } } public <V> Property<V> addProperty(final TinkerVertex vertex, final String key, final V value) { @@ -97,13 +107,12 @@ public final class TinkerGraphComputerView { public List<VertexProperty<?>> getProperty(final TinkerVertex vertex, final String key) { // if the vertex property is already on the vertex, use that. final List<VertexProperty<?>> vertexProperty = this.getValue(vertex, key); - return vertexProperty.isEmpty() ? (List) TinkerHelper.getProperties(vertex).getOrDefault(key, Collections.emptyList()) : vertexProperty; - //return isComputeKey(key) ? this.getValue(vertex, key) : (List) TinkerHelper.getProperties(vertex).getOrDefault(key, Collections.emptyList()); + return vertexProperty.isEmpty() ? (List) getPropertiesMap(vertex).getOrDefault(key, Collections.emptyList()) : vertexProperty; } public List<Property> getProperties(final TinkerVertex vertex) { final List<Property> list = new ArrayList<>(); - for (final List<VertexProperty> properties : TinkerHelper.getProperties(vertex).values()) { + for (final List<VertexProperty> properties : getPropertiesMap(vertex).values()) { list.addAll(properties); } for (final List<VertexProperty<?>> properties : this.computeProperties.getOrDefault(vertex, Collections.emptyMap()).values()) { @@ -112,6 +121,14 @@ public final class TinkerGraphComputerView { return list; } + private Map<String, List<VertexProperty>> getPropertiesMap(final TinkerVertex vertex) { + Map<String, List<VertexProperty>> propertiesMap = TinkerHelper.getProperties(vertex); + if (retainVertexProperties != null) { + propertiesMap.keySet().retainAll(retainVertexProperties); + } + return propertiesMap; + } + public void removeProperty(final TinkerVertex vertex, final String key, final VertexProperty property) { if (isComputeKey(key)) { this.removeValue(vertex, key, property);
