You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2016/05/12 12:44:56 UTC

[4/6] incubator-tinkerpop git commit: lots of work on GraphFilterStrategy and GraphFilter. I made GraphFilter less stringent. I added GraphFilter.getLegallyPositiveEdgeLabels() to help providers know generally which labels to access. I added a bunch more

lots of work on GraphFilterStrategy and GraphFilter. I made GraphFilter less stringent. I added GraphFilter.getLegallyPositiveEdgeLabels() to help providers know generally which labels to access. I added a bunch more tests to GraphFilterTest. GraphFilterStrategy is a GraphComputer traversal strategy that will infer a graphFilter for the traversal if one is not provided. Currently it only works for edge filtering as whether to do it for vertices or not is questionable given that vertex filtering semantically should do edge filtering (but doesn't as GraphFilter a rough cut optimization).


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/607b9610
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/607b9610
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/607b9610

Branch: refs/heads/master
Commit: 607b9610bc873e6ef55b738fd04de17c9762baf0
Parents: b123cbe
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Tue May 10 13:12:35 2016 -0600
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Tue May 10 13:12:35 2016 -0600

----------------------------------------------------------------------
 .../gremlin/process/computer/GraphFilter.java   | 208 ++++++++++++--
 .../optimization/GraphFilterStrategy.java       |  55 ++--
 .../process/computer/GraphFilterTest.java       | 268 ++++++++++++++++++-
 .../AbstractImportCustomizerProvider.java       |   6 +-
 .../computer/GroovyGraphFilterTest.groovy       |  98 -------
 .../process/computer/TinkerGraphComputer.java   |   8 +
 6 files changed, 482 insertions(+), 161 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/607b9610/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilter.java
----------------------------------------------------------------------
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 c8ce53b..59c654d 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
@@ -20,6 +20,7 @@
 package org.apache.tinkerpop.gremlin.process.computer;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
@@ -29,10 +30,12 @@ import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 
 import java.io.Serializable;
-import java.util.Arrays;
-import java.util.HashSet;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * GraphFilter is used by {@link GraphComputer} implementations to prune the source graph data being loaded into the OLAP system.
@@ -47,13 +50,29 @@ import java.util.Set;
  */
 public final class GraphFilter implements Cloneable, Serializable {
 
+    /**
+     * A enum denoting whether a particular result will be allowed or not.
+     * {@link Legal#YES} means that the specified element set will definitely not be removed by {@link GraphFilter}.
+     * {@link Legal#MAYBE} means that the element set may or may not be filtered out by the {@link GraphFilter}.
+     * {@link Legal#NO} means that the specified element set will definitely be removed by {@link GraphFilter}.
+     */
     public enum Legal {
-        YES, NO, MAYBE;
+        YES, MAYBE, NO;
 
+        /**
+         * The enum is either {@link Legal#YES} or {@link Legal#MAYBE}.
+         *
+         * @return true if potentially legal.
+         */
         public boolean positive() {
             return this != NO;
         }
 
+        /**
+         * The enum is {@link Legal#NO}.
+         *
+         * @return true if definitely not legal.
+         */
         public boolean negative() {
             return this == NO;
         }
@@ -61,91 +80,222 @@ public final class GraphFilter implements Cloneable, Serializable {
 
     private Traversal.Admin<Vertex, Vertex> vertexFilter = null;
     private Traversal.Admin<Vertex, Edge> edgeFilter = null;
-
+    private Map<Direction, Map<String, Legal>> edgeLegality = new HashMap<>();
     private boolean allowNoEdges = false;
-    private Direction allowedEdgeDirection = Direction.BOTH;
-    private Set<String> allowedEdgeLabels = new HashSet<>();
-    //private boolean allowAllRemainingEdges = false;
 
+    /**
+     * Set the filter for selecting vertices from the source graph.
+     * The vertex filter can only access the vertex, its properties, and its properties properties.
+     * Vertex filters can not access the incident edges of the vertex.
+     *
+     * @param vertexFilter The {@link Traversal} that will either let the vertex pass or not.
+     */
     public void setVertexFilter(final Traversal<Vertex, Vertex> vertexFilter) {
         if (!TraversalHelper.isLocalProperties(vertexFilter.asAdmin()))
             throw GraphComputer.Exceptions.vertexFilterAccessesIncidentEdges(vertexFilter);
         this.vertexFilter = vertexFilter.asAdmin().clone();
     }
 
+    /**
+     * Set the filter for selecting edges from the source graph.
+     * The edge filter can only access the local star graph (not adjacent vertices).
+     *
+     * @param edgeFilter The {@link Traversal} that will generate the legal incident edges of the vertex.
+     */
     public void setEdgeFilter(final Traversal<Vertex, Edge> edgeFilter) {
         if (!TraversalHelper.isLocalStarGraph(edgeFilter.asAdmin()))
             throw GraphComputer.Exceptions.edgeFilterAccessesAdjacentVertices(edgeFilter);
         this.edgeFilter = edgeFilter.asAdmin().clone();
+        ////
+        this.edgeLegality = new HashMap<>();
+        this.edgeLegality.put(Direction.OUT, new HashMap<>());
+        this.edgeLegality.put(Direction.IN, new HashMap<>());
+        this.edgeLegality.put(Direction.BOTH, new HashMap<>());
         if (this.edgeFilter.getEndStep() instanceof RangeGlobalStep && 0 == ((RangeGlobalStep) this.edgeFilter.getEndStep()).getHighRange())
             this.allowNoEdges = true;
-        else if (this.edgeFilter.getStartStep() instanceof VertexStep) {
-            this.allowedEdgeLabels.clear();
-            this.allowedEdgeLabels.addAll(Arrays.asList(((VertexStep) this.edgeFilter.getStartStep()).getEdgeLabels()));
-            this.allowedEdgeDirection = ((VertexStep) this.edgeFilter.getStartStep()).getDirection();
-            //this.allowAllRemainingEdges = 1 == this.edgeFilter.getSteps().size();
+        ////
+        if (this.edgeFilter.getStartStep() instanceof VertexStep) {
+            final VertexStep step = (VertexStep) this.edgeFilter.getStartStep();
+            final Map<String, Legal> map = this.edgeLegality.get(step.getDirection());
+            if (step.returnsEdge()) {
+                if (step.getEdgeLabels().length == 0)
+                    map.put(null, 1 == this.edgeFilter.getSteps().size() ? Legal.YES : Legal.MAYBE);
+                else {
+                    for (final String label : step.getEdgeLabels()) {
+                        map.put(label, 1 == this.edgeFilter.getSteps().size() ? Legal.YES : Legal.MAYBE);
+                    }
+                }
+            }
+        } else if (this.edgeFilter.getStartStep() instanceof UnionStep) {
+            final UnionStep<?, ?> step = (UnionStep) this.edgeFilter.getStartStep();
+            for (final Traversal.Admin<?, ?> union : step.getGlobalChildren()) {
+                if (union.getStartStep() instanceof VertexStep) {
+                    final VertexStep vertexStep = (VertexStep) union.getStartStep();
+                    final Map<String, Legal> map = this.edgeLegality.get(vertexStep.getDirection());
+                    if (vertexStep.returnsEdge()) {
+                        if (vertexStep.getEdgeLabels().length == 0)
+                            map.put(null, 2 == union.getSteps().size() ? Legal.YES : Legal.MAYBE);
+                        else {
+                            for (final String label : vertexStep.getEdgeLabels()) {
+                                map.put(label, 2 == union.getSteps().size() ? Legal.YES : Legal.MAYBE);
+                            }
+                        }
+
+                    }
+                }
+            }
+        }
+        final Map<String, Legal> outMap = this.edgeLegality.get(Direction.OUT);
+        final Map<String, Legal> inMap = this.edgeLegality.get(Direction.IN);
+        final Map<String, Legal> bothMap = this.edgeLegality.get(Direction.BOTH);
+        for (final Map.Entry<String, Legal> entry : bothMap.entrySet()) {
+            final Legal legal = inMap.get(entry.getKey());
+            if (null == legal || legal.compareTo(entry.getValue()) > 0)
+                inMap.put(entry.getKey(), entry.getValue());
+        }
+        for (final Map.Entry<String, Legal> entry : bothMap.entrySet()) {
+            final Legal legal = outMap.get(entry.getKey());
+            if (null == legal || legal.compareTo(entry.getValue()) > 0)
+                outMap.put(entry.getKey(), entry.getValue());
+        }
+        for (final Map.Entry<String, Legal> entry : outMap.entrySet()) {
+            final Legal legal = inMap.get(entry.getKey());
+            if (null != legal)
+                bothMap.put(entry.getKey(), legal.compareTo(entry.getValue()) > 0 ? legal : entry.getValue());
+        }
+        if (outMap.isEmpty() && inMap.isEmpty() && bothMap.isEmpty()) { // the edge filter could not be reasoned on
+            outMap.put(null, Legal.MAYBE);
+            inMap.put(null, Legal.MAYBE);
+            bothMap.put(null, Legal.MAYBE);
         }
     }
 
-    /*public void compileFilters() {
-        if (null != this.vertexFilter && !this.vertexFilter.isLocked())
-            this.vertexFilter.applyStrategies();
-        if (null != this.edgeFilter && !this.edgeFilter.isLocked())
-            this.edgeFilter.applyStrategies();
-    }*/
-
+    /**
+     * Returns true if the provided vertex meets the vertex-filter criteria.
+     * If no vertex filter is provided, then the vertex is considered legal.
+     *
+     * @param vertex the vertex to test for legality
+     * @return whether the vertex is {@link Legal#YES}.
+     */
     public boolean legalVertex(final Vertex vertex) {
         return null == this.vertexFilter || TraversalUtil.test(vertex, this.vertexFilter);
     }
 
+    /**
+     * Returns an iterator of legal edges incident to the provided vertex.
+     * If no edge filter is provided, then all incident edges are returned.
+     *
+     * @param vertex the vertex whose legal edges are to be access.
+     * @return an iterator of edges that are {@link Legal#YES}.
+     */
     public Iterator<Edge> legalEdges(final Vertex vertex) {
         return null == this.edgeFilter ?
                 vertex.edges(Direction.BOTH) :
                 TraversalUtil.applyAll(vertex, this.edgeFilter);
     }
 
+    /**
+     * Get the vertex filter associated with this graph filter.
+     *
+     * @return the vertex filter or null if no vertex filter was provided.
+     */
     public final Traversal.Admin<Vertex, Vertex> getVertexFilter() {
         return this.vertexFilter;
     }
 
+    /**
+     * Get the edge filter associated with this graph filter.
+     *
+     * @return the edge filter or null if no edge filter was provided.
+     */
     public final Traversal.Admin<Vertex, Edge> getEdgeFilter() {
         return this.edgeFilter;
     }
 
+    /**
+     * Whether filters have been defined.
+     *
+     * @return true if either a vertex or edge filter has been provided.
+     */
     public boolean hasFilter() {
         return this.vertexFilter != null || this.edgeFilter != null;
     }
 
+    /**
+     * Whether an edge filter has been defined.
+     *
+     * @return true if an edge filter was provided.
+     */
     public boolean hasEdgeFilter() {
         return this.edgeFilter != null;
     }
 
+    /**
+     * Whether a vertex filter has been defined.
+     *
+     * @return true if a vertex filter was provided.
+     */
     public boolean hasVertexFilter() {
         return this.vertexFilter != 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.
+     *
+     * @param direction the direction to get the positively legal edge labels for.
+     * @return the set of positively legal edge labels for the direction.
+     */
+    public Set<String> getLegallyPositiveEdgeLabels(final Direction direction) {
+        if (null == this.edgeFilter)
+            return Collections.singleton(null);
+        else if (this.allowNoEdges)
+            return Collections.emptySet();
+        else
+            return this.edgeLegality.get(direction).containsKey(null) ?
+                    Collections.singleton(null) :
+                    this.edgeLegality.get(direction).entrySet()
+                            .stream()
+                            .filter(entry -> entry.getValue().positive())
+                            .map(Map.Entry::getKey)
+                            .collect(Collectors.toSet());
+    }
+
+    /**
+     * Get the legality of a particular edge direction and label.
+     *
+     * @param direction the direction of the edge.
+     * @param label     the label of the edge.
+     * @return the {@link Legal} of the arguments.
+     */
     public Legal checkEdgeLegality(final Direction direction, final String label) {
         if (null == this.edgeFilter)
             return Legal.YES;
-        else if (this.allowNoEdges)
-            return Legal.NO;
-        else if (!this.allowedEdgeDirection.equals(Direction.BOTH) && !this.allowedEdgeDirection.equals(direction))
-            return Legal.NO;
-        else if (!this.allowedEdgeLabels.isEmpty() && !this.allowedEdgeLabels.contains(label))
+        if (this.checkEdgeLegality(direction).negative())
             return Legal.NO;
+        final Map<String, Legal> legalMap = this.edgeLegality.get(direction);
+        if (legalMap.containsKey(label))
+            return legalMap.get(label);
+        else if (legalMap.containsKey(null))
+            return legalMap.get(null);
         else
-            return Legal.MAYBE;
+            return Legal.NO;
     }
 
+    /**
+     * Get the legality of a particular edge direction.
+     *
+     * @param direction the direction of the edge.
+     * @return the {@link Legal} of the edge direction.
+     */
     public Legal checkEdgeLegality(final Direction direction) {
         if (null == this.edgeFilter)
             return Legal.YES;
         else if (this.allowNoEdges)
             return Legal.NO;
-        else if (!this.allowedEdgeDirection.equals(Direction.BOTH) && !this.allowedEdgeDirection.equals(direction))
-            return Legal.NO;
-        else
-            return Legal.MAYBE;
+        return this.edgeLegality.get(direction).values()
+                .stream()
+                .reduce(Legal.NO, (a, b) -> a.compareTo(b) < 0 ? a : b);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/607b9610/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/optimization/GraphFilterStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/optimization/GraphFilterStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/optimization/GraphFilterStrategy.java
index 4ccf476..90819d7 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/optimization/GraphFilterStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/optimization/GraphFilterStrategy.java
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.VertexPr
 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.LambdaHolder;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
@@ -54,17 +55,17 @@ public final class GraphFilterStrategy extends AbstractTraversalStrategy<Travers
 
     @Override
     public void apply(final Traversal.Admin<?, ?> traversal) {
-        if (TraversalHelper.getStepsOfAssignableClass(VertexProgramStep.class, traversal).size() > 1)
+        if (TraversalHelper.getStepsOfAssignableClass(VertexProgramStep.class, traversal).size() > 1)  // do not do if there is an OLAP chain
             return;
-        final Graph graph = traversal.getGraph().orElse(EmptyGraph.instance()); // best guess at what the graph will be as its dynamically determined
-        for (final TraversalVertexProgramStep step : TraversalHelper.getStepsOfClass(TraversalVertexProgramStep.class, traversal)) {
+        final Graph graph = traversal.getGraph().orElse(EmptyGraph.instance()); // given that this strategy only works for single OLAP jobs, the graph is the traversal graph
+        for (final TraversalVertexProgramStep step : TraversalHelper.getStepsOfClass(TraversalVertexProgramStep.class, traversal)) {   // will be zero or one step
             final Traversal.Admin<?, ?> computerTraversal = step.generateProgram(graph).getTraversal().get().clone();
             if (!computerTraversal.isLocked())
                 computerTraversal.applyStrategies();
             final Computer computer = step.getComputer();
-            if (null == computer.getEdges() && !GraphComputer.Persist.EDGES.equals(computer.getPersist())) {
+            if (null == computer.getEdges() && !GraphComputer.Persist.EDGES.equals(computer.getPersist())) {  // if edges() already set, use it
                 final Traversal.Admin<Vertex, Edge> edgeFilter = getEdgeFilter(computerTraversal);
-                if (null != edgeFilter)
+                if (null != edgeFilter)  // if no edges can be filtered, then don't set edges()
                     step.setComputer(computer.edges(edgeFilter));
             }
         }
@@ -72,7 +73,9 @@ public final class GraphFilterStrategy extends AbstractTraversalStrategy<Travers
 
     protected static Traversal.Admin<Vertex, Edge> getEdgeFilter(final Traversal.Admin<?, ?> traversal) {
         if (traversal.getStartStep() instanceof GraphStep && ((GraphStep) traversal.getStartStep()).returnsEdge())
-            return null;
+            return null; // if the traversal is an edge traversal, don't filter (this can be made less stringent)
+        if (TraversalHelper.hasStepOfAssignableClassRecursively(LambdaHolder.class, traversal))
+            return null; // if the traversal contains lambdas, don't filter as you don't know what is being accessed by the lambdas
         final Map<Direction, Set<String>> directionLabels = new HashMap<>();
         final Set<String> outLabels = new HashSet<>();
         final Set<String> inLabels = new HashSet<>();
@@ -81,57 +84,51 @@ public final class GraphFilterStrategy extends AbstractTraversalStrategy<Travers
         directionLabels.put(Direction.IN, inLabels);
         directionLabels.put(Direction.BOTH, bothLabels);
         TraversalHelper.getStepsOfAssignableClassRecursively(VertexStep.class, traversal).forEach(step -> {
-            // in-edge traversals require the outgoing edges for attachement
+            // in-edge traversals require the outgoing edges for attachment
             final Direction direction = step.getDirection().equals(Direction.IN) && step.returnsEdge() ?
                     Direction.BOTH :
                     step.getDirection();
             final String[] edgeLabels = step.getEdgeLabels();
-            Set<String> temp = directionLabels.get(direction);
             if (edgeLabels.length == 0)
-                temp.add(null);
+                directionLabels.get(direction).add(null); // null means all edges (don't filter)
             else
-                Collections.addAll(temp, edgeLabels);
+                Collections.addAll(directionLabels.get(direction), edgeLabels); // add edge labels associated with that direction
         });
-        for (final String label : outLabels) {
+        for (final String label : outLabels) { // if both in and out share the same labels, add them to both
             if (inLabels.contains(label)) {
-                if (null == label)
-                    bothLabels.clear();
-                if (!bothLabels.contains(null))
-                    bothLabels.add(label);
+                bothLabels.add(label);
             }
         }
-        if (bothLabels.contains(null)) {
-            outLabels.clear();
-            inLabels.clear();
-        }
-        for (final String label : bothLabels) {
+        if (bothLabels.contains(null)) // if both on everything, you can't edges() filter
+            return null;
+
+        for (final String label : bothLabels) { // remove labels from out and in that are already handled by both
             outLabels.remove(label);
             inLabels.remove(label);
         }
         // construct edges(...)
-        if (bothLabels.contains(null))
-            return null;
-        else if (outLabels.isEmpty() && inLabels.isEmpty() && bothLabels.isEmpty())
+        if (outLabels.isEmpty() && inLabels.isEmpty() && bothLabels.isEmpty())  // out/in/both are never called, thus, filter all edges
             return __.<Vertex>bothE().limit(0).asAdmin();
         else {
             final String[] ins = inLabels.contains(null) ? new String[]{} : inLabels.toArray(new String[inLabels.size()]);
             final String[] outs = outLabels.contains(null) ? new String[]{} : outLabels.toArray(new String[outLabels.size()]);
             final String[] boths = bothLabels.contains(null) ? new String[]{} : bothLabels.toArray(new String[bothLabels.size()]);
 
-            if (outLabels.isEmpty() && inLabels.isEmpty())
+            if (outLabels.isEmpty() && inLabels.isEmpty()) // only both has labels
                 return __.<Vertex>bothE(boths).asAdmin();
-            else if (inLabels.isEmpty() && bothLabels.isEmpty())
+            else if (inLabels.isEmpty() && bothLabels.isEmpty()) // only out has labels
                 return __.<Vertex>outE(outs).asAdmin();
-            else if (outLabels.isEmpty() && bothLabels.isEmpty())
+            else if (outLabels.isEmpty() && bothLabels.isEmpty()) // only in has labels
                 return __.<Vertex>inE(ins).asAdmin();
-            else if (bothLabels.isEmpty())
+            else if (bothLabels.isEmpty())                        // out and in both have labels
                 return __.<Vertex, Edge>union(__.inE(ins), __.outE(outs)).asAdmin();
-            else if (outLabels.isEmpty() && ins.length > 0)
+            else if (outLabels.isEmpty() && ins.length > 0)       // in and both have labels (and in is not null)
                 return __.<Vertex, Edge>union(__.inE(ins), __.bothE(boths)).asAdmin();
-            else if (inLabels.isEmpty() && outs.length > 0)
+            else if (inLabels.isEmpty() && outs.length > 0)       // out and both have labels (and out is not null)
                 return __.<Vertex, Edge>union(__.outE(outs), __.bothE(boths)).asAdmin();
             else
                 return null;
+            //throw new IllegalStateException("The label combination should not have reached this point: " + outLabels + "::" + inLabels + "::" + bothLabels);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/607b9610/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilterTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilterTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilterTest.java
index c73324b..0420f60 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilterTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/computer/GraphFilterTest.java
@@ -24,9 +24,14 @@ import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.junit.Test;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -34,12 +39,166 @@ import static org.junit.Assert.assertTrue;
 public class GraphFilterTest {
 
     @Test
+    public void shouldHaveValidLegalEnumOrdering() {
+        assertTrue(GraphFilter.Legal.YES.compareTo(GraphFilter.Legal.YES) == 0);
+        assertTrue(GraphFilter.Legal.YES.compareTo(GraphFilter.Legal.NO) < 0);
+        assertTrue(GraphFilter.Legal.YES.compareTo(GraphFilter.Legal.MAYBE) < 0);
+        assertTrue(GraphFilter.Legal.MAYBE.compareTo(GraphFilter.Legal.NO) < 0);
+    }
+
+    @Test
+    public void shouldOnlyAllowEdgeFilterToTraverseLocalStarGraph() {
+        GraphFilter graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.outE());
+        try {
+            graphFilter.setEdgeFilter(__.<Vertex>outE().inV().outE());
+            fail("Should not allow traversals past the star graph");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(e.getMessage(), GraphComputer.Exceptions.edgeFilterAccessesAdjacentVertices(__.<Vertex>outE().inV().outE()).getMessage());
+        }
+    }
+
+    @Test
+    public void shouldOnlyAllowVertexFilterToTraverseVertex() {
+        GraphFilter graphFilter = new GraphFilter();
+        graphFilter.setVertexFilter(__.hasLabel("person"));
+        try {
+            graphFilter.setVertexFilter(__.<Vertex>as("a").outE().<Vertex>select("a"));
+            fail("Should not allow traversals to the incident edges");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(e.getMessage(), GraphComputer.Exceptions.vertexFilterAccessesIncidentEdges(__.<Vertex>as("a").outE().<Vertex>select("a")).getMessage());
+        }
+    }
+
+    @Test
+    public void shouldGetLegallyPositiveEdgeLabels() {
+        GraphFilter graphFilter = new GraphFilter();
+        assertFalse(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.outE("created"));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton("created"), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.outE());
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>outE("created").has("weight", 32));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton("created"), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>identity().outE("created"));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.bothE());
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>bothE().has("weight", 32));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.singleton(null), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>bothE().limit(0));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>bothE("created").has("weight", 32));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton("created"), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.singleton("created"), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.singleton("created"), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.union(__.outE("created"), __.inE("likes")));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton("created"), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(Collections.singleton("likes"), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.emptySet(), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.union(__.outE("created"), __.inE("likes", "created")));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(Collections.singleton("created"), graphFilter.getLegallyPositiveEdgeLabels(Direction.OUT));
+        assertEquals(new HashSet<>(Arrays.asList("likes", "created")), graphFilter.getLegallyPositiveEdgeLabels(Direction.IN));
+        assertEquals(Collections.singleton("created"), graphFilter.getLegallyPositiveEdgeLabels(Direction.BOTH));
+    }
+
+    @Test
     public void shouldHaveProperEdgeLegality() {
         GraphFilter graphFilter = new GraphFilter();
         assertFalse(graphFilter.hasEdgeFilter());
         assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN));
         assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.BOTH));
         assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.BOTH, "likes"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.flatMap(v -> v.get().edges(Direction.OUT, "created")));  // lambdas can not be introspected
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH, "likes"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>identity().bothE());  // there are no strategies for __.
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH, "likes"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>bothE().has("weight", 32));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH, "likes"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>inE().has("weight", 32));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH, "likes"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
         //
         graphFilter = new GraphFilter();
         graphFilter.setEdgeFilter(__.<Vertex>bothE().limit(0));
