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 2015/11/17 19:34:56 UTC

incubator-tinkerpop git commit: Fixed TINKERPOP3-679. There is now Traversal.explain() which returns a TraversalExplanation that has a pretty-print toString() as well as accessor methods for programmatic introspection. Added ExplainTest (dont want to get

Repository: incubator-tinkerpop
Updated Branches:
  refs/heads/TINKERPOP3-679 [created] 87069b28f


Fixed TINKERPOP3-679. There is now Traversal.explain() which returns a TraversalExplanation that has a pretty-print toString() as well as accessor methods for programmatic introspection. Added ExplainTest (dont want to get too crazy here or else provider strategies may cause problems). Added ExplainStep to the docs and made it so both ProfileStep and ExplainStep sections reference each other. Fixed a minor bug in IncidentToAdacentStrategy and AdjacentToIndicentStrategy which was relaized when playing around with explain(). Tada.


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

Branch: refs/heads/TINKERPOP3-679
Commit: 87069b28f47a221f0d84efd675f431c144b13c32
Parents: b060c7d
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Tue Nov 17 11:34:42 2015 -0700
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Tue Nov 17 11:34:42 2015 -0700

----------------------------------------------------------------------
 docs/src/the-traversal.asciidoc                 |  20 ++++
 .../gremlin/process/traversal/Traversal.java    |  12 ++
 .../AdjacentToIncidentStrategy.java             |   5 +-
 .../IncidentToAdjacentStrategy.java             |   6 +
 .../traversal/util/TraversalExplanation.java    | 118 +++++++++++++++++++
 .../step/sideEffect/GroovyExplainTest.groovy    |  35 ++++++
 .../process/GroovyProcessComputerSuite.java     |   2 +
 .../process/GroovyProcessStandardSuite.java     |   2 +
 .../gremlin/process/ProcessComputerSuite.java   |   2 +
 .../gremlin/process/ProcessStandardSuite.java   |   2 +
 .../traversal/step/sideEffect/ExplainTest.java  |  83 +++++++++++++
 .../structure/TinkerGraphPlayTest.java          |   4 +-
 12 files changed, 286 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/docs/src/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/the-traversal.asciidoc b/docs/src/the-traversal.asciidoc
index 49d570f..d7134be 100644
--- a/docs/src/the-traversal.asciidoc
+++ b/docs/src/the-traversal.asciidoc
@@ -579,6 +579,24 @@ g.V().drop()
 g.V()
 ----
 
+[[explain-step]]
+Explain Step
+~~~~~~~~~~~~
+
+The `explain()`-step (*sideEffect*) will return a `TraversalExplanation`. A traversal explanation details how the
+traversal (prior to `explain()`) will be compiled given the registered <<traversalstrategy,traversal strategies>>.
+A `TraversalExplanation` has a `toString()` representation with 3-columns. The first column is the
+traversal strategy being applied. The second column is the traversal strategy category: [D]ecoration, [O]ptimization,
+[P]rovider optimization, [F]inalization, and [V]erification. Finally, the third column is the state of the traversal
+post strategy application. The final traversal is the resultant execution plan.
+
+[gremlin-groovy,modern]
+----
+g.V().outE().identity().inV().count().is(gt(5))
+----
+
+For traversal profiling information, please see <<profile-step,`profile()`>>-step.
+
 [[fold-step]]
 Fold Step
 ~~~~~~~~~
@@ -1282,6 +1300,8 @@ into a single traverser. That new traverser has a `Traverser.bulk()` that is the
 bulks. On the other hand, the `Count` represents the sum of all `Traverser.bulk()` results and thus, expresses the
 number of "represented" (not enumerated) traversers. `Traversers` will always be less than or equal to `Count`.
 
+For traversal compilation information, please see <<explain-step,`explain()`>>-step.
+
 [[range-step]]
 Range Step
 ~~~~~~~~~~

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
index 13a3c57..2c467cf 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 
@@ -171,6 +172,17 @@ public interface Traversal<S, E> extends Iterator<E>, Serializable, Cloneable {
     }
 
     /**
+     * Return a {@link TraversalExplanation} that shows how this traversal will mutate with each applied {@link TraversalStrategy}.
+     *
+     * @return a traversal explanation
+     */
+    public default TraversalExplanation explain() {
+        if (this.asAdmin().isLocked())
+            throw new IllegalStateException("The traversal is locked and can not be explained on a strategy-by-strategy basis");
+        return new TraversalExplanation(this.asAdmin());
+    }
+
+    /**
      * A traversal can be rewritten such that its defined end type E may yield objects of a different type.
      * This helper method allows for the casting of the output to the known the type.
      *

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
index 19757d3..aace11a 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
@@ -35,7 +35,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.PropertyType;
 
-import java.util.Collections;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -60,7 +61,7 @@ public final class AdjacentToIncidentStrategy extends AbstractTraversalStrategy<
         implements TraversalStrategy.OptimizationStrategy {
 
     private static final AdjacentToIncidentStrategy INSTANCE = new AdjacentToIncidentStrategy();
-    private static final Set<Class<? extends OptimizationStrategy>> PRIORS = Collections.singleton(IncidentToAdjacentStrategy.class);
+    private static final Set<Class<? extends OptimizationStrategy>> PRIORS = new HashSet<>(Arrays.asList(IdentityRemovalStrategy.class, IncidentToAdjacentStrategy.class));
 
     private AdjacentToIncidentStrategy() {
     }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
index de05aa9..15327d3 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
@@ -34,6 +34,7 @@ import org.javatuples.Pair;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -128,4 +129,9 @@ public final class IncidentToAdjacentStrategy extends AbstractTraversalStrategy<
     public static IncidentToAdjacentStrategy instance() {
         return INSTANCE;
     }
+
+    @Override
+    public Set<Class<? extends OptimizationStrategy>> applyPrior() {
+        return Collections.singleton(IdentityRemovalStrategy.class);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalExplanation.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalExplanation.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalExplanation.java
new file mode 100644
index 0000000..d7cdf58
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalExplanation.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.javatuples.Pair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Stream;
+
+/**
+ * A TraversalExplanation takes a {@link Traversal} and, for each registered {@link TraversalStrategy}, it creates a mapping reflecting how each strategy alters the traversal.
+ * This is useful for understanding how each traversal strategy mutates the traversal.
+ * This is useful in debugging and analysis of traversal compilation.
+ * The {@link TraversalExplanation#toString()} has a pretty-print representation that is useful in the Gremlin Console.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public class TraversalExplanation {
+
+    private final Traversal.Admin<?, ?> traversal;
+    private final List<Pair<TraversalStrategy, Traversal.Admin<?, ?>>> strategyTraversals = new ArrayList<>();
+
+    public TraversalExplanation(final Traversal.Admin<?, ?> traversal) {
+        this.traversal = traversal.clone();
+        Traversal.Admin<?, ?> mutatingTraversal = this.traversal.clone();
+        for (final TraversalStrategy strategy : this.traversal.getStrategies().toList()) {
+            strategy.apply(mutatingTraversal);
+            this.strategyTraversals.add(Pair.with(strategy, mutatingTraversal));
+            mutatingTraversal = mutatingTraversal.clone();
+        }
+    }
+
+    /**
+     * Get the list of {@link TraversalStrategy} applications. For strategy, the resultant mutated {@link Traversal} is provided.
+     *
+     * @return the list of strategy/traversal pairs
+     */
+    public List<Pair<TraversalStrategy, Traversal.Admin<?, ?>>> getStrategyTraversals() {
+        return Collections.unmodifiableList(this.strategyTraversals);
+    }
+
+    /**
+     * Get the original {@link Traversal} used to create this explanation.
+     *
+     * @return the original traversal
+     */
+    public Traversal.Admin<?, ?> getOriginalTraversal() {
+        return this.traversal;
+    }
+
+    /**
+     * A pretty-print representation of the traversal explanation.
+     *
+     * @return a {@link String} representation of the traversal explanation
+     */
+    @Override
+    public String toString() {
+        final String originalTraversal = "Original Traversal";
+        final String finalTraversal = "Final Traversal";
+        final int maxStrategyColumnLength = this.strategyTraversals.stream().map(Pair::getValue0).map(Object::toString).map(String::length).max(Comparator.<Integer>naturalOrder()).get();
+        final int maxTraversalColumnLength = Stream.concat(Stream.of(Pair.with(null, this.traversal)), this.strategyTraversals.stream()).map(Pair::getValue1).map(Object::toString).map(String::length).max(Comparator.<Integer>naturalOrder()).get();
+
+        final StringBuilder builder = new StringBuilder("Traversal Explanation\n");
+        for (int i = 0; i < (maxStrategyColumnLength + 7 + maxTraversalColumnLength); i++) {
+            builder.append("=");
+        }
+        builder.append("\n");
+        builder.append(originalTraversal);
+        for (int i = 0; i < maxStrategyColumnLength - originalTraversal.length() + 7; i++) {
+            builder.append(" ");
+        }
+        builder.append(this.traversal.toString());
+        builder.append("\n\n");
+        for (final Pair<TraversalStrategy, Traversal.Admin<?, ?>> pairs : this.strategyTraversals) {
+            builder.append(pairs.getValue0());
+            int spacesToAdd = maxStrategyColumnLength - pairs.getValue0().toString().length() + 1;
+            for (int i = 0; i < spacesToAdd; i++) {
+                builder.append(" ");
+            }
+            builder.append("[").append(pairs.getValue0().getTraversalCategory().getSimpleName().substring(0, 1)).append("]");
+            for (int i = 0; i < 3; i++) {
+                builder.append(" ");
+            }
+            builder.append(pairs.getValue1().toString()).append("\n");
+        }
+        builder.append("\n");
+        builder.append(finalTraversal);
+        for (int i = 0; i < maxStrategyColumnLength - finalTraversal.length() + 7; i++) {
+            builder.append(" ");
+        }
+        builder.append(this.strategyTraversals.get(this.strategyTraversals.size() - 1).getValue1());
+        builder.append("\n");
+        return builder.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyExplainTest.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyExplainTest.groovy b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyExplainTest.groovy
new file mode 100644
index 0000000..1c76546
--- /dev/null
+++ b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyExplainTest.groovy
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect
+
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public abstract class GroovyExplainTest {
+
+    public static class Traversals extends ExplainTest {
+
+        public TraversalExplanation get_g_V_outE_identity_inV_explain() {
+            g.V().outE().inV().explain();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessComputerSuite.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessComputerSuite.java b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessComputerSuite.java
index 2c1381c..9df7902 100644
--- a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessComputerSuite.java
+++ b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessComputerSuite.java
@@ -66,6 +66,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroovyUnfoldTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroovyValueMapTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroovyVertexTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyAggregateTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyExplainTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyGroupCountTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyGroupTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyGroupTestV3d0;
@@ -150,6 +151,7 @@ public class GroovyProcessComputerSuite extends ProcessComputerSuite {
             // sideEffect
             GroovyAddEdgeTest.Traversals.class,
             GroovyAggregateTest.Traversals.class,
+            GroovyExplainTest.Traversals.class,
             GroovyGroupTest.Traversals.class,
             GroovyGroupTestV3d0.Traversals.class,
             GroovyGroupCountTest.Traversals.class,

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessStandardSuite.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessStandardSuite.java b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessStandardSuite.java
index 1698eed..ae91b5f 100644
--- a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessStandardSuite.java
+++ b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/process/GroovyProcessStandardSuite.java
@@ -67,6 +67,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroovyUnfoldTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroovyValueMapTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroovyVertexTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyAggregateTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyExplainTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyGroupCountTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyGroupTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroovyGroupTestV3d0;
@@ -147,6 +148,7 @@ public class GroovyProcessStandardSuite extends ProcessStandardSuite {
             GroovyVertexTest.Traversals.class,
             // sideEffect
             GroovyAggregateTest.Traversals.class,
+            GroovyExplainTest.Traversals.class,
             GroovyGroupTest.Traversals.class,
             GroovyGroupTestV3d0.Traversals.class,
             GroovyGroupCountTest.Traversals.class,

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
index 3943b92..45ec0ef 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
@@ -66,6 +66,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.UnfoldTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.ValueMapTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AggregateTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.ExplainTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupCountTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTestV3d0;
@@ -156,6 +157,7 @@ public class ProcessComputerSuite extends AbstractGremlinSuite {
             // sideEffect
             AddEdgeTest.Traversals.class,
             AggregateTest.Traversals.class,
+            ExplainTest.Traversals.class,
             GroupTest.Traversals.class,
             GroupTestV3d0.Traversals.class,
             GroupCountTest.Traversals.class,

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
index 1091812..3b7e634 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
@@ -66,6 +66,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.UnfoldTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.ValueMapTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AggregateTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.ExplainTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupCountTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTestV3d0;
@@ -155,6 +156,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
 
             // sideEffect
             AggregateTest.Traversals.class,
+            ExplainTest.Traversals.class,
             GroupTest.Traversals.class,
             GroupTestV3d0.Traversals.class,
             GroupCountTest.Traversals.class,

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/ExplainTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/ExplainTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/ExplainTest.java
new file mode 100644
index 0000000..97a9a23
--- /dev/null
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/ExplainTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect;
+
+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.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeVertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IdentityRemovalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IncidentToAdjacentStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.javatuples.Pair;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+@RunWith(GremlinProcessRunner.class)
+public abstract class ExplainTest extends AbstractGremlinProcessTest {
+
+    public abstract TraversalExplanation get_g_V_outE_identity_inV_explain();
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_outE_identity_inV_explain() {
+        final TraversalExplanation explanation = get_g_V_outE_identity_inV_explain();
+        printTraversalForm(explanation.getOriginalTraversal());
+        boolean beforeIncident = true;
+        boolean beforeIdentity = true;
+        for (final Pair<TraversalStrategy, Traversal.Admin<?, ?>> pair : explanation.getStrategyTraversals()) {
+            if (pair.getValue0().getClass().equals(IncidentToAdjacentStrategy.class))
+                beforeIncident = false;
+            if (pair.getValue0().getClass().equals(IdentityRemovalStrategy.class))
+                beforeIdentity = false;
+
+            if (beforeIdentity)
+                assertEquals(1, TraversalHelper.getStepsOfClass(IdentityStep.class, pair.getValue1()).size());
+
+            if (beforeIncident)
+                assertEquals(1, TraversalHelper.getStepsOfClass(EdgeVertexStep.class, pair.getValue1()).size());
+
+            if (!beforeIdentity)
+                assertEquals(0, TraversalHelper.getStepsOfClass(IdentityStep.class, pair.getValue1()).size());
+
+            if (!beforeIncident)
+                assertEquals(0, TraversalHelper.getStepsOfClass(EdgeVertexStep.class, pair.getValue1()).size());
+        }
+        assertFalse(beforeIncident);
+    }
+
+    public static class Traversals extends ExplainTest {
+
+        public TraversalExplanation get_g_V_outE_identity_inV_explain() {
+            return g.V().outE().identity().inV().explain();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/87069b28/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
index e7c54ce..fdb44ad 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
@@ -53,9 +53,7 @@ public class TinkerGraphPlayTest {
     public void testPlay8() throws Exception {
         Graph graph = TinkerFactory.createModern();
         GraphTraversalSource g = graph.traversal(); //GraphTraversalSource.computer());
-        Traversal traversal = g.V(1).V().values("name");
-        traversal.forEachRemaining(System.out::println);
-        System.out.println(traversal);
+        System.out.println(g.V().outE("knows").identity().inV().count().is(P.eq(5)).explain());
 
     }