@@ -53,18 +212,119 @@ public class GraphFilterTest {
         assertTrue(graphFilter.hasEdgeFilter());
         assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.IN));
         assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>outE().limit(10));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH));
         assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT));
         assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
         //
         graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.<Vertex>outE("knows").limit(10));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "created"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.bothE("created"));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.BOTH, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH, "knows"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.outE("knows", "likes"));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH, "likes"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.BOTH, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "knows"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "likes"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "created"));
+        //
+        graphFilter = new GraphFilter();
         graphFilter.setEdgeFilter(__.union(__.inE("bought"), __.outE("created"), __.bothE("knows", "likes")));
         assertTrue(graphFilter.hasEdgeFilter());
-        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN));
-        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "likes"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "worksFor"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.union(__.outE("created"), __.bothE("knows", "likes")));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "likes"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "likes"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "worksFor"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.union(__.inE("bought").has("weight", 32), __.outE("created"), __.bothE("knows", "likes")));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "likes"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN, "bought"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "worksFor"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "likes"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "knows"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.union(__.inE("bought").has("weight", 32), __.outE("created"), __.bothE("knows", "likes", "bought")));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "likes"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "blah"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "bought"));
+        assertEquals(GraphFilter.Legal.NO, graphFilter.checkEdgeLegality(Direction.OUT, "worksFor"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "likes"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "knows"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.union(__.outE("created").limit(0), __.inE("created")));
+        assertTrue(graphFilter.hasEdgeFilter());
         assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH));
         assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT, "created"));
-        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT, "likes"));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.IN, "created"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH, "created"));
+        //
+        graphFilter = new GraphFilter();
+        graphFilter.setEdgeFilter(__.union(__.outE(), __.inE().limit(0)));
+        assertTrue(graphFilter.hasEdgeFilter());
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH));
+        assertEquals(GraphFilter.Legal.YES, graphFilter.checkEdgeLegality(Direction.OUT, "created"));
         assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.IN, "created"));
-        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.OUT, "worksFor"));
+        assertEquals(GraphFilter.Legal.MAYBE, graphFilter.checkEdgeLegality(Direction.BOTH, "created"));
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/607b9610/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/AbstractImportCustomizerProvider.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/AbstractImportCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/AbstractImportCustomizerProvider.java
index a266e4f..a6d3609 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/AbstractImportCustomizerProvider.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/AbstractImportCustomizerProvider.java
@@ -23,12 +23,15 @@ import groovy.json.JsonBuilder;
 import org.apache.commons.configuration.Configuration;
 import org.apache.tinkerpop.gremlin.groovy.function.GFunction;
 import org.apache.tinkerpop.gremlin.groovy.loaders.GremlinLoader;
+import org.apache.tinkerpop.gremlin.process.computer.Computer;
 import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
 import org.apache.tinkerpop.gremlin.process.computer.bulkdumping.BulkDumperVertexProgram;
 import org.apache.tinkerpop.gremlin.process.computer.bulkloading.BulkLoaderVertexProgram;
 import org.apache.tinkerpop.gremlin.process.computer.clustering.peerpressure.PeerPressureVertexProgram;
 import org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgram;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy;
 import org.apache.tinkerpop.gremlin.process.remote.RemoteGraph;
 import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
@@ -62,7 +65,6 @@ import org.apache.tinkerpop.gremlin.structure.util.GraphFactory;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedElement;
 import org.apache.tinkerpop.gremlin.util.Gremlin;
 import org.apache.tinkerpop.gremlin.util.TimeUtil;
-import org.apache.tinkerpop.gremlin.process.computer.Computer;
 import org.apache.tinkerpop.gremlin.util.function.FunctionUtils;
 import org.codehaus.groovy.control.customizers.CompilationCustomizer;
 import org.codehaus.groovy.control.customizers.ImportCustomizer;
@@ -103,6 +105,8 @@ public abstract class AbstractImportCustomizerProvider implements ImportCustomiz
         imports.add(IdentityRemovalStrategy.class.getPackage().getName() + DOT_STAR); // optimization strategies
         imports.add(ProfileStrategy.class.getPackage().getName() + DOT_STAR);         // finalization strategies
         imports.add(ReadOnlyStrategy.class.getPackage().getName() + DOT_STAR);        // verification strategies
+        imports.add(VertexProgramStrategy.class.getPackage().getName() + DOT_STAR);   // computer decoration strategies
+        imports.add(GraphFilterStrategy.class.getPackage().getName() + DOT_STAR);     // computer optimization strategies
         imports.add(Event.class.getPackage().getName() + DOT_STAR);                   // eventing
 
         staticImports.add(P.class.getCanonicalName() + DOT_STAR);

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/607b9610/gremlin-groovy/src/test/groovy/org/apache/tinkerpop/gremlin/process/computer/GroovyGraphFilterTest.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/test/groovy/org/apache/tinkerpop/gremlin/process/computer/GroovyGraphFilterTest.groovy b/gremlin-groovy/src/test/groovy/org/apache/tinkerpop/gremlin/process/computer/GroovyGraphFilterTest.groovy
deleted file mode 100644
index adad10c..0000000
--- a/gremlin-groovy/src/test/groovy/org/apache/tinkerpop/gremlin/process/computer/GroovyGraphFilterTest.groovy
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.computer
-
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__
-import org.apache.tinkerpop.gremlin.structure.Direction
-import org.apache.tinkerpop.gremlin.structure.Vertex
-import org.junit.Test
-
-import static org.junit.Assert.*
-
-/**
- * @author Marko A. Rodriguez (http://markorodriguez.com)
- */
-public class GroovyGraphFilterTest {
-
-    @Test
-    public void shouldHandlePreFilterCorrectly() {
-        GraphFilter graphFilter = new GraphFilter();
-        graphFilter.setEdgeFilter(__.<Vertex> bothE().limit(0));
-        assertTrue(graphFilter.allowNoEdges);
-        //
-        graphFilter = new GraphFilter();
-        graphFilter.setEdgeFilter(__.<Vertex> bothE("knows").limit(0));
-        assertTrue(graphFilter.allowNoEdges);
-        //
-        graphFilter = new GraphFilter();
-        graphFilter.setEdgeFilter(__.<Vertex> bothE("knows").select("a"));
-        assertEquals(1, graphFilter.allowedEdgeLabels.size());
-        assertTrue(graphFilter.allowedEdgeLabels.contains("knows"));
-        assertEquals(Direction.BOTH, graphFilter.allowedEdgeDirection);
-        //assertFalse(graphFilter.allowAllRemainingEdges);
-        assertFalse(graphFilter.allowNoEdges);
-        //
-        graphFilter = new GraphFilter();
-        graphFilter.setEdgeFilter(__.<Vertex> outE("knows", "created"));
-        assertEquals(2, graphFilter.allowedEdgeLabels.size());
-        assertTrue(graphFilter.allowedEdgeLabels.contains("knows"));
-        assertTrue(graphFilter.allowedEdgeLabels.contains("created"));
-        assertEquals(Direction.OUT, graphFilter.allowedEdgeDirection);
-        //assertTrue(graphFilter.allowAllRemainingEdges);
-        assertFalse(graphFilter.allowNoEdges);
-        //
-        graphFilter = new GraphFilter();
-        graphFilter.setEdgeFilter(__.<Vertex> inE("knows", "created", "likes"));
-        assertEquals(3, graphFilter.allowedEdgeLabels.size());
-        assertTrue(graphFilter.allowedEdgeLabels.contains("knows"));
-        assertTrue(graphFilter.allowedEdgeLabels.contains("created"));
-        assertTrue(graphFilter.allowedEdgeLabels.contains("likes"));
-        assertEquals(Direction.IN, graphFilter.allowedEdgeDirection);
-        //assertTrue(graphFilter.allowAllRemainingEdges);
-        assertFalse(graphFilter.allowNoEdges);
-        //
-        graphFilter = new GraphFilter();
-        graphFilter.setEdgeFilter(__.<Vertex> inE("knows", "created", "likes"));
-        assertEquals(3, graphFilter.allowedEdgeLabels.size());
-        assertTrue(graphFilter.allowedEdgeLabels.contains("knows"));
-        assertTrue(graphFilter.allowedEdgeLabels.contains("created"));
-        assertTrue(graphFilter.allowedEdgeLabels.contains("likes"));
-        assertEquals(Direction.IN, graphFilter.allowedEdgeDirection);
-        //assertTrue(graphFilter.allowAllRemainingEdges);
-        assertFalse(graphFilter.allowNoEdges);
-        //
-        graphFilter = new GraphFilter();
-        try {
-            graphFilter.setVertexFilter(__.out("likes"));    // cannot leave local vertex
-            fail();
-        } catch (final IllegalArgumentException e) {
-            assertEquals(e.getMessage(), GraphComputer.Exceptions.vertexFilterAccessesIncidentEdges(__.out("likes")).getMessage());
-        }
-        //
-        graphFilter = new GraphFilter();
-        try {
-            graphFilter.setEdgeFilter(__.<Vertex> inE("likes").inV().outE().has("weight", 1));
-            // cannot leave local star graph
-            fail();
-        } catch (final IllegalArgumentException e) {
-            assertEquals(e.getMessage(), GraphComputer.Exceptions.edgeFilterAccessesAdjacentVertices(__.<Vertex> inE("likes").inV().outE().has("weight", 1)).getMessage());
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/607b9610/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputer.java
----------------------------------------------------------------------
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 2628ff1..c333130 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
@@ -24,10 +24,12 @@ import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
 import org.apache.tinkerpop.gremlin.process.computer.GraphFilter;
 import org.apache.tinkerpop.gremlin.process.computer.MapReduce;
 import org.apache.tinkerpop.gremlin.process.computer.VertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy;
 import org.apache.tinkerpop.gremlin.process.computer.util.ComputerGraph;
 import org.apache.tinkerpop.gremlin.process.computer.util.DefaultComputerResult;
 import org.apache.tinkerpop.gremlin.process.computer.util.GraphComputerHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+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;
@@ -54,6 +56,12 @@ import java.util.concurrent.ThreadFactory;
  */
 public final class TinkerGraphComputer implements GraphComputer {
 
+    static {
+        // GraphFilters are expensive w/ TinkerGraphComputer as everything is already in memory
+        TraversalStrategies.GlobalCache.registerStrategies(TinkerGraphComputer.class,
+                TraversalStrategies.GlobalCache.getStrategies(GraphComputer.class).clone().removeStrategies(GraphFilterStrategy.class));
+    }
+
     private ResultGraph resultGraph = null;
     private Persist persist = null;