You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2021/12/03 22:03:47 UTC

[tinkerpop] branch TINKERPOP-2635 updated (373c5d0 -> 517dc06)

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a change to branch TINKERPOP-2635
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.


 discard 373c5d0  TINKERPOP-2635 Add fail() step
     add f6fd6a2  Regenerated python Gremlin corpus CTR
     add ddf1a50  Merge branch '3.5-dev'
     add d410ce6  Bumped gremlint nodejs version to match gremlin-javascript at v16 CTR
     new 2948ae6  TINKERPOP-2635 Add fail() step
     new 517dc06  TINKERPOP-2635 filtering for unproductive by() WIP

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (373c5d0)
            \
             N -- N -- N   refs/heads/TINKERPOP-2635 (517dc06)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.asciidoc                                 |     2 +
 bin/generate-home.sh                               |     1 +
 docs/gremlint/package-lock.json                    | 11104 ++++++++++++++-----
 docs/src/dev/developer/for-committers.asciidoc     |     3 +
 .../tinkerpop/gremlin/process/traversal/Path.java  |     6 +-
 .../tinkerpop/gremlin/process/traversal/Step.java  |     2 +-
 .../process/traversal/TraversalStrategies.java     |     2 -
 .../traversal/step/filter/DedupGlobalStep.java     |    72 +-
 .../traversal/step/filter/PathFilterStep.java      |    17 +-
 .../traversal/step/filter/SampleGlobalStep.java    |    20 +-
 .../traversal/step/filter/WherePredicateStep.java  |    34 +-
 .../process/traversal/step/map/GroupCountStep.java |     2 +-
 .../process/traversal/step/map/GroupStep.java      |    14 +-
 .../process/traversal/step/map/MathStep.java       |    29 +-
 .../traversal/step/map/OrderGlobalStep.java        |    15 +-
 .../process/traversal/step/map/OrderLocalStep.java |   104 +-
 .../process/traversal/step/map/PathStep.java       |    44 +-
 .../process/traversal/step/map/ProjectStep.java    |     2 +-
 .../traversal/step/map/PropertyMapStep.java        |    13 +-
 .../process/traversal/step/map/SelectOneStep.java  |     7 +-
 .../process/traversal/step/map/SelectStep.java     |    12 +-
 .../process/traversal/step/map/TreeStep.java       |    12 +-
 .../step/sideEffect/AggregateGlobalStep.java       |     8 +-
 .../step/sideEffect/AggregateLocalStep.java        |     2 +-
 .../traversal/step/sideEffect/SackValueStep.java   |    18 +-
 ...alObjectFunction.java => TraversalProduct.java} |    40 +-
 .../process/traversal/util/TraversalUtil.java      |   133 +-
 .../gremlin/util/function/ChainedComparator.java   |     2 +
 .../gremlin/process/traversal/PathTest.java        |    20 +
 .../optimization/PathProcessorStrategyTest.java    |    19 +-
 .../Gherkin/GherkinTestRunner.cs                   |     3 +-
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |    43 +
 .../test/cucumber/feature-steps.js                 |     2 +-
 .../gremlin-javascript/test/cucumber/gremlin.js    |    45 +-
 gremlin-python/src/main/python/radish/gremlin.py   |    45 +
 .../server/op/session/SessionOpProcessor.java      |     6 +-
 gremlin-test/features/filter/CyclicPath.feature    |    14 +
 gremlin-test/features/filter/Dedup.feature         |    49 +-
 gremlin-test/features/filter/Sample.feature        |    11 +
 gremlin-test/features/filter/SimplePath.feature    |    17 +
 gremlin-test/features/filter/Where.feature         |    13 +-
 gremlin-test/features/map/Math.feature             |    28 +
 gremlin-test/features/map/Max.feature              |    40 +
 gremlin-test/features/map/Mean.feature             |    44 +
 gremlin-test/features/map/Min.feature              |    40 +
 gremlin-test/features/map/Order.feature            |    76 +-
 gremlin-test/features/map/Path.feature             |    14 +
 gremlin-test/features/map/Project.feature          |    18 +
 gremlin-test/features/map/Select.feature           |    31 +-
 gremlin-test/features/map/Sum.feature              |    45 +-
 gremlin-test/features/map/ValueMap.feature         |    18 +-
 gremlin-test/features/sideEffect/Aggregate.feature |    73 +-
 gremlin-test/features/sideEffect/Group.feature     |    22 +
 .../features/sideEffect/GroupCount.feature         |    22 +
 gremlin-test/features/sideEffect/Sack.feature      |    14 +
 .../process/traversal/step/filter/DedupTest.java   |     3 +-
 .../process/traversal/step/filter/WhereTest.java   |     4 +-
 .../process/traversal/step/map/MaxTest.java        |     2 -
 .../process/traversal/step/map/MeanTest.java       |     2 -
 .../process/traversal/step/map/MinTest.java        |     2 -
 .../process/traversal/step/map/OrderTest.java      |     2 +-
 .../process/traversal/step/map/PathTest.java       |     4 +-
 .../process/traversal/step/map/ProjectTest.java    |    37 +-
 .../process/traversal/step/map/SelectTest.java     |     2 +-
 .../process/traversal/step/map/SumTest.java        |     4 +-
 .../traversal/step/sideEffect/AggregateTest.java   |     4 +-
 .../traversal/step/sideEffect/GroupTest.java       |     2 +-
 .../traversal/step/sideEffect/TreeTest.java        |    25 +
 gremlint/package-lock.json                         |  6120 +++++++++-
 gremlint/pom.xml                                   |     4 +-
 70 files changed, 15805 insertions(+), 2904 deletions(-)
 copy gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/{TraversalObjectFunction.java => TraversalProduct.java} (51%)

[tinkerpop] 02/02: TINKERPOP-2635 filtering for unproductive by() WIP

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2635
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 517dc064c9841bf743a75891511dd268506173bb
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Fri Dec 3 17:01:35 2021 -0500

    TINKERPOP-2635 filtering for unproductive by() WIP
---
 CHANGELOG.asciidoc                                 |   2 +
 docs/src/dev/developer/for-committers.asciidoc     |   3 +
 .../tinkerpop/gremlin/process/traversal/Path.java  |   6 +-
 .../tinkerpop/gremlin/process/traversal/Step.java  |   2 +-
 .../process/traversal/TraversalStrategies.java     |   2 -
 .../traversal/step/filter/DedupGlobalStep.java     |  72 ++++++-----
 .../traversal/step/filter/PathFilterStep.java      |  17 ++-
 .../traversal/step/filter/SampleGlobalStep.java    |  20 +++-
 .../traversal/step/filter/WherePredicateStep.java  |  34 ++++--
 .../process/traversal/step/map/GroupCountStep.java |   2 +-
 .../process/traversal/step/map/GroupStep.java      |  14 ++-
 .../process/traversal/step/map/MathStep.java       |  29 +++--
 .../traversal/step/map/OrderGlobalStep.java        |  15 ++-
 .../process/traversal/step/map/OrderLocalStep.java | 104 ++++++++++------
 .../process/traversal/step/map/PathStep.java       |  44 ++++---
 .../process/traversal/step/map/ProjectStep.java    |   2 +-
 .../traversal/step/map/PropertyMapStep.java        |  13 +-
 .../process/traversal/step/map/SelectOneStep.java  |   7 +-
 .../process/traversal/step/map/SelectStep.java     |  12 +-
 .../process/traversal/step/map/TreeStep.java       |  12 +-
 .../step/sideEffect/AggregateGlobalStep.java       |   8 +-
 .../step/sideEffect/AggregateLocalStep.java        |   2 +-
 .../traversal/step/sideEffect/SackValueStep.java   |  18 ++-
 .../process/traversal/util/TraversalProduct.java   |  55 +++++++++
 .../process/traversal/util/TraversalUtil.java      | 133 +++++++++++++--------
 .../gremlin/util/function/ChainedComparator.java   |   2 +
 .../gremlin/process/traversal/PathTest.java        |  20 ++++
 .../optimization/PathProcessorStrategyTest.java    |  19 +--
 .../Gherkin/GherkinTestRunner.cs                   |   3 +-
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  43 +++++++
 .../test/cucumber/feature-steps.js                 |   2 +-
 .../gremlin-javascript/test/cucumber/gremlin.js    |  45 ++++++-
 gremlin-python/src/main/python/radish/gremlin.py   |  45 +++++++
 .../server/op/session/SessionOpProcessor.java      |   6 +-
 gremlin-test/features/filter/CyclicPath.feature    |  14 +++
 gremlin-test/features/filter/Dedup.feature         |  49 +++++++-
 gremlin-test/features/filter/Sample.feature        |  11 ++
 gremlin-test/features/filter/SimplePath.feature    |  17 +++
 gremlin-test/features/filter/Where.feature         |  13 +-
 gremlin-test/features/map/Math.feature             |  28 +++++
 gremlin-test/features/map/Max.feature              |  40 +++++++
 gremlin-test/features/map/Mean.feature             |  44 +++++++
 gremlin-test/features/map/Min.feature              |  40 +++++++
 gremlin-test/features/map/Order.feature            |  76 +++++++++++-
 gremlin-test/features/map/Path.feature             |  14 +++
 gremlin-test/features/map/Project.feature          |  18 +++
 gremlin-test/features/map/Select.feature           |  31 ++++-
 gremlin-test/features/map/Sum.feature              |  45 ++++++-
 gremlin-test/features/map/ValueMap.feature         |  18 ++-
 gremlin-test/features/sideEffect/Aggregate.feature |  73 ++++++++++-
 gremlin-test/features/sideEffect/Group.feature     |  22 ++++
 .../features/sideEffect/GroupCount.feature         |  22 ++++
 gremlin-test/features/sideEffect/Sack.feature      |  14 +++
 .../process/traversal/step/filter/DedupTest.java   |   3 +-
 .../process/traversal/step/filter/WhereTest.java   |   4 +-
 .../process/traversal/step/map/MaxTest.java        |   2 -
 .../process/traversal/step/map/MeanTest.java       |   2 -
 .../process/traversal/step/map/MinTest.java        |   2 -
 .../process/traversal/step/map/OrderTest.java      |   2 +-
 .../process/traversal/step/map/PathTest.java       |   4 +-
 .../process/traversal/step/map/ProjectTest.java    |  37 ++++--
 .../process/traversal/step/map/SelectTest.java     |   2 +-
 .../process/traversal/step/map/SumTest.java        |   4 +-
 .../traversal/step/sideEffect/AggregateTest.java   |   4 +-
 .../traversal/step/sideEffect/GroupTest.java       |   2 +-
 .../traversal/step/sideEffect/TreeTest.java        |  25 ++++
 66 files changed, 1254 insertions(+), 241 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 703ba95..08275c2 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -39,6 +39,8 @@ limitations under the License.
 * Created a way to produce a corpus of Gremlin traversals via `FeatureReader` and `DocumentationReader` in `gremlin-language`.
 * Exposed Gherkin tests as part of the provider test suite.
 * Packaged Gherkin tests and data as standalone package as a convenience distribution.
+* Removed `ProductiveByStrategy` as a strategy that is applied by default.
+* Changed `by()` modulator semantics to consistently filter.
 * Removed previously deprecated `application/vnd.gremlin-v1.0+gryo-lite` serialization format.
 * Removed previously deprecated `AuthenticationSettings.enableAuditLog`.
 * Removed previously deprecated `GroovyTranslator` from `gremlin-groovy` module.
diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc
index 4114259..8a6c568 100644
--- a/docs/src/dev/developer/for-committers.asciidoc
+++ b/docs/src/dev/developer/for-committers.asciidoc
@@ -530,6 +530,9 @@ or meta-properties for creating or querying data.
 * `@RemoteOnly` - The scenario uses some Gremlin syntax that cannot be supported outside of remote test executions. The
 best example of this sort of test would be one that uses the remote `Lambda` syntax.
 
+Tag filters can be applied to Intellij at execution time by adding a system properties of
+`-Dcucumber.filter.tags=<step-filter>`.
+
 == Developing Benchmarks
 
 Benchmarks are a useful tool to track performance between TinkerPop versions and also as tools to aid development
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java
index d8e51c2..9d78f21 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Path.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.BiConsumer;
@@ -198,8 +199,7 @@ public interface Path extends Cloneable, Iterable<Object> {
     public Path clone();
 
     /**
-     * Determines whether the path is a simple or not.
-     * A simple path has no cycles and thus, no repeated objects.
+     * Determines whether the path is a simple or not. A simple path has no cycles and thus, no repeated objects.
      *
      * @return Whether the path is simple or not
      */
@@ -207,7 +207,7 @@ public interface Path extends Cloneable, Iterable<Object> {
         final List<Object> objects = this.objects();
         for (int i = 0; i < objects.size() - 1; i++) {
             for (int j = i + 1; j < objects.size(); j++) {
-                if (objects.get(i).equals(objects.get(j)))
+                if (Objects.equals(objects.get(i), objects.get(j)))
                     return false;
             }
         }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Step.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Step.java
index 6bc34f5..99fdd91 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Step.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Step.java
@@ -41,7 +41,7 @@ import java.util.Set;
 public interface Step<S, E> extends Iterator<Traverser.Admin<E>>, Serializable, Cloneable {
 
     /**
-     * Add a iterator of {@link Traverser.Admin} objects of type S to the step.
+     * Add an iterator of {@link Traverser.Admin} objects of type S to the step.
      *
      * @param starts The iterator of objects to add
      */
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
index 37ad108..01deea1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
@@ -23,7 +23,6 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.finaliza
 import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.MessagePassingReductionStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
-import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
@@ -225,7 +224,6 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T
                     IncidentToAdjacentStrategy.instance(),
                     AdjacentToIncidentStrategy.instance(),
                     ByModulatorOptimizationStrategy.instance(),
-                    ProductiveByStrategy.instance(),
                     FilterRankingStrategy.instance(),
                     MatchPredicateStrategy.instance(),
                     RepeatUnrollStrategy.instance(),
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java
index dd8235d..9f5b0e5 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java
@@ -31,6 +31,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.Property;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -73,11 +74,18 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal
         if (this.onGraphComputer && !this.executingAtMaster) return true;
         traverser.setBulk(1L);
         if (null == this.dedupLabels) {
-            return this.duplicateSet.add(TraversalUtil.applyNullable(traverser, this.dedupTraversal));
+            final TraversalProduct product = TraversalUtil.produce(traverser, this.dedupTraversal);
+            return product.isProductive() && this.duplicateSet.add(product.get());
         } else {
             final List<Object> objects = new ArrayList<>(this.dedupLabels.size());
-            this.dedupLabels.forEach(label -> objects.add(TraversalUtil.applyNullable((S) this.getSafeScopeValue(Pop.last, label, traverser), this.dedupTraversal)));
-            return this.duplicateSet.add(objects);
+            for (String label : dedupLabels) {
+                final TraversalProduct product = TraversalUtil.produce((S) this.getSafeScopeValue(Pop.last, label, traverser), this.dedupTraversal);
+                if (!product.isProductive()) break;
+                objects.add(product.get());
+            }
+
+            // the object sizes must be equal or else it means a by() wasn't productive and that path will be filtered
+            return objects.size() == dedupLabels.size() && duplicateSet.add(objects);
         }
     }
 
@@ -194,42 +202,50 @@ public final class DedupGlobalStep<S> extends FilterStep<S> implements Traversal
         while (this.starts.hasNext()) {
             final Traverser.Admin<S> traverser = this.starts.next();
             final Object object;
+            boolean productive;
             if (null != this.dedupLabels) {
                 object = new ArrayList<>(this.dedupLabels.size());
                 for (final String label : this.dedupLabels) {
-                    ((List) object).add(TraversalUtil.applyNullable((S) this.getSafeScopeValue(Pop.last, label, traverser), this.dedupTraversal));
+                    final TraversalProduct product = TraversalUtil.produce((S) this.getSafeScopeValue(Pop.last, label, traverser), this.dedupTraversal);
+                    if (!product.isProductive()) break;
+                    ((List) object).add(product.get());
                 }
+
+                productive = ((List) object).size() == this.dedupLabels.size();
             } else {
-                object = TraversalUtil.applyNullable(traverser, this.dedupTraversal);
+                final TraversalProduct product = TraversalUtil.produce(traverser, this.dedupTraversal);
+                productive = product.isProductive();
+                object = productive ? product.get() : null;
             }
-            if (!map.containsKey(object)) {
-                traverser.setBulk(1L);
-
-                // DetachedProperty and DetachedVertexProperty both have a transient for the Host element. that causes
-                // trouble for olap which ends up requiring the Host later. can't change the transient without some
-                // consequences: (1) we break gryo formatting and io tests start failing (2) storing the element with
-                // the property has the potential to bloat detached Element instances as it basically stores that data
-                // twice. Not sure if it's smart to change that at least in 3.4.x and not without some considerable
-                // thought as to what might be major changes. To work around the problem we will detach properties as
-                // references so that the parent element goes with it. Also, given TINKERPOP-2318 property comparisons
-                // have changed in such a way that allows this to work properly
-                if (this.onGraphComputer) {
-                    if (traverser.get() instanceof Property)
-                        traverser.set(ReferenceFactory.detach(traverser.get()));
-                    else
-                        traverser.set(DetachedFactory.detach(traverser.get(), true));
-                } else {
-                    traverser.set(traverser.get());
+
+            if (productive) {
+                if (!map.containsKey(object)) {
+                    traverser.setBulk(1L);
+
+                    // DetachedProperty and DetachedVertexProperty both have a transient for the Host element. that causes
+                    // trouble for olap which ends up requiring the Host later. can't change the transient without some
+                    // consequences: (1) we break gryo formatting and io tests start failing (2) storing the element with
+                    // the property has the potential to bloat detached Element instances as it basically stores that data
+                    // twice. Not sure if it's smart to change that at least in 3.4.x and not without some considerable
+                    // thought as to what might be major changes. To work around the problem we will detach properties as
+                    // references so that the parent element goes with it. Also, given TINKERPOP-2318 property comparisons
+                    // have changed in such a way that allows this to work properly
+                    if (this.onGraphComputer) {
+                        if (traverser.get() instanceof Property)
+                            traverser.set(ReferenceFactory.detach(traverser.get()));
+                        else
+                            traverser.set(DetachedFactory.detach(traverser.get(), true));
+                    } else {
+                        traverser.set(traverser.get());
+                    }
+                    map.put(object, traverser);
                 }
-                map.put(object, traverser);
             }
         }
+
         this.barrier = null;
         this.barrierIterator = null;
-        if (map.isEmpty())
-            throw FastNoSuchElementException.instance();
-        else
-            return map;
+        return map;
     }
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/PathFilterStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/PathFilterStep.java
index 8fa93bf..7a7d9db 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/PathFilterStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/PathFilterStep.java
@@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.MutablePath;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -62,8 +63,20 @@ public final class PathFilterStep<S> extends FilterStep<S> implements FromToModu
         else {
             this.traversalRing.reset();
             final Path byPath = MutablePath.make();
-            path.forEach((object, labels) -> byPath.extend(TraversalUtil.applyNullable(object, this.traversalRing.next()), labels));
-            return byPath.isSimple() == this.isSimple;
+            final List<Set<String>> labels = path.labels();
+            final List<Object> objects = path.objects();
+            for (int ix = 0; ix < objects.size(); ix++) {
+                final Traversal.Admin t = traversalRing.next();
+                final TraversalProduct p = TraversalUtil.produce(objects.get(ix), t);
+
+                // if not productive we can quit coz this path is getting filtered
+                if (!p.isProductive()) break;
+
+                byPath.extend(p.get(), labels.get(ix));
+            }
+
+            // the path sizes must be equal or else it means a by() wasn't productive and that path will be filtered
+            return path.size() == byPath.size() && byPath.isSimple() == this.isSimple;
         }
     }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
index b342100..20c8396a 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
@@ -28,11 +28,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.CollectingBarrie
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.ProjectedTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.Random;
 import java.util.Set;
 
@@ -79,7 +81,7 @@ public final class SampleGlobalStep<S> extends CollectingBarrierStep<S> implemen
     @Override
     public void processAllStarts() {
         while (this.starts.hasNext()) {
-            this.traverserSet.add(this.createProjectedTraverser(this.starts.next()));
+            this.createProjectedTraverser(this.starts.next()).ifPresent(traverserSet::add);
         }
     }
 
@@ -124,8 +126,20 @@ public final class SampleGlobalStep<S> extends CollectingBarrierStep<S> implemen
     }
 
 
-    private final ProjectedTraverser<S, Number> createProjectedTraverser(final Traverser.Admin<S> traverser) {
-        return new ProjectedTraverser<>(traverser, Collections.singletonList(TraversalUtil.apply(traverser, this.probabilityTraversal)));
+    private Optional<ProjectedTraverser<S, Number>> createProjectedTraverser(final Traverser.Admin<S> traverser) {
+        final TraversalProduct product = TraversalUtil.produce(traverser, this.probabilityTraversal);
+        if (product.isProductive()) {
+            final Object o = product.get();
+            if (!(o instanceof Number)) {
+                throw new IllegalStateException(String.format(
+                        "Traverser %s does not evaluate to a number with %s", traverser, this.probabilityTraversal));
+            }
+
+            return Optional.of(new ProjectedTraverser<>(traverser, Collections.singletonList((Number) product.get())));
+        } else {
+            return Optional.empty();
+        }
+
     }
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java
index 2ab3d9d..6820f88 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java
@@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -74,11 +75,21 @@ public final class WherePredicateStep<S> extends FilterStep<S> implements Scopin
         }
     }
 
-    private void setPredicateValues(final P<Object> predicate, final Traverser.Admin<S> traverser, final Iterator<String> selectKeysIterator) {
-        if (predicate instanceof ConnectiveP)
-            ((ConnectiveP<Object>) predicate).getPredicates().forEach(p -> this.setPredicateValues(p, traverser, selectKeysIterator));
-        else
-            predicate.setValue(TraversalUtil.applyNullable((S) this.getSafeScopeValue(Pop.last, selectKeysIterator.next(), traverser), this.traversalRing.next()));
+    private boolean setPredicateValues(final P<Object> predicate, final Traverser.Admin<S> traverser, final Iterator<String> selectKeysIterator) {
+        if (predicate instanceof ConnectiveP) {
+            for (P<Object> p : ((ConnectiveP<Object>) predicate).getPredicates()) {
+                if (!this.setPredicateValues(p, traverser, selectKeysIterator))
+                    return false;
+            }
+
+            return true;
+        } else {
+            final TraversalProduct product = TraversalUtil.produce((S) this.getSafeScopeValue(Pop.last, selectKeysIterator.next(), traverser), this.traversalRing.next());
+            if (product.isProductive())
+                predicate.setValue(product.get());
+
+            return product.isProductive();
+        }
     }
 
     public Optional<P<?>> getPredicate() {
@@ -101,17 +112,18 @@ public final class WherePredicateStep<S> extends FilterStep<S> implements Scopin
 
     @Override
     protected boolean filter(final Traverser.Admin<S> traverser) {
-        final Object value = null == this.startKey ?
-                TraversalUtil.applyNullable(traverser, this.traversalRing.next()) :
-                TraversalUtil.applyNullable((S) this.getSafeScopeValue(Pop.last, this.startKey, traverser), this.traversalRing.next());
-        this.setPredicateValues(this.predicate, traverser, this.selectKeys.iterator());
+        final TraversalProduct product = null == this.startKey ?
+                TraversalUtil.produce(traverser, this.traversalRing.next()) :
+                TraversalUtil.produce((S) this.getSafeScopeValue(Pop.last, this.startKey, traverser), this.traversalRing.next());
+
+        final boolean predicateValuesProductive = this.setPredicateValues(this.predicate, traverser, this.selectKeys.iterator());
         this.traversalRing.reset();
-        return this.predicate.test(value);
+
+        return product.isProductive() && predicateValuesProductive && this.predicate.test(product.get());
     }
 
     @Override
     public String toString() {
-        // TODO: revert the predicates to their string form?
         return StringFactory.stepString(this, this.startKey, this.predicate, this.traversalRing);
     }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupCountStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupCountStep.java
index 0f3fa25..77fd379 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupCountStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupCountStep.java
@@ -54,7 +54,7 @@ public class GroupCountStep<S, E> extends ReducingBarrierStep<S, Map<E, Long>> i
     @Override
     public Map<E, Long> projectTraverser(final Traverser.Admin<S> traverser) {
         final Map<E, Long> map = new HashMap<>(1);
-        map.put(TraversalUtil.applyNullable(traverser, this.keyTraversal), traverser.bulk());
+        TraversalUtil.produce(traverser, this.keyTraversal).ifProductive(p -> map.put((E) p, traverser.bulk()));
         return map;
     }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupStep.java
index 2213b2e..ff7ba1c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupStep.java
@@ -124,11 +124,15 @@ public final class GroupStep<S, K, V> extends ReducingBarrierStep<S, Map<K, V>>
             resetBarrierForProfiling = false;
         }
 
-        if (null == this.barrierStep) {
-            if (this.valueTraversal.hasNext())
-                map.put(TraversalUtil.applyNullable(traverser, this.keyTraversal), (V) this.valueTraversal.next());
-        } else if (this.barrierStep.hasNextBarrier())
-            map.put(TraversalUtil.applyNullable(traverser, this.keyTraversal), (V) this.barrierStep.nextBarrier());
+        TraversalUtil.produce(traverser, this.keyTraversal).ifProductive(p -> {
+            if (null == this.barrierStep) {
+                if (this.valueTraversal.hasNext()) {
+                    map.put((K) p, (V) this.valueTraversal.next());
+                }
+            } else if (this.barrierStep.hasNextBarrier())
+                map.put((K) p, (V) this.barrierStep.nextBarrier());
+        });
+
         return map;
     }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java
index 149f67a..612e20d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java
@@ -29,6 +29,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -45,7 +47,7 @@ import java.util.regex.Pattern;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class MathStep<S> extends ScalarMapStep<S, Double> implements ByModulating, TraversalParent, Scoping, PathProcessor {
+public final class MathStep<S> extends MapStep<S, Double> implements ByModulating, TraversalParent, Scoping, PathProcessor {
 
     private static final String CURRENT = "_";
     private final String equation;
@@ -61,16 +63,21 @@ public final class MathStep<S> extends ScalarMapStep<S, Double> implements ByMod
 
     @Override
     protected Traverser.Admin<Double> processNextStart() {
-        return PathProcessor.processTraverserPathLabels(super.processNextStart(), this.keepLabels);
-    }
+        final Traverser.Admin traverser = this.starts.next();
 
-    @Override
-    protected Double map(final Traverser.Admin<S> traverser) {
         final Expression localExpression = new Expression(this.expression.getExpression());
+        boolean productive = true;
         for (final String var : this.expression.getVariables()) {
-            final Object o = var.equals(CURRENT) ?
-                    TraversalUtil.applyNullable(traverser, this.traversalRing.next()) :
-                    TraversalUtil.applyNullable((S) this.getNullableScopeValue(Pop.last, var, traverser), this.traversalRing.next());
+            final TraversalProduct product = var.equals(CURRENT) ?
+                    TraversalUtil.produce(traverser, this.traversalRing.next()) :
+                    TraversalUtil.produce((S) this.getNullableScopeValue(Pop.last, var, traverser), this.traversalRing.next());
+
+            if (!product.isProductive()) {
+                productive = false;
+                break;
+            }
+
+            final Object o = product.get();
 
             // it's possible for ElementValueTraversal to return null or something that is possibly not a Number.
             // worth a check to try to return a nice error message. The TraversalRing<S, Number> is a bit optimistic
@@ -83,7 +90,11 @@ public final class MathStep<S> extends ScalarMapStep<S, Double> implements ByMod
             localExpression.setVariable(var, ((Number) o).doubleValue());
         }
         this.traversalRing.reset();
-        return localExpression.evaluate();
+
+        // if at least one of the traversals wasnt productive it will filter
+        return productive ?
+                PathProcessor.processTraverserPathLabels(traverser.split(localExpression.evaluate(), this), this.keepLabels) :
+                EmptyTraverser.instance();
     }
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
index a7f7892..2723790 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
@@ -31,6 +31,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.CollectingBarrie
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.ProjectedTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.util.function.MultiComparator;
@@ -42,6 +43,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 import java.util.Random;
 import java.util.Set;
 import java.util.function.BinaryOperator;
@@ -79,7 +81,8 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
     @Override
     public void processAllStarts() {
         while (this.starts.hasNext()) {
-            this.traverserSet.add(this.createProjectedTraverser(this.starts.next()));
+            // only add the traverser if the comparator traversal was productive
+            this.createProjectedTraverser(this.starts.next()).ifPresent(traverserSet::add);
         }
     }
 
@@ -170,16 +173,20 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
         return MemoryComputeKey.of(this.getId(), new OrderBiOperator<>(this.limit, this.multiComparator, this.random), false, true);
     }
 
-    private final ProjectedTraverser<S, Object> createProjectedTraverser(final Traverser.Admin<S> traverser) {
+    private Optional<ProjectedTraverser<S, Object>> createProjectedTraverser(final Traverser.Admin<S> traverser) {
         // this was ProjectedTraverser<S, C> but the projection may not be C in the case of a lambda where a
         // Comparable may not be expected but rather an object that can be compared in any way given a lambda.
         // not sure why this is suddenly an issue but Intellij would not let certain tests pass without this
         // adjustment here.
         final List<Object> projections = new ArrayList<>(this.comparators.size());
         for (final Pair<Traversal.Admin<S, C>, Comparator<C>> pair : this.comparators) {
-            projections.add(TraversalUtil.apply(traverser, pair.getValue0()));
+            final TraversalProduct product = TraversalUtil.produce(traverser, pair.getValue0());
+            if (!product.isProductive()) break;
+            projections.add(product.get());
         }
-        return new ProjectedTraverser(traverser, projections);
+
+        // if a traversal wasn't productive then the sizes wont match and it will filter
+        return projections.size() == comparators.size() ? Optional.of(new ProjectedTraverser(traverser, projections)) : Optional.empty();
     }
 
     private final MultiComparator<C> createMultiComparator() {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderLocalStep.java
index eb3a5f8..fdcfaa4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderLocalStep.java
@@ -27,8 +27,9 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.ComparatorHolder;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Seedable;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
-import org.apache.tinkerpop.gremlin.util.function.ChainedComparator;
 import org.javatuples.Pair;
 
 import java.util.ArrayList;
@@ -48,7 +49,6 @@ import java.util.stream.Collectors;
 public final class OrderLocalStep<S, C extends Comparable> extends ScalarMapStep<S, S> implements ComparatorHolder<S, C>, ByModulating, TraversalParent, Seedable {
 
     private List<Pair<Traversal.Admin<S, C>, Comparator<C>>> comparators = new ArrayList<>();
-    private ChainedComparator<S, C> chainedComparator = null;
     private final Random random = new Random();
 
     public OrderLocalStep(final Traversal.Admin traversal) {
@@ -62,15 +62,73 @@ public final class OrderLocalStep<S, C extends Comparable> extends ScalarMapStep
 
     @Override
     protected S map(final Traverser.Admin<S> traverser) {
-        if (null == this.chainedComparator)
-            this.chainedComparator = new ChainedComparator<>(false, this.comparators);
         final S start = traverser.get();
-        if (start instanceof Collection)
-            return (S) sortCollection((Collection) start, this.chainedComparator);
-        else if (start instanceof Map)
-            return (S) sortMap((Map) start, this.chainedComparator);
-        else
-            return start;
+
+        // modulate each traverser object and keep its value to sort on as Pair<traverser-object,List<modulated-object>>
+        // as of 3.6.0 this transformation occurs in place and values held in memory so that unproductive by() values
+        // can be filtered. without this trade-off for more memory it would be necessary to apply the modulation twice
+        // once for the filter and once for the compare.
+        if (start instanceof Collection) {
+            final Collection<S> original = (Collection<S>) start;
+            final List<Pair<S, List<C>>> filteredAndModulated = filterAndModulate(original);
+            return (S) filteredAndModulated.stream().map(Pair::getValue0).collect(Collectors.toList());
+        } else if (start instanceof Map) {
+            final List<Pair<S, List<C>>> filteredAndModulated = filterAndModulate(((Map) start).entrySet());
+            final LinkedHashMap sortedMap = new LinkedHashMap();
+            filteredAndModulated.stream().map(Pair::getValue0).map(entry -> (Map.Entry) entry).
+                    forEach(entry -> sortedMap.put(entry.getKey(), entry.getValue()));
+            return (S) sortedMap;
+        }
+
+        return start;
+    }
+
+    /**
+     * Take the collection and apply modulators removing traversers that have modulators that aren't productive.
+     */
+    private List<Pair<S, List<C>>> filterAndModulate(final Collection<S> original) {
+        if (comparators.isEmpty())
+            this.comparators.add(new Pair<>(new IdentityTraversal(), (Comparator) Order.asc));
+
+        // detect shuffle and optimize by either ignoring other comparators if shuffle is last or
+        // ignoring comparators that are shuffles if they are in the middle
+        final boolean isShuffle = (Comparator) this.comparators.get(this.comparators.size() - 1).getValue1() == Order.shuffle;
+        final List<Pair<Traversal.Admin<S, C>, Comparator<C>>> relevantComparators = isShuffle ?
+                this.comparators :
+                this.comparators.stream().filter(p -> (Comparator) p.getValue1() != Order.shuffle).collect(Collectors.toList());
+
+        final List<Pair<S, List<C>>> filteredAndModulated = new ArrayList<>();
+        final List<Traversal.Admin<S, C>> modulators = relevantComparators.stream().map(Pair::getValue0).collect(Collectors.toList());
+        for (S s : original) {
+            // filter out unproductive by()
+            final List<C> modulations = modulators.stream().map(t -> TraversalUtil.produce(s, t)).
+                    filter(TraversalProduct::isProductive).
+                    map(product -> (C) product.get()).collect(Collectors.toList());
+
+            // when sizes arent the same it means a by() wasn't productive and it is ignored
+            if (modulations.size() == modulators.size()) {
+                filteredAndModulated.add(Pair.with(s, modulations));
+            }
+        }
+
+        if (isShuffle) {
+            Collections.shuffle(filteredAndModulated, random);
+        } else {
+            // sort the filter/modulated local list in place using the index of the modulator/comparators
+            Collections.sort(filteredAndModulated, (o1, o2) -> {
+                final List<C> modulated1 = o1.getValue1();
+                final List<C> modulated2 = o2.getValue1();
+                for (int ix = 0; ix < modulated1.size(); ix++) {
+                    final int comparison = relevantComparators.get(ix).getValue1().compare(modulated1.get(ix), modulated2.get(ix));
+                    if (comparison != 0)
+                        return comparison;
+                }
+
+                return 0;
+            });
+        }
+
+        return filteredAndModulated;
     }
 
     @Override
@@ -138,7 +196,6 @@ public final class OrderLocalStep<S, C extends Comparable> extends ScalarMapStep
         for (final Pair<Traversal.Admin<S, C>, Comparator<C>> comparator : this.comparators) {
             clone.comparators.add(new Pair<>(comparator.getValue0().clone(), comparator.getValue1()));
         }
-        clone.chainedComparator = null;
         return clone;
     }
 
@@ -147,29 +204,4 @@ public final class OrderLocalStep<S, C extends Comparable> extends ScalarMapStep
         super.setTraversal(parentTraversal);
         this.comparators.stream().map(Pair::getValue0).forEach(TraversalParent.super::integrateChild);
     }
-
-    /////////////
-
-    private <A> List<A> sortCollection(final Collection<A> collection, final ChainedComparator comparator) {
-        if (collection instanceof List) {
-            if (comparator.isShuffle())
-                Collections.shuffle((List) collection, random);
-            else
-                Collections.sort((List) collection, comparator);
-            return (List<A>) collection;
-        } else {
-            return sortCollection(new ArrayList<>(collection), comparator);
-        }
-    }
-
-    private <K, V> Map<K, V> sortMap(final Map<K, V> map, final ChainedComparator comparator) {
-        final List<Map.Entry<K, V>> entries = new ArrayList<>(map.entrySet());
-        if (comparator.isShuffle())
-            Collections.shuffle(entries, random);
-        else
-            Collections.sort(entries, comparator);
-        final LinkedHashMap<K, V> sortedMap = new LinkedHashMap<>();
-        entries.forEach(entry -> sortedMap.put(entry.getKey(), entry.getValue()));
-        return sortedMap;
-    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java
index 07c8d7d..8d073a9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathStep.java
@@ -27,6 +27,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.MutablePath;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -38,7 +40,7 @@ import java.util.Set;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class PathStep<S> extends ScalarMapStep<S, Path> implements TraversalParent, PathProcessor, ByModulating, FromToModulating {
+public final class PathStep<S> extends MapStep<S, Path> implements TraversalParent, PathProcessor, ByModulating, FromToModulating {
 
     private TraversalRing<Object, Object> traversalRing;
     private Set<String> keepLabels;
@@ -51,20 +53,6 @@ public final class PathStep<S> extends ScalarMapStep<S, Path> implements Travers
     }
 
     @Override
-    protected Path map(final Traverser.Admin<S> traverser) {
-        final Path path = traverser.path().subPath(this.fromLabel, this.toLabel);
-        if (this.traversalRing.isEmpty())
-            return path;
-        else {
-            this.traversalRing.reset();
-            final Path byPath = MutablePath.make();
-            path.forEach((object, labels) -> byPath.extend(TraversalUtil.applyNullable(object, this.traversalRing.next()), labels));
-            return byPath;
-        }
-    }
-
-
-    @Override
     public PathStep<S> clone() {
         final PathStep<S> clone = (PathStep<S>) super.clone();
         clone.traversalRing = this.traversalRing.clone();
@@ -122,7 +110,31 @@ public final class PathStep<S> extends ScalarMapStep<S, Path> implements Travers
 
     @Override
     protected Traverser.Admin<Path> processNextStart() {
-        return PathProcessor.processTraverserPathLabels(super.processNextStart(), this.keepLabels);
+        final Traverser.Admin<S> traverser = this.starts.next();
+        final Path path = traverser.path().subPath(this.fromLabel, this.toLabel);
+        if (this.traversalRing.isEmpty())
+            return PathProcessor.processTraverserPathLabels(traverser.split(path, this), this.keepLabels);
+        else {
+            this.traversalRing.reset();
+            final Path byPath = MutablePath.make();
+
+            final List<Set<String>> labels = path.labels();
+            final List<Object> objects = path.objects();
+            for (int ix = 0; ix < objects.size(); ix++) {
+                final Traversal.Admin t = traversalRing.next();
+                final TraversalProduct p = TraversalUtil.produce(objects.get(ix), t);
+
+                // if not productive we can quit coz this path is getting filtered
+                if (!p.isProductive()) break;
+
+                byPath.extend(p.get(), labels.get(ix));
+            }
+
+            // the path sizes must be equal or else it means a by() wasn't productive and that path will be filtered
+            return path.size() == byPath.size() ?
+                    PathProcessor.processTraverserPathLabels(traverser.split(byPath, this), this.keepLabels) :
+                    EmptyTraverser.instance();
+        }
     }
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectStep.java
index b97c8d6..1aa9eae 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectStep.java
@@ -52,7 +52,7 @@ public final class ProjectStep<S, E> extends ScalarMapStep<S, Map<String, E>> im
     protected Map<String, E> map(final Traverser.Admin<S> traverser) {
         final Map<String, E> end = new LinkedHashMap<>(this.projectKeys.size(), 1.0f);
         for (final String projectKey : this.projectKeys) {
-            end.put(projectKey, TraversalUtil.applyNullable(traverser, this.traversalRing.next()));
+            TraversalUtil.produce(traverser, this.traversalRing.next()).ifProductive(p -> end.put(projectKey, (E) p));
         }
         this.traversalRing.reset();
         return end;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java
index 12cd6be..35f83cd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.Element;
@@ -39,6 +40,7 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -108,8 +110,15 @@ public class PropertyMapStep<K,E> extends ScalarMapStep<Element, Map<K, E>>
             }
         }
         if (!traversalRing.isEmpty()) {
-            for (final Object key : map.keySet()) {
-                map.compute(key, (k, v) -> TraversalUtil.applyNullable(v, (Traversal.Admin) this.traversalRing.next()));
+            // will cop a ConcurrentModification if a key is dropped so need this little copy here
+            final Set<Object> keys = new HashSet<>(map.keySet());
+            for (final Object key : keys) {
+                map.compute(key, (k, v) -> {
+                    final TraversalProduct product = TraversalUtil.produce(v, (Traversal.Admin) this.traversalRing.next());
+
+                    // compute() should take the null and remove the key
+                    return product.isProductive() ? product.get() : null;
+                });
             }
             this.traversalRing.reset();
         }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java
index 976c512..b2de4b0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java
@@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
@@ -59,7 +60,11 @@ public final class SelectOneStep<S, E> extends MapStep<S, E> implements Traversa
         try {
             final S o = getScopeValue(pop, selectKey, traverser);
             if (null == o) return traverser.split(null, this);
-            final Traverser.Admin<E> outTraverser = traverser.split(TraversalUtil.applyNullable(o, this.selectTraversal), this);
+
+            final TraversalProduct product = TraversalUtil.produce(o, this.selectTraversal);
+            if (!product.isProductive()) return EmptyTraverser.instance();
+
+            final Traverser.Admin<E> outTraverser = traverser.split((E) product.get(), this);
             if (!(this.getTraversal().getParent() instanceof MatchStep))
                 PathProcessor.processTraverserPathLabels(outTraverser, this.keepLabels);
             return outTraverser;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java
index 9edde9d..05a6a5b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java
@@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -69,8 +70,17 @@ public final class SelectStep<S, E> extends MapStep<S, Map<String, E>> implement
         try {
             for (final String selectKey : this.selectKeys) {
                 final E end = this.getScopeValue(this.pop, selectKey, traverser);
-                bindings.put(selectKey, TraversalUtil.applyNullable(end, this.traversalRing.next()));
+                final TraversalProduct product = TraversalUtil.produce(end, this.traversalRing.next());
+
+                if (!product.isProductive()) break;
+
+                bindings.put(selectKey, (E) product.get());
             }
+
+            // bindings should be the same size as keys or else there was an uproductive by() in which case we filter
+            // with an EmptyTraverser
+            if (bindings.size() != selectKeys.size()) return EmptyTraverser.instance();
+
         } catch (KeyNotFoundException nfe) {
             return EmptyTraverser.instance();
         } finally {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java
index 1904859..ee70053 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java
@@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.ReducingBarrierStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -81,10 +82,13 @@ public final class TreeStep<S> extends ReducingBarrierStep<S, Tree> implements T
         Tree depth = topTree;
         final Path path = traverser.path();
         for (int i = 0; i < path.size(); i++) {
-            final Object object = TraversalUtil.applyNullable(path.<Object>get(i), this.traversalRing.next());
-            if (!depth.containsKey(object))
-                depth.put(object, new Tree<>());
-            depth = (Tree) depth.get(object);
+            final TraversalProduct product = TraversalUtil.produce(path.<Object>get(i), this.traversalRing.next());
+            if (product.isProductive()) {
+                final Object object = product.get();
+                if (!depth.containsKey(object))
+                    depth.put(object, new Tree<>());
+                depth = (Tree) depth.get(object);
+            }
         }
         this.traversalRing.reset();
         return topTree;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateGlobalStep.java
index 0f75963..c8fa203 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateGlobalStep.java
@@ -31,6 +31,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequire
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
 import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
+import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.util.function.BulkSetSupplier;
 
@@ -125,8 +126,11 @@ public final class AggregateGlobalStep<S> extends AbstractStep<S, S> implements
             final BulkSet<Object> bulkSet = new BulkSet<>();
             while (this.starts.hasNext()) {
                 final Traverser.Admin<S> traverser = this.starts.next();
-                bulkSet.add(TraversalUtil.applyNullable(traverser, this.aggregateTraversal), traverser.bulk());
-                traverser.setStepId(this.getNextStep().getId()); // when barrier is reloaded, the traversers should be at the next step
+                TraversalUtil.produce(traverser, aggregateTraversal).ifProductive(p -> bulkSet.add(p, traverser.bulk()));
+
+                traverser.setStepId(this.getNextStep().getId());
+
+                // when barrier is reloaded, the traversers should be at the next step
                 this.barrier.add(traverser);
             }
             this.getTraversal().getSideEffects().add(this.sideEffectKey, bulkSet);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateLocalStep.java
index 3c7c0fa..7331caf 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateLocalStep.java
@@ -53,7 +53,7 @@ public final class AggregateLocalStep<S> extends SideEffectStep<S> implements Si
     @Override
     protected void sideEffect(final Traverser.Admin<S> traverser) {
         final BulkSet<Object> bulkSet = new BulkSet<>();
-        bulkSet.add(TraversalUtil.applyNullable(traverser, this.storeTraversal), traverser.bulk());
+        TraversalUtil.produce(traverser, this.storeTraversal).ifProductive(p -> bulkSet.add(p, traverser.bulk()));
         this.getTraversal().getSideEffects().add(this.sideEffectKey, bulkSet);
     }
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SackValueStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SackValueStep.java
index bd0e828..b19f099 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SackValueStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SackValueStep.java
@@ -23,7 +23,10 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.LambdaHolder;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalProduct;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
@@ -35,7 +38,7 @@ import java.util.function.BiFunction;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class SackValueStep<S, A, B> extends SideEffectStep<S> implements TraversalParent, ByModulating, LambdaHolder {
+public final class SackValueStep<S, A, B> extends AbstractStep<S, S> implements TraversalParent, ByModulating, LambdaHolder {
 
     private Traversal.Admin<S, B> sackTraversal = null;
 
@@ -63,8 +66,17 @@ public final class SackValueStep<S, A, B> extends SideEffectStep<S> implements T
     }
 
     @Override
-    protected void sideEffect(final Traverser.Admin<S> traverser) {
-        traverser.sack(this.sackFunction.apply(traverser.sack(), null == this.sackTraversal ? (B) traverser.get() : TraversalUtil.apply(traverser, this.sackTraversal)));
+    protected Traverser.Admin<S> processNextStart() {
+        final Traverser.Admin<S> traverser = this.starts.next();
+        if (null == sackTraversal) {
+            traverser.sack(this.sackFunction.apply(traverser.sack(), (B) traverser.get()));
+            return traverser;
+        } else {
+            final TraversalProduct product = TraversalUtil.produce(traverser, this.sackTraversal);
+            if (!product.isProductive()) return EmptyTraverser.instance();
+            traverser.sack(this.sackFunction.apply(traverser.sack(), (B) product.get()));
+            return traverser;
+        }
     }
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalProduct.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalProduct.java
new file mode 100644
index 0000000..9711d81
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalProduct.java
@@ -0,0 +1,55 @@
+/*
+ * 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 java.util.function.Consumer;
+
+/**
+ * This class represents the state of the output of a child {@link Traversal} where it is either productive or not.
+ * It is productive if it has at least one traverser within it, even if that traverser is holding a {@code null}.
+ */
+public final class TraversalProduct {
+    public static final TraversalProduct UNPRODUCTIVE = new TraversalProduct();
+    private final Object o;
+    private final boolean productive;
+
+    private TraversalProduct() {
+        this.o = null; // null is valid technically but productive=false trumps it
+        this.productive = false;
+    }
+
+    TraversalProduct(final Object o) {
+        this.o = o;
+        this.productive = true;
+    }
+
+    public Object get() {
+        return o;
+    }
+
+    public boolean isProductive() {
+        return productive;
+    }
+
+    public void ifProductive(final Consumer<Object> c) {
+        if (productive) c.accept(o);
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
index 4b2b15f..1acb547 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
@@ -34,12 +34,8 @@ public final class TraversalUtil {
     private TraversalUtil() {
     }
 
-    public static final <S, E> E apply(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
-        final Traverser.Admin<S> split = traverser.split();
-        split.setSideEffects(traversal.getSideEffects());
-        split.setBulk(1l);
-        traversal.reset();
-        traversal.addStart(split);
+    public static <S, E> E apply(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
+        final Traverser<S> split = prepare(traverser, traversal);
         try {
             return traversal.next(); // map
         } catch (final NoSuchElementException e) {
@@ -52,23 +48,43 @@ public final class TraversalUtil {
         }
     }
 
-    public static final <S, E> Iterator<E> applyAll(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
-        final Traverser.Admin<S> split = traverser.split();
-        split.setSideEffects(traversal.getSideEffects());
-        split.setBulk(1l);
+    public static <S, E> E apply(final S start, final Traversal.Admin<S, E> traversal) {
         traversal.reset();
-        traversal.addStart(split);
+        traversal.addStart(traversal.getTraverserGenerator().generate(start, traversal.getStartStep(), 1l));
+        try {
+            return traversal.next(); // map
+        } catch (final NoSuchElementException e) {
+            throw new IllegalArgumentException("The provided start does not map to a value: " + start + "->" + traversal);
+        } finally {
+            //Close the traversal to release any underlying resources.
+            CloseableIterator.closeIterator(traversal);
+        }
+    }
+
+    public static <S, E> E applyNullable(final S start, final Traversal.Admin<S, E> traversal) {
+        return null == traversal ? (E) start : TraversalUtil.apply(start, traversal);
+    }
+
+    public static <S, E> E applyNullable(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
+        return null == traversal ? (E) traverser.get() : TraversalUtil.apply(traverser, traversal);
+    }
+
+    public static <S, E> Iterator<E> applyAll(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
+        prepare(traverser, traversal);
         return traversal; // flatmap
     }
 
-    public static final <S, E> boolean test(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal, E end) {
+    public static <S, E> Iterator<E> applyAll(final S start, final Traversal.Admin<S, E> traversal) {
+        traversal.reset();
+        traversal.addStart(traversal.getTraverserGenerator().generate(start, traversal.getStartStep(), 1l));
+        return traversal; // flatMap
+    }
+
+    public static <S, E> boolean test(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal, E end) {
         if (null == end) return TraversalUtil.test(traverser, traversal);
 
-        final Traverser.Admin<S> split = traverser.split();
-        split.setSideEffects(traversal.getSideEffects());
-        split.setBulk(1l);
-        traversal.reset();
-        traversal.addStart(split);
+        prepare(traverser, traversal);
+
         final Step<?, E> endStep = traversal.getEndStep();
         boolean result = false;
         while (traversal.hasNext()) {
@@ -85,17 +101,44 @@ public final class TraversalUtil {
         return result;
     }
 
-    public static final <S, E> E applyNullable(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
-        return null == traversal ? (E) traverser.get() : TraversalUtil.apply(traverser, traversal);
+    public static <S, E> TraversalProduct produce(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
+        if (null == traversal) {
+            return new TraversalProduct(traverser.get());
+        } else {
+            prepare(traverser, traversal);
+            try {
+                if (traversal.hasNext()) {
+                    return new TraversalProduct(traversal.next());
+                } else {
+                    return TraversalProduct.UNPRODUCTIVE;
+                }
+            } finally {
+                CloseableIterator.closeIterator(traversal);
+            }
+        }
     }
 
-    public static final <S, E> boolean test(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
-        final Traverser.Admin<S> split = traverser.split();
-        split.setSideEffects(traversal.getSideEffects());
-        split.setBulk(1l);
-        traversal.reset();
-        traversal.addStart(split);
-        boolean val =  traversal.hasNext(); // filter
+    public static <S, E> TraversalProduct produce(final S start, final Traversal.Admin<S, E> traversal) {
+        if (null == traversal) {
+            return new TraversalProduct(start);
+        } else {
+            traversal.reset();
+            traversal.addStart(traversal.getTraverserGenerator().generate(start, traversal.getStartStep(), 1L));
+            try {
+                if (traversal.hasNext()) {
+                    return new TraversalProduct(traversal.next());
+                } else {
+                    return TraversalProduct.UNPRODUCTIVE;
+                }
+            } finally {
+                CloseableIterator.closeIterator(traversal);
+            }
+        }
+    }
+
+    public static <S, E> boolean test(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
+        prepare(traverser, traversal);
+        final boolean val =  traversal.hasNext(); // filter
 
         //Close the traversal to release any underlying resources.
         CloseableIterator.closeIterator(traversal);
@@ -103,28 +146,7 @@ public final class TraversalUtil {
         return val;
     }
 
-    ///////
-
-    public static final <S, E> E apply(final S start, final Traversal.Admin<S, E> traversal) {
-        traversal.reset();
-        traversal.addStart(traversal.getTraverserGenerator().generate(start, traversal.getStartStep(), 1l));
-        try {
-            return traversal.next(); // map
-        } catch (final NoSuchElementException e) {
-            throw new IllegalArgumentException("The provided start does not map to a value: " + start + "->" + traversal);
-        } finally {
-            //Close the traversal to release any underlying resources.
-            CloseableIterator.closeIterator(traversal);
-        }
-    }
-
-    public static final <S, E> Iterator<E> applyAll(final S start, final Traversal.Admin<S, E> traversal) {
-        traversal.reset();
-        traversal.addStart(traversal.getTraverserGenerator().generate(start, traversal.getStartStep(), 1l));
-        return traversal; // flatMap
-    }
-
-    public static final <S, E> boolean test(final S start, final Traversal.Admin<S, E> traversal, final E end) {
+    public static <S, E> boolean test(final S start, final Traversal.Admin<S, E> traversal, final E end) {
         if (null == end) return TraversalUtil.test(start, traversal);
 
         traversal.reset();
@@ -145,11 +167,7 @@ public final class TraversalUtil {
         return result;
     }
 
-    public static final <S, E> E applyNullable(final S start, final Traversal.Admin<S, E> traversal) {
-        return null == traversal ? (E) start : TraversalUtil.apply(start, traversal);
-    }
-
-    public static final <S, E> boolean test(final S start, final Traversal.Admin<S, E> traversal) {
+    public static <S, E> boolean test(final S start, final Traversal.Admin<S, E> traversal) {
         traversal.reset();
         traversal.addStart(traversal.getTraverserGenerator().generate(start, traversal.getStartStep(), 1l));
         boolean result = traversal.hasNext(); // filter
@@ -159,4 +177,13 @@ public final class TraversalUtil {
 
         return result;
     }
+
+    public static <S, E> Traverser<S> prepare(final Traverser.Admin<S> traverser, final Traversal.Admin<S, E> traversal) {
+        final Traverser.Admin<S> split = traverser.split();
+        split.setSideEffects(traversal.getSideEffects());
+        split.setBulk(1L);
+        traversal.reset();
+        traversal.addStart(split);
+        return split;
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/ChainedComparator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/ChainedComparator.java
index 984767f..1a7b114 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/ChainedComparator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/ChainedComparator.java
@@ -33,7 +33,9 @@ import java.util.stream.Collectors;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @deprecated As of release 3.6.0, not replaced.
  */
+@Deprecated
 public final class ChainedComparator<S, C extends Comparable> implements Comparator<S>, Serializable, Cloneable {
 
     private List<Pair<Traversal.Admin<S, C>, Comparator<C>>> comparators = new ArrayList<>();
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java
index 4adc701..c28ffc4 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PathTest.java
@@ -33,6 +33,8 @@ import java.util.List;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -529,4 +531,22 @@ public class PathTest {
             assertEquals(1, subPath.labels().get(2).size());
         });
     }
+
+    @Test
+    public void shouldHandleNullSemanticsCorrectly() {
+        PATH_SUPPLIERS.forEach(supplier -> {
+            final Path p = supplier.get();
+            assertThat(p.isSimple(), is(true));
+            p.extend(null, Collections.emptySet());
+            assertThat(p.isSimple(), is(true));
+            p.extend(1, Collections.emptySet());
+            assertThat(p.isSimple(), is(true));
+            p.extend(null, Collections.emptySet());
+
+            // immutable path won't change so its just going to always be empty and thus always simple
+            if (!(p instanceof ImmutablePath)) {
+                assertThat(p.isSimple(), is(false));
+            }
+        });
+    }
 }
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategyTest.java
index 88bb949..47cd52f 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategyTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/PathProcessorStrategyTest.java
@@ -43,6 +43,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.coalesce;
 import static org.junit.Assert.assertEquals;
@@ -53,9 +54,9 @@ import static org.junit.Assert.assertEquals;
 @RunWith(Parameterized.class)
 public class PathProcessorStrategyTest {
     private static final Translator.ScriptTranslator translator = GroovyTranslator.of("__");
-    private static final Collection<TraversalStrategy> withoutProductiveByStrategy =
-            TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList().stream().
-                    filter(s -> s != ProductiveByStrategy.instance()).collect(Collectors.toList());
+    private static final Collection<TraversalStrategy> withProductiveByStrategy =
+            Stream.concat(Stream.of(ProductiveByStrategy.instance()),
+                          TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList().stream()).collect(Collectors.toList());
 
     @Parameterized.Parameter(value = 0)
     public Traversal.Admin original;
@@ -94,12 +95,12 @@ public class PathProcessorStrategyTest {
                 {__.select("a").by(__.outE().count()), __.select("a").map(__.outE().count()), Collections.emptyList()},
                 {__.select("a").by("name"), __.select("a").map(new ValueTraversal<>("name")), Collections.emptyList()},
                 {__.select("a").out(), __.select("a").out(), Collections.emptyList()},
-                {__.select(Pop.all, "a").by(__.values("name")), __.select(Pop.all, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
-                {__.select(Pop.last, "a").by(__.values("name")), __.select(Pop.last, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
-                {__.select(Pop.first, "a").by(__.values("name")), __.select(Pop.first, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
-                {__.select(Pop.all, "a").by(__.values("name")), __.select(Pop.all, "a").by("name"), withoutProductiveByStrategy},
-                {__.select(Pop.last, "a").by(__.values("name")), __.select(Pop.last, "a").map(__.values("name")), withoutProductiveByStrategy},
-                {__.select(Pop.first, "a").by(__.values("name")), __.select(Pop.first, "a").map(__.values("name")), withoutProductiveByStrategy},
+                {__.select(Pop.all, "a").by(__.values("name")), __.select(Pop.all, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
+                {__.select(Pop.last, "a").by(__.values("name")), __.select(Pop.last, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
+                {__.select(Pop.first, "a").by(__.values("name")), __.select(Pop.first, "a").by(new ValueTraversal<>("name", coalesce(new ValueTraversal<>("name"), new ConstantTraversal(null)).asAdmin())), withProductiveByStrategy},
+                {__.select(Pop.all, "a").by(__.values("name")), __.select(Pop.all, "a").by("name"), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+                {__.select(Pop.last, "a").by(__.values("name")), __.select(Pop.last, "a").map(__.values("name")), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
+                {__.select(Pop.first, "a").by(__.values("name")), __.select(Pop.first, "a").map(__.values("name")), TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()},
                 // select("a","b")
                 {__.select("a", "b"), __.select("a", "b"), Collections.emptyList()},
                 {__.select("a", "b").by(), __.select("a", "b").by(), Collections.emptyList()},
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
index 412a16f..ace783d 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
@@ -44,7 +44,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
             new Dictionary<string, IgnoreReason>
             {
                 // Add here the name of scenarios to ignore and the reason, e.g.:
-                {"g_V_group_byXageX", IgnoreReason.NullKeysInMapNotSupported},
+                {"g_withStrategiesXProductiveByStrategyX_V_group_byXageX", IgnoreReason.NullKeysInMapNotSupported},
+                {"g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX", IgnoreReason.NullKeysInMapNotSupported},
 
                 // they are not failing as a result of the Gremlin itself - they are failing because of shortcomings in
                 // the test suite.
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 8977404..7186968 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -131,6 +131,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_coinX0X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Coin(0.0)}}, 
                {"g_withStrategiesXSeedStrategyX_V_coinX50X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new SeedStrategy(seed: 999999)).V().Coin(0.5)}}, 
                {"g_VX1X_outXcreatedX_inXcreatedX_cyclicPath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out("created").In("created").CyclicPath()}}, 
+               {"g_VX1X_both_both_cyclicPath_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Both().Both().CyclicPath().By("age")}}, 
                {"g_VX1X_outXcreatedX_inXcreatedX_cyclicPath_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out("created").In("created").CyclicPath().Path()}}, 
                {"g_VX1X_asXaX_outXcreatedX_asXbX_inXcreatedX_asXcX_cyclicPath_fromXaX_toXbX_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("created").As("b").In("created").As("c").CyclicPath().From("a").To("b").Path()}}, 
                {"g_injectX0X_V_both_coalesceXhasXname_markoX_both_constantX0XX_cyclicPath_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(0).V().Both().Coalesce<object>(__.Has("name","marko").Both(),__.Constant<object>(0)).CyclicPath().Path()}}, 
@@ -155,6 +156,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_both_properties_dedup_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Both().Properties<object>().Dedup().Count()}}, 
                {"g_V_both_properties_properties_dedup_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Both().Properties<object>().Properties<object>().Dedup().Count()}}, 
                {"g_V_order_byXname_descX_barrier_dedup_age_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Order().By("name",Order.Desc).Barrier().Dedup().By("age").Values<object>("name")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_order_byXname_descX_barrier_dedup_age_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Order().By("name",Order.Desc).Barrier().Dedup().By("age").Values<object>("name")}}, 
+               {"g_V_both_dedup_age_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Both().Dedup().By("age").Values<object>("name")}}, 
+               {"g_V_asXaX_both_asXbX_both_asXcX_dedupXa_bX_age_selectXa_b_cX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").Both().As("b").Both().As("c").Dedup("a","b").By("age").Select<object>("a","b","c").By("name")}}, 
                {"g_V_drop", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().As("a").AddV().As("b").AddE("knows").To("a"), (g,p) =>g.V().Drop(), (g,p) =>g.V(), (g,p) =>g.E()}}, 
                {"g_V_outE_drop", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().As("a").AddV().As("b").AddE("knows").To("a"), (g,p) =>g.V().OutE().Drop(), (g,p) =>g.V(), (g,p) =>g.E()}}, 
                {"g_V_properties_drop", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().Property("name","bob").AddV().Property("name","alice"), (g,p) =>g.V().Properties<object>().Drop(), (g,p) =>g.V(), (g,p) =>g.V().Properties<object>()}}, 
@@ -281,10 +285,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_localXoutE_sampleX1X_byXweightXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.OutE().Sample(1).By("weight"))}}, 
                {"g_withStrategiesXSeedStrategyX_V_group_byXlabelX_byXbothE_weight_order_sampleX2X_foldXunfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new SeedStrategy(seed: 999999)).V().Group<object,object>().By(T.Label).By(__.BothE().Values<object>("weight").Order().Sample(2).Fold()).Unfold<object>()}}, 
                {"g_withStrategiesXSeedStrategyX_V_group_byXlabelX_byXbothE_weight_order_fold_sampleXlocal_5XXunfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new SeedStrategy(seed: 999999)).V().Group<object,object>().By(T.Label).By(__.BothE().Values<object>("weight").Order().Fold().Sample(Scope.Local,5)).Unfold<object>()}}, 
+               {"g_withStrategiesXSeedStrategyX_V_order_byXlabel_descX_sampleX1X_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new SeedStrategy(seed: 999999)).V().Order().By(T.Label,Order.Desc).Sample(1).By("age")}}, 
                {"g_VX1X_outXcreatedX_inXcreatedX_simplePath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out("created").In("created").SimplePath()}}, 
                {"g_V_repeatXboth_simplePathX_timesX3X_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Both().SimplePath()).Times(3).Path()}}, 
                {"g_V_asXaX_out_asXbX_out_asXcX_simplePath_byXlabelX_fromXbX_toXcX_path_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").Out().As("b").Out().As("c").SimplePath().By(T.Label).From("b").To("c").Path().By("name")}}, 
                {"g_injectX0X_V_both_coalesceXhasXname_markoX_both_constantX0XX_simplePath_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(0).V().Both().Coalesce<object>(__.Has("name","marko").Both(),__.Constant<object>(0)).SimplePath().Path()}}, 
+               {"g_V_both_asXaX_both_asXbX_simplePath_path_byXageX__fromXaX_toXbX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Both().As("a").Both().As("b").SimplePath().Path().By("age").From("a").To("b")}}, 
                {"g_V_valuesXnameX_order_tailXglobal_2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Order().Tail<object>(Scope.Global,2)}}, 
                {"g_V_valuesXnameX_order_tailX2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Order().Tail<object>(2)}}, 
                {"g_V_valuesXnameX_order_tail", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Order().Tail<object>()}}, 
@@ -323,6 +329,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_asXaX_outEXcreatedX_asXbX_inV_asXcX_inXcreatedX_asXdX_whereXa_ltXbX_orXgtXcXX_andXneqXdXXX_byXageX_byXweightX_byXinXcreatedX_valuesXageX_minX_selectXa_c_dX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").OutE("created").As("b").InV().As("c").In("created").As("d").Where("a",P.Lt("b").Or(P.Gt("c")).And(P.Neq("d"))).By("age").By("weight").By(__.In("created").Values<object>("age").Min<object>()).Select<object>("a","c [...]
                {"g_VX1X_asXaX_out_hasXageX_whereXgtXaXX_byXageX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out().Has("age").Where(P.Gt("a")).By("age").Values<object>("name")}}, 
                {"g_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid3"]).As("a").In().Out().As("b").Where("a",P.Eq("b")).By("age").Values<object>("name")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V(p["vid3"]).As("a").In().Out().As("b").Where("a",P.Eq("b")).By("age").Values<object>("name")}}, 
                {"g_V_coworker", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Filter(__.OutE("created")).Aggregate("p").As("p1").Values<object>("name").As("p1n").Select<object>("p").Unfold<object>().Where(P.Neq("p1")).As("p2").Values<object>("name").As("p2n").Select<object>("p2").Out("created").Choose<object>(__.In("created").Where(P.Eq("p1")),__.Values<object>("name"),__.Constant<object>(p["xx1"])).Group<object,object>(). [...]
                {"g_V_coworker_with_midV", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Filter(__.OutE("created")).As("p1").V().HasLabel("person").Where(P.Neq("p1")).Filter(__.OutE("created")).As("p2").Map<object>(__.Out("created").Where(__.In("created").As("p1")).Values<object>("name").Fold()).Group<object,object>().By(__.Select<object>("p1").By("name")).By(__.Group<object,object>().By(__.Select<object>("p2").By("name")). [...]
                {"g_V_playlist_paths", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("name","Bob_Dylan").In("sungBy").Order().By("name").As("a").Repeat(__.Out().Order().By("name").SimplePath().From("a")).Until(__.Out("writtenBy").Has("name","Johnny_Cash")).Limit<object>(1).As("b").Repeat(__.Out().Order().By("name").As("c").SimplePath().From("b").To("c")).Until(__.Out("sungBy").Has("name","Grateful_Dead")).Limit<object>(1).Path().From("a") [...]
@@ -451,6 +458,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_asXaX_outXcreatedX_asXbX_mathXb_plus_aX_byXinXcreatedX_countX_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").Out("created").As("b").Math("b + a").By(__.In("created").Count()).By("age")}}, 
                {"g_withSackX1X_injectX1X_repeatXsackXsumX_byXconstantX1XXX_timesX5X_emit_mathXsin__X_byXsackX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(1).Inject(1).Repeat(__.Sack(Operator.Sum).By(__.Constant<object>(1))).Times(5).Emit().Math("sin _").By(__.Sack<object>())}}, 
                {"g_V_projectXa_b_cX_byXbothE_weight_sumX_byXbothE_countX_byXnameX_order_byXmathXa_div_bX_descX_selectXcX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Project<object>("a","b","c").By(__.BothE().Values<object>("weight").Sum<object>()).By(__.BothE().Count()).By("name").Order().By(__.Math("a / b"),Order.Desc).Select<object>("c")}}, 
+               {"g_V_mathXit_plus_itXbyXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Math("_+_").By("age")}}, 
+               {"g_V_valueMap_mathXit_plus_itXbyXselectXageX_unfoldXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object,object>().Math("_+_").By(__.Select<object>("age").Unfold<object>())}}, 
                {"g_V_age_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Max<object>()}}, 
                {"g_V_foo_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Max<object>()}}, 
                {"g_V_name_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Max<object>()}}, 
@@ -460,7 +469,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_aggregateXaX_byXageX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Max<object>()}}, 
                {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Max<object>()}}, 
                {"g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Max<object>(Scope.Local)}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Max<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXfooX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Max<object>()}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Max<object>()}}, 
                {"g_V_foo_fold_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Max<object>(Scope.Local)}}, 
                {"g_V_name_fold_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Fold().Max<object>(Scope.Local)}}, 
                {"g_V_repeatXbothX_timesX5X_age_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Both()).Times(5).Values<object>("age").Max<object>()}}, 
@@ -471,9 +482,13 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_foo_fold_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Mean<object>(Scope.Local)}}, 
                {"g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Group<object,object>().By("name").By(__.BothE().Values<object>("weight").Mean<object>())}}, 
                {"g_V_aggregateXaX_byXageX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Mean<object>(Scope.Local)}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Mean<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXageX_capXaX_unfold_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Mean<object>()}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Mean<object>()}}, 
                {"g_V_aggregateXaX_byXfooX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Mean<object>(Scope.Local)}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Mean<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXfooX_capXaX_unfold_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Mean<object>()}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Mean<object>()}}, 
                {"g_injectXnull_10_20_nullX_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Mean<object>()}}, 
                {"g_injectXlistXnull_10_20_nullXX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Mean<object>(Scope.Local)}}, 
                {"g_V_age_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Min<object>()}}, 
@@ -481,9 +496,13 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_name_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Min<object>()}}, 
                {"g_V_age_fold_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Fold().Min<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXageX_capXaX_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Min<object>(Scope.Local)}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Min<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXageX_capXaX_unfold_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Min<object>()}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Min<object>()}}, 
                {"g_V_aggregateXaX_byXfooX_capXaX_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Min<object>(Scope.Local)}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Min<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXfooX_capXaX_unfold_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Min<object>()}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Min<object>()}}, 
                {"g_V_foo_fold_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Min<object>(Scope.Local)}}, 
                {"g_V_name_fold_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Fold().Min<object>(Scope.Local)}}, 
                {"g_V_repeatXbothX_timesX5X_age_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Both()).Times(5).Values<object>("age").Min<object>()}}, 
@@ -508,11 +527,16 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_both_hasLabelXpersonX_order_byXage_descX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Both().HasLabel("person").Order().By("age",Order.Desc).Values<object>("name")}}, 
                {"g_V_order_byXoutE_count_descX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Order().By(__.OutE().Count(),Order.Desc)}}, 
                {"g_V_hasLabelXpersonX_order_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Order().By("age")}}, 
+               {"g_V_order_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Order().By("age")}}, 
+               {"g_V_fold_orderXlocalX_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fold().Order(Scope.Local).By("age")}}, 
+               {"g_V_fold_orderXlocalX_byXage_descX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fold().Order(Scope.Local).By("age",Order.Desc)}}, 
                {"g_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Or(__.HasLabel("person"),__.Has("software","name","lop")).Order().By("age")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Or(__.HasLabel("person"),__.Has("software","name","lop")).Order().By("age")}}, 
                {"g_VX1X_hasXlabel_personX_mapXmapXint_ageXX_orderXlocalX_byXvalues_descX_byXkeys_ascX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V((Vertex) p["v1"]).HasLabel("person").Map<object>((IFunction) p["l1"]).Order(Scope.Local).By(Column.Values,Order.Desc).By(Column.Keys,Order.Asc)}}, 
                {"g_V_hasXsong_name_OHBOYX_outXfollowedByX_outXfollowedByX_order_byXperformancesX_byXsongType_descX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("song","name","OH BOY").Out("followedBy").Out("followedBy").Order().By("performances").By("songType",Order.Desc).By("name")}}, 
                {"g_V_hasLabelXsongX_order_byXperformances_descX_byXnameX_rangeX110_120X_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("song").Order().By("performances",Order.Desc).By("name").Range<object>(110,120).Values<object>("name")}}, 
                {"g_VX1X_elementMap_orderXlocalX_byXkeys_descXunfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).ElementMap<object>().Order(Scope.Local).By(Column.Keys,Order.Desc).Unfold<object>()}}, 
+               {"g_VX1X_elementMap_orderXlocalX_byXkeys_ascXunfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).ElementMap<object>().Order(Scope.Local).By(Column.Keys,Order.Asc).Unfold<object>()}}, 
                {"g_V_pageRank_hasXpageRankX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().PageRank().Has("gremlin.pageRankVertexProgram.pageRank")}}, 
                {"g_V_outXcreatedX_pageRank_withXedges_bothEX_withXpropertyName_projectRankX_withXtimes_0X_valueMapXname_projectRankX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out("created").PageRank().With("~tinkerpop.pageRank.edges",__.BothE()).With("~tinkerpop.pageRank.propertyName","projectRank").With("~tinkerpop.pageRank.times",0).ValueMap<object,object>("name","projectRank")}}, 
                {"g_V_pageRank_order_byXpageRank_descX_byXnameX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().PageRank().Order().By("gremlin.pageRankVertexProgram.pageRank",Order.Desc).By("name").Values<object>("name")}}, 
@@ -530,6 +554,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_VX1X_outEXcreatedX_inV_inE_outV_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).OutE("created").InV().InE().OutV().Path()}}, 
                {"g_V_asXaX_out_asXbX_out_asXcX_path_fromXbX_toXcX_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").Out().As("b").Out().As("c").Path().From("b").To("c").By("name")}}, 
                {"g_VX1X_out_path_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Path().By("age")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_VX1X_out_path_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V(p["vid1"]).Out().Path().By("age")}}, 
                {"g_injectX1_null_nullX_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1,null,null).Path()}}, 
                {"g_injectX1_null_nullX_path_dedup", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1,null,null).Path().Dedup()}}, 
                {"g_V_peerPressure_hasXclusterX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().PeerPressure().Has("gremlin.peerPressureVertexProgram.cluster")}}, 
@@ -539,6 +564,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_outXcreatedX_projectXa_bX_byXnameX_byXinXcreatedX_countX_order_byXselectXbX__descX_selectXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out("created").Project<object>("a","b").By("name").By(__.In("created").Count()).Order().By(__.Select<object>("b"),Order.Desc).Select<object>("a")}}, 
                {"g_V_valueMap_projectXxX_byXselectXnameXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object,object>().Project<object>("x").By(__.Select<object>("name"))}}, 
                {"g_V_projectXa_bX_byXinE_countX_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Project<object>("a","b").By(__.InE().Count()).By("age")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_projectXa_bX_byXinE_countX_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Project<object>("a","b").By(__.InE().Count()).By("age")}}, 
                {"g_V_hasXageX_propertiesXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age").Properties<object>("name").Value<object>()}}, 
                {"g_V_hasXageX_propertiesXname_ageX_value", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age").Properties<object>("name","age").Value<object>()}}, 
                {"g_V_hasXageX_propertiesXage_nameX_value", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age").Properties<object>("age","name").Value<object>()}}, 
@@ -607,6 +633,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.E(p["eid11"]).Properties<object>("weight").As("a").Select<object>("a").By(T.Key)}}, 
                {"g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.E(p["eid11"]).Properties<object>("weight").As("a").Select<object>("a").By(T.Value)}}, 
                {"g_V_asXaX_selectXaX_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a").Select<object>("a").By("age")}}, 
+               {"g_V_asXa_nX_selectXa_nX_byXageX_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("a","n").Select<object>("a","n").By("age").By("name")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_asXaX_selectXaX_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().As("a").Select<object>("a").By("age")}}, 
                {"g_withSideEffectXk_nullX_injectXxX_selectXkX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSideEffect("k",null).Inject("x").Select<object>("k")}}, 
                {"g_V_shortestPath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Identity().ShortestPath()}}, 
                {"g_V_both_dedup_shortestPath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Both().Dedup().ShortestPath()}}, 
@@ -629,9 +657,13 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_foo_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Sum<object>(Scope.Local)}}, 
                {"g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_sumX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Group<object,object>().By("name").By(__.BothE().Values<object>("weight").Sum<object>())}}, 
                {"g_V_aggregateXaX_byXageX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Sum<object>(Scope.Local)}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Sum<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXageX_capXaX_unfold_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Sum<object>()}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Sum<object>()}}, 
                {"g_V_aggregateXaX_byXfooX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Sum<object>(Scope.Local)}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Sum<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXfooX_capXaX_unfold_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Sum<object>()}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Sum<object>()}}, 
                {"g_injectXnull_10_5_nullX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Sum<object>()}}, 
                {"g_injectXlistXnull_10_5_nullXX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Sum<object>(Scope.Local)}}, 
                {"g_V_localXoutE_foldX_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.OutE().Fold()).Unfold<object>()}}, 
@@ -650,6 +682,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Filter(__.OutE("created")).ValueMap<object,object>().With("~tinkerpop.valueMap.tokens")}}, 
                {"g_VX1X_valueMapXname_locationX_byXunfoldX_by", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).ValueMap<object,object>("name","location").By(__.Unfold<object>()).By()}}, 
                {"g_V_valueMapXname_age_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object,object>("name","age",null)}}, 
+               {"g_V_valueMapXname_ageX_byXisXxXXbyXunfoldX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object,object>("name","age").By(__.Is("x")).By(__.Unfold<object>())}}, 
                {"g_VXnullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(null)}}, 
                {"g_VXlistXnullXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["xx1"])}}, 
                {"g_VX1_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"],null)}}, 
@@ -711,17 +744,24 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_aggregateXxX_byXnameX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("x").By("name").Cap<object>("x")}}, 
                {"g_V_out_aggregateXaX_path", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out().Aggregate("a").Path()}}, 
                {"g_V_hasLabelXpersonX_aggregateXxX_byXageX_capXxX_asXyX_selectXyX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Aggregate("x").By("age").Cap<object>("x").As("y").Select<object>("y")}}, 
+               {"g_V_aggregateXxX_byXageX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("x").By("age").Cap<object>("x")}}, 
+               {"g_V_aggregateXlocal_xX_byXageX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate(Scope.Local,"x").By("age").Cap<object>("x")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXlocal_xX_byXageX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate(Scope.Local,"x").By("age").Cap<object>("x")}}, 
                {"g_V_aggregateXlocal_a_nameX_out_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate(Scope.Local,"a").By("name").Out().Cap<object>("a")}}, 
                {"g_VX1X_aggregateXlocal_aX_byXnameX_out_aggregateXlocal_aX_byXnameX_name_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Aggregate(Scope.Local,"a").By("name").Out().Aggregate(Scope.Local,"a").By("name").Values<object>("name").Cap<object>("a")}}, 
                {"g_withSideEffectXa_setX_V_both_name_aggregateXlocal_aX_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSideEffect("a",p["xx1"]).V().Both().Values<object>("name").Aggregate(Scope.Local,"a").Cap<object>("a")}}, 
                {"g_V_aggregateXlocal_aX_byXoutEXcreatedX_countX_out_out_aggregateXlocal_aX_byXinEXcreatedX_weight_sumX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate(Scope.Local,"a").By(__.OutE("created").Count()).Out().Out().Aggregate(Scope.Local,"a").By(__.InE("created").Values<object>("weight").Sum<object>()).Cap<object>("a")}}, 
                {"g_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("x").By(__.Values<object>("age").Is(P.Gt(29))).Cap<object>("x")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("x").By(__.Values<object>("age").Is(P.Gt(29))).Cap<object>("x")}}, 
                {"g_V_aggregateXxX_byXout_order_byXnameXX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("x").By(__.Out().Order().By("name")).Cap<object>("x")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXout_order_byXnameXX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("x").By(__.Out().Order().By("name")).Cap<object>("x")}}, 
                {"g_V_fail", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fail()}}, 
                {"g_V_failXmsgX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fail("msg")}}, 
                {"g_V_unionXout_failX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Union<object>(__.Out(),__.Fail())}}, 
                {"g_V_group_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By("name")}}, 
                {"g_V_group_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By("age")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_group_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Group<object,object>().By("age")}}, 
+               {"g_V_group_byXnameX_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By("name").By("age")}}, 
                {"g_V_group_byXnameX_by", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By("name").By()}}, 
                {"g_V_groupXaX_byXnameX_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group("a").By("name").Cap<object>("a")}}, 
                {"g_V_hasXlangX_groupXaX_byXlangX_byXnameX_out_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("lang").Group("a").By("lang").By("name").Out().Cap<object>("a")}}, 
@@ -745,6 +785,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_group_byXlabelX_byXlabel_countX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By(__.Label()).By(__.Label().Count())}}, 
                {"g_V_groupXmX_byXlabelX_byXlabel_countX_capXmX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group("m").By(__.Label()).By(__.Label().Count()).Cap<object>("m")}}, 
                {"g_V_outXcreatedX_groupCount_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out("created").GroupCount<object>().By("name")}}, 
+               {"g_V_groupCount_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().GroupCount<object>().By("age")}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().GroupCount<object>().By("age")}}, 
                {"g_V_outXcreatedX_name_groupCount", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out("created").Values<object>("name").GroupCount<object>()}}, 
                {"g_V_outXcreatedX_groupCountXaX_byXnameX_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out("created").GroupCount("a").By("name").Cap<object>("a")}}, 
                {"g_V_outXcreatedX_name_groupCountXaX_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out("created").Values<object>("name").GroupCount("a").Cap<object>("a")}}, 
@@ -783,6 +825,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_withBulkXfalseX_withSackX1_sumX_VX1X_localXoutEXknowsX_barrierXnormSackX_inVX_inXknowsX_barrier_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithBulk(false).WithSack(1.0,Operator.Sum).V(p["vid1"]).Local<object>(__.OutE("knows").Barrier(Barrier.NormSack).InV()).In("knows").Barrier().Sack<object>()}}, 
                {"g_withBulkXfalseX_withSackX1_sumX_V_out_barrier_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithBulk(false).WithSack(1,Operator.Sum).V().Out().Barrier().Sack<object>()}}, 
                {"g_withSackX1_sumX_VX1X_localXoutXknowsX_barrierXnormSackXX_inXknowsX_barrier_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(1.0,Operator.Sum).V(p["vid1"]).Local<object>(__.Out("knows").Barrier(Barrier.NormSack)).In("knows").Barrier().Sack<object>()}}, 
+               {"g_V_sackXassignX_byXageX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Sack(Operator.Assign).By("age").Sack<object>()}}, 
                {"g_V_hasXageX_groupCountXaX_byXnameX_out_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age").GroupCount("a").By("name").Out().Cap<object>("a")}}, 
                {"g_V_storeXa_nameX_out_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Store("a").By("name").Out().Cap<object>("a")}}, 
                {"g_VX1X_storeXaX_byXnameX_out_storeXaX_byXnameX_name_capXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Store("a").By("name").Out().Store("a").By("name").Values<object>("name").Cap<object>("a")}}, 
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
index 5205f85..90f63ff 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
@@ -68,7 +68,7 @@ const ignoredScenarios = {
   // An associative array containing the scenario name as key, for example:
   'g_withSideEffectXa_setX_V_both_name_storeXaX_capXaX': new IgnoreError(ignoreReason.setNotSupported),
   'g_withSideEffectXa_setX_V_both_name_aggregateXlocal_aX_capXaX': new IgnoreError(ignoreReason.setNotSupported),
-  'g_V_group_byXageX': new IgnoreError(ignoreReason.nullKeysInMapNotSupportedWell),
+  'g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX': new IgnoreError(ignoreReason.nullKeysInMapNotSupportedWell),
   'g_V_shortestPath_edgesIncluded': new IgnoreError(ignoreReason.needsFurtherInvestigation),
   'g_V_shortestPath_edgesIncluded_edgesXoutEX': new IgnoreError(ignoreReason.needsFurtherInvestigation),
   'g_V_shortestpath': new IgnoreError(ignoreReason.needsFurtherInvestigation)
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index d303f97..ea908b0 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -119,6 +119,7 @@ const gremlins = {
     g_V_coinX0X: [function({g}) { return g.V().coin(0.0) }], 
     g_withStrategiesXSeedStrategyX_V_coinX50X: [function({g}) { return g.withStrategies(new SeedStrategy({seed:999999})).V().coin(0.5) }], 
     g_VX1X_outXcreatedX_inXcreatedX_cyclicPath: [function({g, vid1}) { return g.V(vid1).out("created").in_("created").cyclicPath() }], 
+    g_VX1X_both_both_cyclicPath_byXageX: [function({g, vid1}) { return g.V(vid1).both().both().cyclicPath().by("age") }], 
     g_VX1X_outXcreatedX_inXcreatedX_cyclicPath_path: [function({g, vid1}) { return g.V(vid1).out("created").in_("created").cyclicPath().path() }], 
     g_VX1X_asXaX_outXcreatedX_asXbX_inXcreatedX_asXcX_cyclicPath_fromXaX_toXbX_path: [function({g, vid1}) { return g.V(vid1).as("a").out("created").as("b").in_("created").as("c").cyclicPath().from_("a").to("b").path() }], 
     g_injectX0X_V_both_coalesceXhasXname_markoX_both_constantX0XX_cyclicPath_path: [function({g}) { return g.inject(0).V().both().coalesce(__.has("name","marko").both(),__.constant(0)).cyclicPath().path() }], 
@@ -143,6 +144,9 @@ const gremlins = {
     g_V_both_properties_dedup_count: [function({g}) { return g.V().both().properties().dedup().count() }], 
     g_V_both_properties_properties_dedup_count: [function({g}) { return g.V().both().properties().properties().dedup().count() }], 
     g_V_order_byXname_descX_barrier_dedup_age_name: [function({g}) { return g.V().order().by("name",Order.desc).barrier().dedup().by("age").values("name") }], 
+    g_withStrategiesXProductiveByStrategyX_V_order_byXname_descX_barrier_dedup_age_name: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().order().by("name",Order.desc).barrier().dedup().by("age").values("name") }], 
+    g_V_both_dedup_age_name: [function({g}) { return g.V().both().dedup().by("age").values("name") }], 
+    g_V_asXaX_both_asXbX_both_asXcX_dedupXa_bX_age_selectXa_b_cX_name: [function({g}) { return g.V().as("a").both().as("b").both().as("c").dedup("a","b").by("age").select("a","b","c").by("name") }], 
     g_V_drop: [function({g}) { return g.addV().as("a").addV().as("b").addE("knows").to("a") }, function({g}) { return g.V().drop() }, function({g}) { return g.V() }, function({g}) { return g.E() }], 
     g_V_outE_drop: [function({g}) { return g.addV().as("a").addV().as("b").addE("knows").to("a") }, function({g}) { return g.V().outE().drop() }, function({g}) { return g.V() }, function({g}) { return g.E() }], 
     g_V_properties_drop: [function({g}) { return g.addV().property("name","bob").addV().property("name","alice") }, function({g}) { return g.V().properties().drop() }, function({g}) { return g.V() }, function({g}) { return g.V().properties() }], 
@@ -269,10 +273,12 @@ const gremlins = {
     g_V_localXoutE_sampleX1X_byXweightXX: [function({g}) { return g.V().local(__.outE().sample(1).by("weight")) }], 
     g_withStrategiesXSeedStrategyX_V_group_byXlabelX_byXbothE_weight_order_sampleX2X_foldXunfold: [function({g}) { return g.withStrategies(new SeedStrategy({seed:999999})).V().group().by(T.label).by(__.bothE().values("weight").order().sample(2).fold()).unfold() }], 
     g_withStrategiesXSeedStrategyX_V_group_byXlabelX_byXbothE_weight_order_fold_sampleXlocal_5XXunfold: [function({g}) { return g.withStrategies(new SeedStrategy({seed:999999})).V().group().by(T.label).by(__.bothE().values("weight").order().fold().sample(Scope.local,5)).unfold() }], 
+    g_withStrategiesXSeedStrategyX_V_order_byXlabel_descX_sampleX1X_byXageX: [function({g}) { return g.withStrategies(new SeedStrategy({seed:999999})).V().order().by(T.label,Order.desc).sample(1).by("age") }], 
     g_VX1X_outXcreatedX_inXcreatedX_simplePath: [function({g, vid1}) { return g.V(vid1).out("created").in_("created").simplePath() }], 
     g_V_repeatXboth_simplePathX_timesX3X_path: [function({g}) { return g.V().repeat(__.both().simplePath()).times(3).path() }], 
     g_V_asXaX_out_asXbX_out_asXcX_simplePath_byXlabelX_fromXbX_toXcX_path_byXnameX: [function({g}) { return g.V().as("a").out().as("b").out().as("c").simplePath().by(T.label).from_("b").to("c").path().by("name") }], 
     g_injectX0X_V_both_coalesceXhasXname_markoX_both_constantX0XX_simplePath_path: [function({g}) { return g.inject(0).V().both().coalesce(__.has("name","marko").both(),__.constant(0)).simplePath().path() }], 
+    g_V_both_asXaX_both_asXbX_simplePath_path_byXageX__fromXaX_toXbX: [function({g}) { return g.V().both().as("a").both().as("b").simplePath().path().by("age").from_("a").to("b") }], 
     g_V_valuesXnameX_order_tailXglobal_2X: [function({g}) { return g.V().values("name").order().tail(Scope.global,2) }], 
     g_V_valuesXnameX_order_tailX2X: [function({g}) { return g.V().values("name").order().tail(2) }], 
     g_V_valuesXnameX_order_tail: [function({g}) { return g.V().values("name").order().tail() }], 
@@ -311,6 +317,7 @@ const gremlins = {
     g_V_asXaX_outEXcreatedX_asXbX_inV_asXcX_inXcreatedX_asXdX_whereXa_ltXbX_orXgtXcXX_andXneqXdXXX_byXageX_byXweightX_byXinXcreatedX_valuesXageX_minX_selectXa_c_dX: [function({g}) { return g.V().as("a").outE("created").as("b").inV().as("c").in_("created").as("d").where("a",P.lt("b").or(P.gt("c")).and(P.neq("d"))).by("age").by("weight").by(__.in_("created").values("age").min()).select("a","c","d").by("name") }], 
     g_VX1X_asXaX_out_hasXageX_whereXgtXaXX_byXageX_name: [function({g, vid1}) { return g.V(vid1).as("a").out().has("age").where(P.gt("a")).by("age").values("name") }], 
     g_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name: [function({g, vid3}) { return g.V(vid3).as("a").in_().out().as("b").where("a",P.eq("b")).by("age").values("name") }], 
+    g_withStrategiesXProductiveByStrategyX_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name: [function({g, vid3}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V(vid3).as("a").in_().out().as("b").where("a",P.eq("b")).by("age").values("name") }], 
     g_V_coworker: [function({g, xx1}) { return g.V().hasLabel("person").filter(__.outE("created")).aggregate("p").as("p1").values("name").as("p1n").select("p").unfold().where(P.neq("p1")).as("p2").values("name").as("p2n").select("p2").out("created").choose(__.in_("created").where(P.eq("p1")),__.values("name"),__.constant(xx1)).group().by(__.select("p1n")).by(__.group().by(__.select("p2n")).by(__.unfold().fold().project("numCoCreated","coCreated").by(__.count(Scope.local)).by())).unfold() }], 
     g_V_coworker_with_midV: [function({g}) { return g.V().hasLabel("person").filter(__.outE("created")).as("p1").V().hasLabel("person").where(P.neq("p1")).filter(__.outE("created")).as("p2").map(__.out("created").where(__.in_("created").as("p1")).values("name").fold()).group().by(__.select("p1").by("name")).by(__.group().by(__.select("p2").by("name")).by(__.project("numCoCreated","coCreated").by(__.count(Scope.local)).by())).unfold() }], 
     g_V_playlist_paths: [function({g}) { return g.V().has("name","Bob_Dylan").in_("sungBy").order().by("name").as("a").repeat(__.out().order().by("name").simplePath().from_("a")).until(__.out("writtenBy").has("name","Johnny_Cash")).limit(1).as("b").repeat(__.out().order().by("name").as("c").simplePath().from_("b").to("c")).until(__.out("sungBy").has("name","Grateful_Dead")).limit(1).path().from_("a").unfold().project("song","artists").by("name").by(__.coalesce(__.out("sungBy","writtenBy" [...]
@@ -439,14 +446,20 @@ const gremlins = {
     g_V_asXaX_outXcreatedX_asXbX_mathXb_plus_aX_byXinXcreatedX_countX_byXageX: [function({g}) { return g.V().as("a").out("created").as("b").math("b + a").by(__.in_("created").count()).by("age") }], 
     g_withSackX1X_injectX1X_repeatXsackXsumX_byXconstantX1XXX_timesX5X_emit_mathXsin__X_byXsackX: [function({g}) { return g.withSack(1).inject(1).repeat(__.sack(Operator.sum).by(__.constant(1))).times(5).emit().math("sin _").by(__.sack()) }], 
     g_V_projectXa_b_cX_byXbothE_weight_sumX_byXbothE_countX_byXnameX_order_byXmathXa_div_bX_descX_selectXcX: [function({g}) { return g.V().project("a","b","c").by(__.bothE().values("weight").sum()).by(__.bothE().count()).by("name").order().by(__.math("a / b"),Order.desc).select("c") }], 
+    g_V_mathXit_plus_itXbyXageX: [function({g}) { return g.V().math("_+_").by("age") }], 
+    g_V_valueMap_mathXit_plus_itXbyXselectXageX_unfoldXX: [function({g}) { return g.V().valueMap().math("_+_").by(__.select("age").unfold()) }], 
     g_V_age_max: [function({g}) { return g.V().values("age").max() }], 
     g_V_foo_max: [function({g}) { return g.V().values("foo").max() }], 
     g_V_name_max: [function({g}) { return g.V().values("name").max() }], 
     g_V_age_fold_maxXlocalX: [function({g}) { return g.V().values("age").fold().max(Scope.local) }], 
     g_V_aggregateXaX_byXageX_capXaX_maxXlocalX: [function({g}) { return g.V().aggregate("a").by("age").cap("a").max(Scope.local) }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_maxXlocalX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("age").cap("a").max(Scope.local) }], 
     g_V_aggregateXaX_byXageX_capXaX_unfold_max: [function({g}) { return g.V().aggregate("a").by("age").cap("a").unfold().max() }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_max: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("age").cap("a").unfold().max() }], 
     g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").max(Scope.local) }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_maxXlocalX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").max(Scope.local) }], 
     g_V_aggregateXaX_byXfooX_capXaX_unfold_max: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").unfold().max() }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_max: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").unfold().max() }], 
     g_V_foo_fold_maxXlocalX: [function({g}) { return g.V().values("foo").fold().max(Scope.local) }], 
     g_V_name_fold_maxXlocalX: [function({g}) { return g.V().values("name").fold().max(Scope.local) }], 
     g_V_repeatXbothX_timesX5X_age_max: [function({g}) { return g.V().repeat(__.both()).times(5).values("age").max() }], 
@@ -457,9 +470,13 @@ const gremlins = {
     g_V_foo_fold_meanXlocalX: [function({g}) { return g.V().values("foo").fold().mean(Scope.local) }], 
     g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX: [function({g}) { return g.V().hasLabel("software").group().by("name").by(__.bothE().values("weight").mean()) }], 
     g_V_aggregateXaX_byXageX_meanXlocalX: [function({g}) { return g.V().aggregate("a").by("age").cap("a").mean(Scope.local) }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_meanXlocalX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("age").cap("a").mean(Scope.local) }], 
     g_V_aggregateXaX_byXageX_capXaX_unfold_mean: [function({g}) { return g.V().aggregate("a").by("age").cap("a").unfold().mean() }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_mean: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("age").cap("a").unfold().mean() }], 
     g_V_aggregateXaX_byXfooX_meanXlocalX: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").mean(Scope.local) }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_meanXlocalX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").mean(Scope.local) }], 
     g_V_aggregateXaX_byXfooX_capXaX_unfold_mean: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").unfold().mean() }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").unfold().mean() }], 
     g_injectXnull_10_20_nullX_mean: [function({g, xx1, xx2}) { return g.inject(null,xx1,xx2,null).mean() }], 
     g_injectXlistXnull_10_20_nullXX_meanXlocalX: [function({g, xx1}) { return g.inject(xx1).mean(Scope.local) }], 
     g_V_age_min: [function({g}) { return g.V().values("age").min() }], 
@@ -467,9 +484,13 @@ const gremlins = {
     g_V_name_min: [function({g}) { return g.V().values("name").min() }], 
     g_V_age_fold_minXlocalX: [function({g}) { return g.V().values("age").fold().min(Scope.local) }], 
     g_V_aggregateXaX_byXageX_capXaX_minXlocalX: [function({g}) { return g.V().aggregate("a").by("age").cap("a").min(Scope.local) }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_minXlocalX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("age").cap("a").min(Scope.local) }], 
     g_V_aggregateXaX_byXageX_capXaX_unfold_min: [function({g}) { return g.V().aggregate("a").by("age").cap("a").unfold().min() }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_min: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("age").cap("a").unfold().min() }], 
     g_V_aggregateXaX_byXfooX_capXaX_minXlocalX: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").min(Scope.local) }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_minXlocalX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").min(Scope.local) }], 
     g_V_aggregateXaX_byXfooX_capXaX_unfold_min: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").unfold().min() }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_min: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").unfold().min() }], 
     g_V_foo_fold_minXlocalX: [function({g}) { return g.V().values("foo").fold().min(Scope.local) }], 
     g_V_name_fold_minXlocalX: [function({g}) { return g.V().values("name").fold().min(Scope.local) }], 
     g_V_repeatXbothX_timesX5X_age_min: [function({g}) { return g.V().repeat(__.both()).times(5).values("age").min() }], 
@@ -494,11 +515,14 @@ const gremlins = {
     g_V_both_hasLabelXpersonX_order_byXage_descX_name: [function({g}) { return g.V().both().hasLabel("person").order().by("age",Order.desc).values("name") }], 
     g_V_order_byXoutE_count_descX: [function({g}) { return g.V().order().by(__.outE().count(),Order.desc) }], 
     g_V_hasLabelXpersonX_order_byXageX: [function({g}) { return g.V().hasLabel("person").order().by("age") }], 
+    g_V_order_byXageX: [function({g}) { return g.V().order().by("age") }], 
+    g_V_fold_orderXlocalX_byXageX: [function({g}) { return g.V().fold().order(Scope.local).by("age") }, function({g}) { return g.V().fold().order(Scope.local).by("age",Order.desc) }], 
     g_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX: [function({g}) { return g.V().or(__.hasLabel("person"),__.has("software","name","lop")).order().by("age") }], 
+    g_withStrategiesXProductiveByStrategyX_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().or(__.hasLabel("person"),__.has("software","name","lop")).order().by("age") }], 
     g_VX1X_hasXlabel_personX_mapXmapXint_ageXX_orderXlocalX_byXvalues_descX_byXkeys_ascX: [function({g, l1, v1}) { return g.V(v1).hasLabel("person").map(l1).order(Scope.local).by(Column.values,Order.desc).by(Column.keys,Order.asc) }], 
     g_V_hasXsong_name_OHBOYX_outXfollowedByX_outXfollowedByX_order_byXperformancesX_byXsongType_descX: [function({g}) { return g.V().has("song","name","OH BOY").out("followedBy").out("followedBy").order().by("performances").by("songType",Order.desc).by("name") }], 
     g_V_hasLabelXsongX_order_byXperformances_descX_byXnameX_rangeX110_120X_name: [function({g}) { return g.V().hasLabel("song").order().by("performances",Order.desc).by("name").range(110,120).values("name") }], 
-    g_VX1X_elementMap_orderXlocalX_byXkeys_descXunfold: [function({g, vid1}) { return g.V(vid1).elementMap().order(Scope.local).by(Column.keys,Order.desc).unfold() }], 
+    g_VX1X_elementMap_orderXlocalX_byXkeys_descXunfold: [function({g, vid1}) { return g.V(vid1).elementMap().order(Scope.local).by(Column.keys,Order.desc).unfold() }, function({g, vid1}) { return g.V(vid1).elementMap().order(Scope.local).by(Column.keys,Order.asc).unfold() }], 
     g_V_pageRank_hasXpageRankX: [function({g}) { return g.V().pageRank().has("gremlin.pageRankVertexProgram.pageRank") }], 
     g_V_outXcreatedX_pageRank_withXedges_bothEX_withXpropertyName_projectRankX_withXtimes_0X_valueMapXname_projectRankX: [function({g}) { return g.V().out("created").pageRank().with_("~tinkerpop.pageRank.edges",__.bothE()).with_("~tinkerpop.pageRank.propertyName","projectRank").with_("~tinkerpop.pageRank.times",0).valueMap("name","projectRank") }], 
     g_V_pageRank_order_byXpageRank_descX_byXnameX_name: [function({g}) { return g.V().pageRank().order().by("gremlin.pageRankVertexProgram.pageRank",Order.desc).by("name").values("name") }], 
@@ -516,6 +540,7 @@ const gremlins = {
     g_VX1X_outEXcreatedX_inV_inE_outV_path: [function({g, vid1}) { return g.V(vid1).outE("created").inV().inE().outV().path() }], 
     g_V_asXaX_out_asXbX_out_asXcX_path_fromXbX_toXcX_byXnameX: [function({g}) { return g.V().as("a").out().as("b").out().as("c").path().from_("b").to("c").by("name") }], 
     g_VX1X_out_path_byXageX: [function({g, vid1}) { return g.V(vid1).out().path().by("age") }], 
+    g_withStrategiesXProductiveByStrategyX_VX1X_out_path_byXageX: [function({g, vid1}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V(vid1).out().path().by("age") }], 
     g_injectX1_null_nullX_path: [function({g}) { return g.inject(1,null,null).path() }], 
     g_injectX1_null_nullX_path_dedup: [function({g}) { return g.inject(1,null,null).path().dedup() }], 
     g_V_peerPressure_hasXclusterX: [function({g}) { return g.V().peerPressure().has("gremlin.peerPressureVertexProgram.cluster") }], 
@@ -525,6 +550,7 @@ const gremlins = {
     g_V_outXcreatedX_projectXa_bX_byXnameX_byXinXcreatedX_countX_order_byXselectXbX__descX_selectXaX: [function({g}) { return g.V().out("created").project("a","b").by("name").by(__.in_("created").count()).order().by(__.select("b"),Order.desc).select("a") }], 
     g_V_valueMap_projectXxX_byXselectXnameXX: [function({g}) { return g.V().valueMap().project("x").by(__.select("name")) }], 
     g_V_projectXa_bX_byXinE_countX_byXageX: [function({g}) { return g.V().project("a","b").by(__.inE().count()).by("age") }], 
+    g_withStrategiesXProductiveByStrategyX_V_projectXa_bX_byXinE_countX_byXageX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().project("a","b").by(__.inE().count()).by("age") }], 
     g_V_hasXageX_propertiesXnameX: [function({g}) { return g.V().has("age").properties("name").value() }], 
     g_V_hasXageX_propertiesXname_ageX_value: [function({g}) { return g.V().has("age").properties("name","age").value() }], 
     g_V_hasXageX_propertiesXage_nameX_value: [function({g}) { return g.V().has("age").properties("age","name").value() }], 
@@ -593,6 +619,8 @@ const gremlins = {
     g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX: [function({g, eid11}) { return g.E(eid11).properties("weight").as("a").select("a").by(T.key) }], 
     g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX: [function({g, eid11}) { return g.E(eid11).properties("weight").as("a").select("a").by(T.value) }], 
     g_V_asXaX_selectXaX_byXageX: [function({g}) { return g.V().as("a").select("a").by("age") }], 
+    g_V_asXa_nX_selectXa_nX_byXageX_byXnameX: [function({g}) { return g.V().as("a","n").select("a","n").by("age").by("name") }], 
+    g_withStrategiesXProductiveByStrategyX_V_asXaX_selectXaX_byXageX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().as("a").select("a").by("age") }], 
     g_withSideEffectXk_nullX_injectXxX_selectXkX: [function({g}) { return g.withSideEffect("k",null).inject("x").select("k") }], 
     g_V_shortestPath: [function({g}) { return g.V().identity().shortestPath() }], 
     g_V_both_dedup_shortestPath: [function({g}) { return g.V().both().dedup().shortestPath() }], 
@@ -615,9 +643,13 @@ const gremlins = {
     g_V_foo_fold_sumXlocalX: [function({g}) { return g.V().values("foo").fold().sum(Scope.local) }], 
     g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_sumX: [function({g}) { return g.V().hasLabel("software").group().by("name").by(__.bothE().values("weight").sum()) }], 
     g_V_aggregateXaX_byXageX_sumXlocalX: [function({g}) { return g.V().aggregate("a").by("age").cap("a").sum(Scope.local) }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_sumXlocalX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("age").cap("a").sum(Scope.local) }], 
     g_V_aggregateXaX_byXageX_capXaX_unfold_sum: [function({g}) { return g.V().aggregate("a").by("age").cap("a").unfold().sum() }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_sum: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("age").cap("a").unfold().sum() }], 
     g_V_aggregateXaX_byXfooX_sumXlocalX: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").sum(Scope.local) }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_sumXlocalX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").sum(Scope.local) }], 
     g_V_aggregateXaX_byXfooX_capXaX_unfold_sum: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").unfold().sum() }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_sum: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("a").by("foo").cap("a").unfold().sum() }], 
     g_injectXnull_10_5_nullX_sum: [function({g, xx1, xx2}) { return g.inject(null,xx1,xx2,null).sum() }], 
     g_injectXlistXnull_10_5_nullXX_sumXlocalX: [function({g, xx1}) { return g.inject(xx1).sum(Scope.local) }], 
     g_V_localXoutE_foldX_unfold: [function({g}) { return g.V().local(__.outE().fold()).unfold() }], 
@@ -636,6 +668,7 @@ const gremlins = {
     g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX: [function({g}) { return g.V().hasLabel("person").filter(__.outE("created")).valueMap().with_("~tinkerpop.valueMap.tokens") }], 
     g_VX1X_valueMapXname_locationX_byXunfoldX_by: [function({g, vid1}) { return g.V(vid1).valueMap("name","location").by(__.unfold()).by() }], 
     g_V_valueMapXname_age_nullX: [function({g}) { return g.V().valueMap("name","age",null) }], 
+    g_V_valueMapXname_ageX_byXisXxXXbyXunfoldX: [function({g}) { return g.V().valueMap("name","age").by(__.is("x")).by(__.unfold()) }], 
     g_VXnullX: [function({g}) { return g.V(null) }], 
     g_VXlistXnullXX: [function({g, xx1}) { return g.V(xx1) }], 
     g_VX1_nullX: [function({g, vid1}) { return g.V(vid1,null) }], 
@@ -697,17 +730,24 @@ const gremlins = {
     g_V_aggregateXxX_byXnameX_capXxX: [function({g}) { return g.V().aggregate("x").by("name").cap("x") }], 
     g_V_out_aggregateXaX_path: [function({g}) { return g.V().out().aggregate("a").path() }], 
     g_V_hasLabelXpersonX_aggregateXxX_byXageX_capXxX_asXyX_selectXyX: [function({g}) { return g.V().hasLabel("person").aggregate("x").by("age").cap("x").as("y").select("y") }], 
+    g_V_aggregateXxX_byXageX_capXxX: [function({g}) { return g.V().aggregate("x").by("age").cap("x") }], 
+    g_V_aggregateXlocal_xX_byXageX_capXxX: [function({g}) { return g.V().aggregate(Scope.local,"x").by("age").cap("x") }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXlocal_xX_byXageX_capXxX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate(Scope.local,"x").by("age").cap("x") }], 
     g_V_aggregateXlocal_a_nameX_out_capXaX: [function({g}) { return g.V().aggregate(Scope.local,"a").by("name").out().cap("a") }], 
     g_VX1X_aggregateXlocal_aX_byXnameX_out_aggregateXlocal_aX_byXnameX_name_capXaX: [function({g, vid1}) { return g.V(vid1).aggregate(Scope.local,"a").by("name").out().aggregate(Scope.local,"a").by("name").values("name").cap("a") }], 
     g_withSideEffectXa_setX_V_both_name_aggregateXlocal_aX_capXaX: [function({g, xx1}) { return g.withSideEffect("a",xx1).V().both().values("name").aggregate(Scope.local,"a").cap("a") }], 
     g_V_aggregateXlocal_aX_byXoutEXcreatedX_countX_out_out_aggregateXlocal_aX_byXinEXcreatedX_weight_sumX: [function({g}) { return g.V().aggregate(Scope.local,"a").by(__.outE("created").count()).out().out().aggregate(Scope.local,"a").by(__.inE("created").values("weight").sum()).cap("a") }], 
     g_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX: [function({g}) { return g.V().aggregate("x").by(__.values("age").is(P.gt(29))).cap("x") }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("x").by(__.values("age").is(P.gt(29))).cap("x") }], 
     g_V_aggregateXxX_byXout_order_byXnameXX_capXxX: [function({g}) { return g.V().aggregate("x").by(__.out().order().by("name")).cap("x") }], 
+    g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXout_order_byXnameXX_capXxX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().aggregate("x").by(__.out().order().by("name")).cap("x") }], 
     g_V_fail: [function({g}) { return g.V().fail() }], 
     g_V_failXmsgX: [function({g}) { return g.V().fail("msg") }], 
     g_V_unionXout_failX: [function({g}) { return g.V().union(__.out(),__.fail()) }], 
     g_V_group_byXnameX: [function({g}) { return g.V().group().by("name") }], 
     g_V_group_byXageX: [function({g}) { return g.V().group().by("age") }], 
+    g_withStrategiesXProductiveByStrategyX_V_group_byXageX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().group().by("age") }], 
+    g_V_group_byXnameX_byXageX: [function({g}) { return g.V().group().by("name").by("age") }], 
     g_V_group_byXnameX_by: [function({g}) { return g.V().group().by("name").by() }], 
     g_V_groupXaX_byXnameX_capXaX: [function({g}) { return g.V().group("a").by("name").cap("a") }], 
     g_V_hasXlangX_groupXaX_byXlangX_byXnameX_out_capXaX: [function({g}) { return g.V().has("lang").group("a").by("lang").by("name").out().cap("a") }], 
@@ -731,6 +771,8 @@ const gremlins = {
     g_V_group_byXlabelX_byXlabel_countX: [function({g}) { return g.V().group().by(__.label()).by(__.label().count()) }], 
     g_V_groupXmX_byXlabelX_byXlabel_countX_capXmX: [function({g}) { return g.V().group("m").by(__.label()).by(__.label().count()).cap("m") }], 
     g_V_outXcreatedX_groupCount_byXnameX: [function({g}) { return g.V().out("created").groupCount().by("name") }], 
+    g_V_groupCount_byXageX: [function({g}) { return g.V().groupCount().by("age") }], 
+    g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX: [function({g}) { return g.withStrategies(new ProductiveByStrategy({productiveKeys:[]})).V().groupCount().by("age") }], 
     g_V_outXcreatedX_name_groupCount: [function({g}) { return g.V().out("created").values("name").groupCount() }], 
     g_V_outXcreatedX_groupCountXaX_byXnameX_capXaX: [function({g}) { return g.V().out("created").groupCount("a").by("name").cap("a") }], 
     g_V_outXcreatedX_name_groupCountXaX_capXaX: [function({g}) { return g.V().out("created").values("name").groupCount("a").cap("a") }], 
@@ -769,6 +811,7 @@ const gremlins = {
     g_withBulkXfalseX_withSackX1_sumX_VX1X_localXoutEXknowsX_barrierXnormSackX_inVX_inXknowsX_barrier_sack: [function({g, vid1}) { return g.withBulk(false).withSack(1.0,Operator.sum).V(vid1).local(__.outE("knows").barrier(Barrier.normSack).inV()).in_("knows").barrier().sack() }], 
     g_withBulkXfalseX_withSackX1_sumX_V_out_barrier_sack: [function({g}) { return g.withBulk(false).withSack(1,Operator.sum).V().out().barrier().sack() }], 
     g_withSackX1_sumX_VX1X_localXoutXknowsX_barrierXnormSackXX_inXknowsX_barrier_sack: [function({g, vid1}) { return g.withSack(1.0,Operator.sum).V(vid1).local(__.out("knows").barrier(Barrier.normSack)).in_("knows").barrier().sack() }], 
+    g_V_sackXassignX_byXageX_sack: [function({g}) { return g.V().sack(Operator.assign).by("age").sack() }], 
     g_V_hasXageX_groupCountXaX_byXnameX_out_capXaX: [function({g}) { return g.V().has("age").groupCount("a").by("name").out().cap("a") }], 
     g_V_storeXa_nameX_out_capXaX: [function({g}) { return g.V().store("a").by("name").out().cap("a") }], 
     g_VX1X_storeXaX_byXnameX_out_storeXaX_byXnameX_name_capXaX: [function({g, vid1}) { return g.V(vid1).store("a").by("name").out().store("a").by("name").values("name").cap("a") }], 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 13900d6..5e705c6 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -104,6 +104,7 @@ world.gremlins = {
     'g_V_coinX0X': [(lambda g:g.V().coin(float(0.0)))], 
     'g_withStrategiesXSeedStrategyX_V_coinX50X': [(lambda g:g.withStrategies(*[TraversalStrategy('SeedStrategy',{'seed':999999,'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy')]).V().coin(float(0.5)))], 
     'g_VX1X_outXcreatedX_inXcreatedX_cyclicPath': [(lambda g, vid1=None:g.V(vid1).out('created').in_('created').cyclicPath())], 
+    'g_VX1X_both_both_cyclicPath_byXageX': [(lambda g, vid1=None:g.V(vid1).both().both().cyclicPath().by('age'))], 
     'g_VX1X_outXcreatedX_inXcreatedX_cyclicPath_path': [(lambda g, vid1=None:g.V(vid1).out('created').in_('created').cyclicPath().path())], 
     'g_VX1X_asXaX_outXcreatedX_asXbX_inXcreatedX_asXcX_cyclicPath_fromXaX_toXbX_path': [(lambda g, vid1=None:g.V(vid1).as_('a').out('created').as_('b').in_('created').as_('c').cyclicPath().from_('a').to('b').path())], 
     'g_injectX0X_V_both_coalesceXhasXname_markoX_both_constantX0XX_cyclicPath_path': [(lambda g:g.inject(0).V().both().coalesce(__.has('name','marko').both(),__.constant(0)).cyclicPath().path())], 
@@ -128,6 +129,9 @@ world.gremlins = {
     'g_V_both_properties_dedup_count': [(lambda g:g.V().both().properties().dedup().count())], 
     'g_V_both_properties_properties_dedup_count': [(lambda g:g.V().both().properties().properties().dedup().count())], 
     'g_V_order_byXname_descX_barrier_dedup_age_name': [(lambda g:g.V().order().by('name',Order.desc).barrier().dedup().by('age').name)], 
+    'g_withStrategiesXProductiveByStrategyX_V_order_byXname_descX_barrier_dedup_age_name': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().order().by('name',Order.desc).barrier().dedup().by('age').name)], 
+    'g_V_both_dedup_age_name': [(lambda g:g.V().both().dedup().by('age').name)], 
+    'g_V_asXaX_both_asXbX_both_asXcX_dedupXa_bX_age_selectXa_b_cX_name': [(lambda g:g.V().as_('a').both().as_('b').both().as_('c').dedup('a','b').by('age').select('a','b','c').by('name'))], 
     'g_V_drop': [(lambda g:g.addV().as_('a').addV().as_('b').addE('knows').to('a')), (lambda g:g.V().drop()), (lambda g:g.V()), (lambda g:g.E())], 
     'g_V_outE_drop': [(lambda g:g.addV().as_('a').addV().as_('b').addE('knows').to('a')), (lambda g:g.V().outE().drop()), (lambda g:g.V()), (lambda g:g.E())], 
     'g_V_properties_drop': [(lambda g:g.addV().property('name','bob').addV().property('name','alice')), (lambda g:g.V().properties().drop()), (lambda g:g.V()), (lambda g:g.V().properties())], 
@@ -254,10 +258,12 @@ world.gremlins = {
     'g_V_localXoutE_sampleX1X_byXweightXX': [(lambda g:g.V().local(__.outE().sample(1).by('weight')))], 
     'g_withStrategiesXSeedStrategyX_V_group_byXlabelX_byXbothE_weight_order_sampleX2X_foldXunfold': [(lambda g:g.withStrategies(*[TraversalStrategy('SeedStrategy',{'seed':999999,'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy')]).V().group().by(T.label).by(__.bothE().weight.order().sample(2).fold()).unfold())], 
     'g_withStrategiesXSeedStrategyX_V_group_byXlabelX_byXbothE_weight_order_fold_sampleXlocal_5XXunfold': [(lambda g:g.withStrategies(*[TraversalStrategy('SeedStrategy',{'seed':999999,'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy')]).V().group().by(T.label).by(__.bothE().weight.order().fold().sample(Scope.local,5)).unfold())], 
+    'g_withStrategiesXSeedStrategyX_V_order_byXlabel_descX_sampleX1X_byXageX': [(lambda g:g.withStrategies(*[TraversalStrategy('SeedStrategy',{'seed':999999,'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy')]).V().order().by(T.label,Order.desc).sample(1).by('age'))], 
     'g_VX1X_outXcreatedX_inXcreatedX_simplePath': [(lambda g, vid1=None:g.V(vid1).out('created').in_('created').simplePath())], 
     'g_V_repeatXboth_simplePathX_timesX3X_path': [(lambda g:g.V().repeat(__.both().simplePath()).times(3).path())], 
     'g_V_asXaX_out_asXbX_out_asXcX_simplePath_byXlabelX_fromXbX_toXcX_path_byXnameX': [(lambda g:g.V().as_('a').out().as_('b').out().as_('c').simplePath().by(T.label).from_('b').to('c').path().by('name'))], 
     'g_injectX0X_V_both_coalesceXhasXname_markoX_both_constantX0XX_simplePath_path': [(lambda g:g.inject(0).V().both().coalesce(__.has('name','marko').both(),__.constant(0)).simplePath().path())], 
+    'g_V_both_asXaX_both_asXbX_simplePath_path_byXageX__fromXaX_toXbX': [(lambda g:g.V().both().as_('a').both().as_('b').simplePath().path().by('age').from_('a').to('b'))], 
     'g_V_valuesXnameX_order_tailXglobal_2X': [(lambda g:g.V().name.order().tail(Scope.global_,2))], 
     'g_V_valuesXnameX_order_tailX2X': [(lambda g:g.V().name.order().tail(2))], 
     'g_V_valuesXnameX_order_tail': [(lambda g:g.V().name.order().tail())], 
@@ -296,6 +302,7 @@ world.gremlins = {
     'g_V_asXaX_outEXcreatedX_asXbX_inV_asXcX_inXcreatedX_asXdX_whereXa_ltXbX_orXgtXcXX_andXneqXdXXX_byXageX_byXweightX_byXinXcreatedX_valuesXageX_minX_selectXa_c_dX': [(lambda g:g.V().as_('a').outE('created').as_('b').inV().as_('c').in_('created').as_('d').where('a',P.lt('b').or_(P.gt('c')).and_(P.neq('d'))).by('age').by('weight').by(__.in_('created').age.min_()).select('a','c','d').by('name'))], 
     'g_VX1X_asXaX_out_hasXageX_whereXgtXaXX_byXageX_name': [(lambda g, vid1=None:g.V(vid1).as_('a').out().has('age').where(P.gt('a')).by('age').name)], 
     'g_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name': [(lambda g, vid3=None:g.V(vid3).as_('a').in_().out().as_('b').where('a',P.eq('b')).by('age').name)], 
+    'g_withStrategiesXProductiveByStrategyX_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name': [(lambda g, vid3=None:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V(vid3).as_('a').in_().out().as_('b').where('a',P.eq('b')).by('age').name)], 
     'g_V_coworker': [(lambda g, xx1=None:g.V().hasLabel('person').filter_(__.outE('created')).aggregate('p').as_('p1').name.as_('p1n').select('p').unfold().where(P.neq('p1')).as_('p2').name.as_('p2n').select('p2').out('created').choose(__.in_('created').where(P.eq('p1')),__.name,__.constant(xx1)).group().by(__.select('p1n')).by(__.group().by(__.select('p2n')).by(__.unfold().fold().project('numCoCreated','coCreated').by(__.count(Scope.local)).by())).unfold())], 
     'g_V_coworker_with_midV': [(lambda g:g.V().hasLabel('person').filter_(__.outE('created')).as_('p1').V().hasLabel('person').where(P.neq('p1')).filter_(__.outE('created')).as_('p2').map(__.out('created').where(__.in_('created').as_('p1')).name.fold()).group().by(__.select('p1').by('name')).by(__.group().by(__.select('p2').by('name')).by(__.project('numCoCreated','coCreated').by(__.count(Scope.local)).by())).unfold())], 
     'g_V_playlist_paths': [(lambda g:g.V().has('name','Bob_Dylan').in_('sungBy').order().by('name').as_('a').repeat(__.out().order().by('name').simplePath().from_('a')).until(__.out('writtenBy').has('name','Johnny_Cash'))[0:1].as_('b').repeat(__.out().order().by('name').as_('c').simplePath().from_('b').to('c')).until(__.out('sungBy').has('name','Grateful_Dead'))[0:1].path().from_('a').unfold().project('song','artists').by('name').by(__.coalesce(__.out('sungBy','writtenBy').dedup().name.o [...]
@@ -424,14 +431,20 @@ world.gremlins = {
     'g_V_asXaX_outXcreatedX_asXbX_mathXb_plus_aX_byXinXcreatedX_countX_byXageX': [(lambda g:g.V().as_('a').out('created').as_('b').math('b + a').by(__.in_('created').count()).by('age'))], 
     'g_withSackX1X_injectX1X_repeatXsackXsumX_byXconstantX1XXX_timesX5X_emit_mathXsin__X_byXsackX': [(lambda g:g.withSack(1).inject(1).repeat(__.sack(Operator.sum_).by(__.constant(1))).times(5).emit().math('sin _').by(__.sack()))], 
     'g_V_projectXa_b_cX_byXbothE_weight_sumX_byXbothE_countX_byXnameX_order_byXmathXa_div_bX_descX_selectXcX': [(lambda g:g.V().project('a','b','c').by(__.bothE().weight.sum_()).by(__.bothE().count()).by('name').order().by(__.math('a / b'),Order.desc).select('c'))], 
+    'g_V_mathXit_plus_itXbyXageX': [(lambda g:g.V().math('_+_').by('age'))], 
+    'g_V_valueMap_mathXit_plus_itXbyXselectXageX_unfoldXX': [(lambda g:g.V().valueMap().math('_+_').by(__.select('age').unfold()))], 
     'g_V_age_max': [(lambda g:g.V().age.max_())], 
     'g_V_foo_max': [(lambda g:g.V().foo.max_())], 
     'g_V_name_max': [(lambda g:g.V().name.max_())], 
     'g_V_age_fold_maxXlocalX': [(lambda g:g.V().age.fold().max_(Scope.local))], 
     'g_V_aggregateXaX_byXageX_capXaX_maxXlocalX': [(lambda g:g.V().aggregate('a').by('age').cap('a').max_(Scope.local))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_maxXlocalX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('age').cap('a').max_(Scope.local))], 
     'g_V_aggregateXaX_byXageX_capXaX_unfold_max': [(lambda g:g.V().aggregate('a').by('age').cap('a').unfold().max_())], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_max': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('age').cap('a').unfold().max_())], 
     'g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX': [(lambda g:g.V().aggregate('a').by('foo').cap('a').max_(Scope.local))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_maxXlocalX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').max_(Scope.local))], 
     'g_V_aggregateXaX_byXfooX_capXaX_unfold_max': [(lambda g:g.V().aggregate('a').by('foo').cap('a').unfold().max_())], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_max': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').unfold().max_())], 
     'g_V_foo_fold_maxXlocalX': [(lambda g:g.V().foo.fold().max_(Scope.local))], 
     'g_V_name_fold_maxXlocalX': [(lambda g:g.V().name.fold().max_(Scope.local))], 
     'g_V_repeatXbothX_timesX5X_age_max': [(lambda g:g.V().repeat(__.both()).times(5).age.max_())], 
@@ -442,9 +455,13 @@ world.gremlins = {
     'g_V_foo_fold_meanXlocalX': [(lambda g:g.V().foo.fold().mean(Scope.local))], 
     'g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX': [(lambda g:g.V().hasLabel('software').group().by('name').by(__.bothE().weight.mean()))], 
     'g_V_aggregateXaX_byXageX_meanXlocalX': [(lambda g:g.V().aggregate('a').by('age').cap('a').mean(Scope.local))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_meanXlocalX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('age').cap('a').mean(Scope.local))], 
     'g_V_aggregateXaX_byXageX_capXaX_unfold_mean': [(lambda g:g.V().aggregate('a').by('age').cap('a').unfold().mean())], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_mean': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('age').cap('a').unfold().mean())], 
     'g_V_aggregateXaX_byXfooX_meanXlocalX': [(lambda g:g.V().aggregate('a').by('foo').cap('a').mean(Scope.local))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_meanXlocalX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').mean(Scope.local))], 
     'g_V_aggregateXaX_byXfooX_capXaX_unfold_mean': [(lambda g:g.V().aggregate('a').by('foo').cap('a').unfold().mean())], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').unfold().mean())], 
     'g_injectXnull_10_20_nullX_mean': [(lambda g, xx1=None,xx2=None:g.inject(None,xx1,xx2,None).mean())], 
     'g_injectXlistXnull_10_20_nullXX_meanXlocalX': [(lambda g, xx1=None:g.inject(xx1).mean(Scope.local))], 
     'g_V_age_min': [(lambda g:g.V().age.min_())], 
@@ -452,9 +469,13 @@ world.gremlins = {
     'g_V_name_min': [(lambda g:g.V().name.min_())], 
     'g_V_age_fold_minXlocalX': [(lambda g:g.V().age.fold().min_(Scope.local))], 
     'g_V_aggregateXaX_byXageX_capXaX_minXlocalX': [(lambda g:g.V().aggregate('a').by('age').cap('a').min_(Scope.local))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_minXlocalX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('age').cap('a').min_(Scope.local))], 
     'g_V_aggregateXaX_byXageX_capXaX_unfold_min': [(lambda g:g.V().aggregate('a').by('age').cap('a').unfold().min_())], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_min': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('age').cap('a').unfold().min_())], 
     'g_V_aggregateXaX_byXfooX_capXaX_minXlocalX': [(lambda g:g.V().aggregate('a').by('foo').cap('a').min_(Scope.local))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_minXlocalX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').min_(Scope.local))], 
     'g_V_aggregateXaX_byXfooX_capXaX_unfold_min': [(lambda g:g.V().aggregate('a').by('foo').cap('a').unfold().min_())], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_min': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').unfold().min_())], 
     'g_V_foo_fold_minXlocalX': [(lambda g:g.V().foo.fold().min_(Scope.local))], 
     'g_V_name_fold_minXlocalX': [(lambda g:g.V().name.fold().min_(Scope.local))], 
     'g_V_repeatXbothX_timesX5X_age_min': [(lambda g:g.V().repeat(__.both()).times(5).age.min_())], 
@@ -479,11 +500,16 @@ world.gremlins = {
     'g_V_both_hasLabelXpersonX_order_byXage_descX_name': [(lambda g:g.V().both().hasLabel('person').order().by('age',Order.desc).name)], 
     'g_V_order_byXoutE_count_descX': [(lambda g:g.V().order().by(__.outE().count(),Order.desc))], 
     'g_V_hasLabelXpersonX_order_byXageX': [(lambda g:g.V().hasLabel('person').order().by('age'))], 
+    'g_V_order_byXageX': [(lambda g:g.V().order().by('age'))], 
+    'g_V_fold_orderXlocalX_byXageX': [(lambda g:g.V().fold().order(Scope.local).by('age'))], 
+    'g_V_fold_orderXlocalX_byXage_descX': [(lambda g:g.V().fold().order(Scope.local).by('age',Order.desc))], 
     'g_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX': [(lambda g:g.V().or_(__.hasLabel('person'),__.has('software','name','lop')).order().by('age'))], 
+    'g_withStrategiesXProductiveByStrategyX_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().or_(__.hasLabel('person'),__.has('software','name','lop')).order().by('age'))], 
     'g_VX1X_hasXlabel_personX_mapXmapXint_ageXX_orderXlocalX_byXvalues_descX_byXkeys_ascX': [(lambda g, l1=None,v1=None:g.V(v1).hasLabel('person').map(l1).order(Scope.local).by(Column.values,Order.desc).by(Column.keys,Order.asc))], 
     'g_V_hasXsong_name_OHBOYX_outXfollowedByX_outXfollowedByX_order_byXperformancesX_byXsongType_descX': [(lambda g:g.V().has('song','name','OH BOY').out('followedBy').out('followedBy').order().by('performances').by('songType',Order.desc).by('name'))], 
     'g_V_hasLabelXsongX_order_byXperformances_descX_byXnameX_rangeX110_120X_name': [(lambda g:g.V().hasLabel('song').order().by('performances',Order.desc).by('name')[110:120].name)], 
     'g_VX1X_elementMap_orderXlocalX_byXkeys_descXunfold': [(lambda g, vid1=None:g.V(vid1).elementMap().order(Scope.local).by(Column.keys,Order.desc).unfold())], 
+    'g_VX1X_elementMap_orderXlocalX_byXkeys_ascXunfold': [(lambda g, vid1=None:g.V(vid1).elementMap().order(Scope.local).by(Column.keys,Order.asc).unfold())], 
     'g_V_pageRank_hasXpageRankX': [(lambda g:g.V().pageRank().has('gremlin.pageRankVertexProgram.pageRank'))], 
     'g_V_outXcreatedX_pageRank_withXedges_bothEX_withXpropertyName_projectRankX_withXtimes_0X_valueMapXname_projectRankX': [(lambda g:g.V().out('created').pageRank().with_('~tinkerpop.pageRank.edges',__.bothE()).with_('~tinkerpop.pageRank.propertyName','projectRank').with_('~tinkerpop.pageRank.times',0).valueMap('name','projectRank'))], 
     'g_V_pageRank_order_byXpageRank_descX_byXnameX_name': [(lambda g:g.V().pageRank().order().by('gremlin.pageRankVertexProgram.pageRank',Order.desc).by('name').name)], 
@@ -501,6 +527,7 @@ world.gremlins = {
     'g_VX1X_outEXcreatedX_inV_inE_outV_path': [(lambda g, vid1=None:g.V(vid1).outE('created').inV().inE().outV().path())], 
     'g_V_asXaX_out_asXbX_out_asXcX_path_fromXbX_toXcX_byXnameX': [(lambda g:g.V().as_('a').out().as_('b').out().as_('c').path().from_('b').to('c').by('name'))], 
     'g_VX1X_out_path_byXageX': [(lambda g, vid1=None:g.V(vid1).out().path().by('age'))], 
+    'g_withStrategiesXProductiveByStrategyX_VX1X_out_path_byXageX': [(lambda g, vid1=None:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V(vid1).out().path().by('age'))], 
     'g_injectX1_null_nullX_path': [(lambda g:g.inject(1,None,None).path())], 
     'g_injectX1_null_nullX_path_dedup': [(lambda g:g.inject(1,None,None).path().dedup())], 
     'g_V_peerPressure_hasXclusterX': [(lambda g:g.V().peerPressure().has('gremlin.peerPressureVertexProgram.cluster'))], 
@@ -510,6 +537,7 @@ world.gremlins = {
     'g_V_outXcreatedX_projectXa_bX_byXnameX_byXinXcreatedX_countX_order_byXselectXbX__descX_selectXaX': [(lambda g:g.V().out('created').project('a','b').by('name').by(__.in_('created').count()).order().by(__.select('b'),Order.desc).select('a'))], 
     'g_V_valueMap_projectXxX_byXselectXnameXX': [(lambda g:g.V().valueMap().project('x').by(__.select('name')))], 
     'g_V_projectXa_bX_byXinE_countX_byXageX': [(lambda g:g.V().project('a','b').by(__.inE().count()).by('age'))], 
+    'g_withStrategiesXProductiveByStrategyX_V_projectXa_bX_byXinE_countX_byXageX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().project('a','b').by(__.inE().count()).by('age'))], 
     'g_V_hasXageX_propertiesXnameX': [(lambda g:g.V().has('age').properties('name').value())], 
     'g_V_hasXageX_propertiesXname_ageX_value': [(lambda g:g.V().has('age').properties('name','age').value())], 
     'g_V_hasXageX_propertiesXage_nameX_value': [(lambda g:g.V().has('age').properties('age','name').value())], 
@@ -578,6 +606,8 @@ world.gremlins = {
     'g_EX11X_propertiesXweightX_asXaX_selectXaX_byXkeyX': [(lambda g, eid11=None:g.E(eid11).properties('weight').as_('a').select('a').by(T.key))], 
     'g_EX11X_propertiesXweightX_asXaX_selectXaX_byXvalueX': [(lambda g, eid11=None:g.E(eid11).properties('weight').as_('a').select('a').by(T.value))], 
     'g_V_asXaX_selectXaX_byXageX': [(lambda g:g.V().as_('a').select('a').by('age'))], 
+    'g_V_asXa_nX_selectXa_nX_byXageX_byXnameX': [(lambda g:g.V().as_('a','n').select('a','n').by('age').by('name'))], 
+    'g_withStrategiesXProductiveByStrategyX_V_asXaX_selectXaX_byXageX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().as_('a').select('a').by('age'))], 
     'g_withSideEffectXk_nullX_injectXxX_selectXkX': [(lambda g:g.withSideEffect('k',None).inject('x').select('k'))], 
     'g_V_shortestPath': [(lambda g:g.V().identity().shortestPath())], 
     'g_V_both_dedup_shortestPath': [(lambda g:g.V().both().dedup().shortestPath())], 
@@ -600,9 +630,13 @@ world.gremlins = {
     'g_V_foo_fold_sumXlocalX': [(lambda g:g.V().foo.fold().sum_(Scope.local))], 
     'g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_sumX': [(lambda g:g.V().hasLabel('software').group().by('name').by(__.bothE().weight.sum_()))], 
     'g_V_aggregateXaX_byXageX_sumXlocalX': [(lambda g:g.V().aggregate('a').by('age').cap('a').sum_(Scope.local))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_sumXlocalX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('age').cap('a').sum_(Scope.local))], 
     'g_V_aggregateXaX_byXageX_capXaX_unfold_sum': [(lambda g:g.V().aggregate('a').by('age').cap('a').unfold().sum_())], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_sum': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('age').cap('a').unfold().sum_())], 
     'g_V_aggregateXaX_byXfooX_sumXlocalX': [(lambda g:g.V().aggregate('a').by('foo').cap('a').sum_(Scope.local))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_sumXlocalX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').sum_(Scope.local))], 
     'g_V_aggregateXaX_byXfooX_capXaX_unfold_sum': [(lambda g:g.V().aggregate('a').by('foo').cap('a').unfold().sum_())], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_sum': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('a').by('foo').cap('a').unfold().sum_())], 
     'g_injectXnull_10_5_nullX_sum': [(lambda g, xx1=None,xx2=None:g.inject(None,xx1,xx2,None).sum_())], 
     'g_injectXlistXnull_10_5_nullXX_sumXlocalX': [(lambda g, xx1=None:g.inject(xx1).sum_(Scope.local))], 
     'g_V_localXoutE_foldX_unfold': [(lambda g:g.V().local(__.outE().fold()).unfold())], 
@@ -621,6 +655,7 @@ world.gremlins = {
     'g_V_hasLabelXpersonX_filterXoutEXcreatedXX_valueMap_withXtokensX': [(lambda g:g.V().hasLabel('person').filter_(__.outE('created')).valueMap().with_('~tinkerpop.valueMap.tokens'))], 
     'g_VX1X_valueMapXname_locationX_byXunfoldX_by': [(lambda g, vid1=None:g.V(vid1).valueMap('name','location').by(__.unfold()).by())], 
     'g_V_valueMapXname_age_nullX': [(lambda g:g.V().valueMap('name','age',None))], 
+    'g_V_valueMapXname_ageX_byXisXxXXbyXunfoldX': [(lambda g:g.V().valueMap('name','age').by(__.is_('x')).by(__.unfold()))], 
     'g_VXnullX': [(lambda g:g.V(None))], 
     'g_VXlistXnullXX': [(lambda g, xx1=None:g.V(xx1))], 
     'g_VX1_nullX': [(lambda g, vid1=None:g.V(vid1,None))], 
@@ -682,17 +717,24 @@ world.gremlins = {
     'g_V_aggregateXxX_byXnameX_capXxX': [(lambda g:g.V().aggregate('x').by('name').cap('x'))], 
     'g_V_out_aggregateXaX_path': [(lambda g:g.V().out().aggregate('a').path())], 
     'g_V_hasLabelXpersonX_aggregateXxX_byXageX_capXxX_asXyX_selectXyX': [(lambda g:g.V().hasLabel('person').aggregate('x').by('age').cap('x').as_('y').select('y'))], 
+    'g_V_aggregateXxX_byXageX_capXxX': [(lambda g:g.V().aggregate('x').by('age').cap('x'))], 
+    'g_V_aggregateXlocal_xX_byXageX_capXxX': [(lambda g:g.V().aggregate(Scope.local,'x').by('age').cap('x'))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXlocal_xX_byXageX_capXxX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate(Scope.local,'x').by('age').cap('x'))], 
     'g_V_aggregateXlocal_a_nameX_out_capXaX': [(lambda g:g.V().aggregate(Scope.local,'a').by('name').out().cap('a'))], 
     'g_VX1X_aggregateXlocal_aX_byXnameX_out_aggregateXlocal_aX_byXnameX_name_capXaX': [(lambda g, vid1=None:g.V(vid1).aggregate(Scope.local,'a').by('name').out().aggregate(Scope.local,'a').by('name').name.cap('a'))], 
     'g_withSideEffectXa_setX_V_both_name_aggregateXlocal_aX_capXaX': [(lambda g, xx1=None:g.withSideEffect('a',xx1).V().both().name.aggregate(Scope.local,'a').cap('a'))], 
     'g_V_aggregateXlocal_aX_byXoutEXcreatedX_countX_out_out_aggregateXlocal_aX_byXinEXcreatedX_weight_sumX': [(lambda g:g.V().aggregate(Scope.local,'a').by(__.outE('created').count()).out().out().aggregate(Scope.local,'a').by(__.inE('created').weight.sum_()).cap('a'))], 
     'g_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX': [(lambda g:g.V().aggregate('x').by(__.age.is_(P.gt(29))).cap('x'))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('x').by(__.age.is_(P.gt(29))).cap('x'))], 
     'g_V_aggregateXxX_byXout_order_byXnameXX_capXxX': [(lambda g:g.V().aggregate('x').by(__.out().order().by('name')).cap('x'))], 
+    'g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXout_order_byXnameXX_capXxX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().aggregate('x').by(__.out().order().by('name')).cap('x'))], 
     'g_V_fail': [(lambda g:g.V().fail())], 
     'g_V_failXmsgX': [(lambda g:g.V().fail('msg'))], 
     'g_V_unionXout_failX': [(lambda g:g.V().union(__.out(),__.fail()))], 
     'g_V_group_byXnameX': [(lambda g:g.V().group().by('name'))], 
     'g_V_group_byXageX': [(lambda g:g.V().group().by('age'))], 
+    'g_withStrategiesXProductiveByStrategyX_V_group_byXageX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().group().by('age'))], 
+    'g_V_group_byXnameX_byXageX': [(lambda g:g.V().group().by('name').by('age'))], 
     'g_V_group_byXnameX_by': [(lambda g:g.V().group().by('name').by())], 
     'g_V_groupXaX_byXnameX_capXaX': [(lambda g:g.V().group('a').by('name').cap('a'))], 
     'g_V_hasXlangX_groupXaX_byXlangX_byXnameX_out_capXaX': [(lambda g:g.V().has('lang').group('a').by('lang').by('name').out().cap('a'))], 
@@ -716,6 +758,8 @@ world.gremlins = {
     'g_V_group_byXlabelX_byXlabel_countX': [(lambda g:g.V().group().by(__.label()).by(__.label().count()))], 
     'g_V_groupXmX_byXlabelX_byXlabel_countX_capXmX': [(lambda g:g.V().group('m').by(__.label()).by(__.label().count()).cap('m'))], 
     'g_V_outXcreatedX_groupCount_byXnameX': [(lambda g:g.V().out('created').groupCount().by('name'))], 
+    'g_V_groupCount_byXageX': [(lambda g:g.V().groupCount().by('age'))], 
+    'g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX': [(lambda g:g.withStrategies(*[TraversalStrategy('ProductiveByStrategy',{'productiveKeys':[],'strategy':'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'}, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy')]).V().groupCount().by('age'))], 
     'g_V_outXcreatedX_name_groupCount': [(lambda g:g.V().out('created').name.groupCount())], 
     'g_V_outXcreatedX_groupCountXaX_byXnameX_capXaX': [(lambda g:g.V().out('created').groupCount('a').by('name').cap('a'))], 
     'g_V_outXcreatedX_name_groupCountXaX_capXaX': [(lambda g:g.V().out('created').name.groupCount('a').cap('a'))], 
@@ -754,6 +798,7 @@ world.gremlins = {
     'g_withBulkXfalseX_withSackX1_sumX_VX1X_localXoutEXknowsX_barrierXnormSackX_inVX_inXknowsX_barrier_sack': [(lambda g, vid1=None:g.withBulk(False).withSack(float(1.0),Operator.sum_).V(vid1).local(__.outE('knows').barrier(Barrier.normSack).inV()).in_('knows').barrier().sack())], 
     'g_withBulkXfalseX_withSackX1_sumX_V_out_barrier_sack': [(lambda g:g.withBulk(False).withSack(1,Operator.sum_).V().out().barrier().sack())], 
     'g_withSackX1_sumX_VX1X_localXoutXknowsX_barrierXnormSackXX_inXknowsX_barrier_sack': [(lambda g, vid1=None:g.withSack(float(1.0),Operator.sum_).V(vid1).local(__.out('knows').barrier(Barrier.normSack)).in_('knows').barrier().sack())], 
+    'g_V_sackXassignX_byXageX_sack': [(lambda g:g.V().sack(Operator.assign).by('age').sack())], 
     'g_V_hasXageX_groupCountXaX_byXnameX_out_capXaX': [(lambda g:g.V().has('age').groupCount('a').by('name').out().cap('a'))], 
     'g_V_storeXa_nameX_out_capXaX': [(lambda g:g.V().store('a').by('name').out().cap('a'))], 
     'g_VX1X_storeXaX_byXnameX_out_storeXaX_byXnameX_name_capXaX': [(lambda g, vid1=None:g.V(vid1).store('a').by('name').out().store('a').by('name').name.cap('a'))], 
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
index a5df693..101acbb 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
@@ -363,7 +363,6 @@ public class SessionOpProcessor extends AbstractEvalOpProcessor {
         final String traversalSourceName = aliases.entrySet().iterator().next().getValue();
         final TraversalSource g = graphManager.getTraversalSource(traversalSourceName);
 
-        // todo: should session be grabbed here???
         final Session session = getSession(context, msg);
 
         // handle bytecode based graph operations like commit/rollback commands
@@ -504,6 +503,8 @@ public class SessionOpProcessor extends AbstractEvalOpProcessor {
         if (graph.features().graph().supportsTransactions()) {
             if (TX_COMMIT.equals(bytecode) || TX_ROLLBACK.equals(bytecode)) {
                 final boolean commit = TX_COMMIT.equals(bytecode);
+
+                // there is no timeout on a commit/rollback
                 submitToGremlinExecutor(context, 0, session, new FutureTask<>(() -> {
                     try {
                         if (graph.tx().isOpen()) {
@@ -540,7 +541,8 @@ public class SessionOpProcessor extends AbstractEvalOpProcessor {
                             }
                             context.writeAndFlush(specialResponseMsg.create());
                         } else {
-                            logger.warn(String.format("Exception processing a Traversal on request [%s].", msg.getRequestId()), ex);
+                            logger.warn(String.format("Exception processing a Traversal on request [%s] to %s the transaction.",
+                                    msg.getRequestId(), commit ? "commit" : "rollback"), ex);
                             context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
                                     .statusMessage(ex.getMessage())
                                     .statusAttributeException(ex).create());
diff --git a/gremlin-test/features/filter/CyclicPath.feature b/gremlin-test/features/filter/CyclicPath.feature
index 2de63ef..9deea88 100644
--- a/gremlin-test/features/filter/CyclicPath.feature
+++ b/gremlin-test/features/filter/CyclicPath.feature
@@ -30,6 +30,20 @@ Feature: Step - cyclicPath()
       | result |
       | v[marko] |
 
+  @GraphComputerVerificationReferenceOnly
+  Scenario: g_VX1X_both_both_cyclicPath_byXageX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).both().both().cyclicPath().by('age')
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |
+      | v[marko] |
+
   Scenario: g_VX1X_outXcreatedX_inXcreatedX_cyclicPath_path
     Given the modern graph
     And using the parameter vid1 defined as "v[marko].id"
diff --git a/gremlin-test/features/filter/Dedup.feature b/gremlin-test/features/filter/Dedup.feature
index 0cf9c21..c7b4e42 100644
--- a/gremlin-test/features/filter/Dedup.feature
+++ b/gremlin-test/features/filter/Dedup.feature
@@ -299,15 +299,29 @@ Feature: Step - dedup()
       | result |
       | d[21].l |
 
+  Scenario: g_V_order_byXname_descX_barrier_dedup_age_name
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().order().by("name",desc).barrier().dedup().by("age").values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | vadas |
+      | peter |
+      | marko |
+      | josh  |
+
   # ensures that dedup() returns the first item encountered. the ordering supplied will put "ripple" in front of
   # "lop" and make it the first to hit dedup() and as "age" is not a property of either of those only "ripple" will
   # win. the barrier() is required to trick out FilterRankingStrategy which will optimize the traversal placing the
   # dedup() prior to the order() and sorta ruin the test semantics
-  Scenario: g_V_order_byXname_descX_barrier_dedup_age_name
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_order_byXname_descX_barrier_dedup_age_name
     Given the modern graph
     And the traversal of
       """
-      g.V().order().by("name",desc).barrier().dedup().by("age").values("name")
+      g.withStrategies(ProductiveByStrategy).V().order().by("name",desc).barrier().dedup().by("age").values("name")
       """
     When iterated to list
     Then the result should be unordered
@@ -316,4 +330,33 @@ Feature: Step - dedup()
       | ripple |
       | peter |
       | marko |
-      | josh  |
\ No newline at end of file
+      | josh  |
+
+  Scenario: g_V_both_dedup_age_name
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().both().dedup().by("age").values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | vadas |
+      | peter |
+      | marko |
+      | josh  |
+
+  @GraphComputerVerificationReferenceOnly
+  Scenario: g_V_asXaX_both_asXbX_both_asXcX_dedupXa_bX_age_selectXa_b_cX_name
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().as("a").both().as("b").both().as("c").dedup("a","b").by("age").select("a","b","c").by("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"a":"marko", "b":"vadas", "c":"marko"}] |
+      | m[{"a":"marko", "b":"josh", "c":"ripple"}] |
+      | m[{"a":"vadas", "b":"marko", "c":"lop"}] |
+      | m[{"a":"josh", "b":"marko", "c":"lop"}] |
diff --git a/gremlin-test/features/filter/Sample.feature b/gremlin-test/features/filter/Sample.feature
index 44aa971..0319637 100644
--- a/gremlin-test/features/filter/Sample.feature
+++ b/gremlin-test/features/filter/Sample.feature
@@ -68,3 +68,14 @@ Feature: Step - sample()
       | result |
       | m[{"software":"l[d[0.2].d,d[0.4].d,d[0.4].d,d[1.0].d]"}] |
       | m[{"person":"l[d[0.5].d,d[1.0].d,d[0.4].d,d[0.2].d,d[1.0].d]"}] |
+
+  Scenario: g_withStrategiesXSeedStrategyX_V_order_byXlabel_descX_sampleX1X_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(new SeedStrategy(seed: 999999)).V().order().by(T.label, Order.desc).sample(1).by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[peter] |
\ No newline at end of file
diff --git a/gremlin-test/features/filter/SimplePath.feature b/gremlin-test/features/filter/SimplePath.feature
index 0020651..1555244 100644
--- a/gremlin-test/features/filter/SimplePath.feature
+++ b/gremlin-test/features/filter/SimplePath.feature
@@ -88,3 +88,20 @@ Feature: Step - simplePath()
       | p[d[0].i,v[lop],v[marko],v[josh]] |
       | p[d[0].i,v[josh],v[marko],v[lop]] |
       | p[d[0].i,v[josh],v[marko],v[vadas]] |
+
+  @GraphComputerVerificationInjectionNotSupported
+  Scenario: g_V_both_asXaX_both_asXbX_simplePath_path_byXageX__fromXaX_toXbX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().both().as('a').both().as('b').simplePath().path().by('age').from('a').to('b')
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | p[d[29].i,d[32].i] |
+      | p[d[29].i,d[27].i] |
+      | p[d[29].i,d[32].i] |
+      | p[d[32].i,d[29].i] |
+      | p[d[29].i,d[27].i] |
+      | p[d[32].i,d[29].i] |
diff --git a/gremlin-test/features/filter/Where.feature b/gremlin-test/features/filter/Where.feature
index 156d124..137ed5c 100644
--- a/gremlin-test/features/filter/Where.feature
+++ b/gremlin-test/features/filter/Where.feature
@@ -333,7 +333,6 @@ Feature: Step - where()
       | result |
       | josh |
 
-  # comparison of null "age" values in where()
   @GraphComputerVerificationReferenceOnly
   Scenario: g_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name
     Given the modern graph
@@ -343,6 +342,18 @@ Feature: Step - where()
       g.V(vid3).as("a").in().out().as("b").where("a", P.eq("b")).by("age").values("name")
       """
     When iterated to list
+    Then the result should be empty
+
+  # comparison of null "age" values in where()
+  @GraphComputerVerificationReferenceOnly
+  Scenario: g_withStrategiesXProductiveByStrategyX_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name
+    Given the modern graph
+    And using the parameter vid3 defined as "v[lop].id"
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V(vid3).as("a").in().out().as("b").where("a", P.eq("b")).by("age").values("name")
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | lop |
diff --git a/gremlin-test/features/map/Math.feature b/gremlin-test/features/map/Math.feature
index 768f37c..06cbd93 100644
--- a/gremlin-test/features/map/Math.feature
+++ b/gremlin-test/features/map/Math.feature
@@ -132,3 +132,31 @@ Feature: Step - math()
       | vadas |
       | lop   |
       | peter |
+
+  Scenario: g_V_mathXit_plus_itXbyXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().math("_+_").by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[64.0].d |
+      | d[58.0].d |
+      | d[54.0].d |
+      | d[70.0].d |
+
+  Scenario: g_V_valueMap_mathXit_plus_itXbyXselectXageX_unfoldXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().valueMap().math("_+_").by(__.select("age").unfold())
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[64.0].d |
+      | d[58.0].d |
+      | d[54.0].d |
+      | d[70.0].d |
\ No newline at end of file
diff --git a/gremlin-test/features/map/Max.feature b/gremlin-test/features/map/Max.feature
index 0d85cce..ef5a127 100644
--- a/gremlin-test/features/map/Max.feature
+++ b/gremlin-test/features/map/Max.feature
@@ -71,6 +71,17 @@ Feature: Step - max()
       | result |
       | d[35].i |
 
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_maxXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("age").cap("a").max(Scope.local)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[35].i |
+
   Scenario: g_V_aggregateXaX_byXageX_capXaX_unfold_max
     Given the modern graph
     And the traversal of
@@ -82,6 +93,17 @@ Feature: Step - max()
       | result |
       | d[35].i |
 
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_max
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("age").cap("a").unfold().max()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[35].i |
+
   Scenario: g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX
     Given the modern graph
     And the traversal of
@@ -89,6 +111,15 @@ Feature: Step - max()
       g.V().aggregate("a").by("foo").cap("a").max(Scope.local)
       """
     When iterated to list
+    Then the result should be empty
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_maxXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("foo").cap("a").max(Scope.local)
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | null |
@@ -100,6 +131,15 @@ Feature: Step - max()
       g.V().aggregate("a").by("foo").cap("a").unfold().max()
       """
     When iterated to list
+    Then the result should be empty
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_max
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("foo").cap("a").unfold().max()
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | null |
diff --git a/gremlin-test/features/map/Mean.feature b/gremlin-test/features/map/Mean.feature
index cb1b2de..1b1de2f 100644
--- a/gremlin-test/features/map/Mean.feature
+++ b/gremlin-test/features/map/Mean.feature
@@ -82,6 +82,18 @@ Feature: Step - mean()
       | d[30.75].d |
 
   # null values are ignored in mean() which is similar to sum aggregation works in SQL
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_meanXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("age").cap("a").mean(Scope.local)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[30.75].d |
+
+  # null values are ignored in mean() which is similar to sum aggregation works in SQL
   Scenario: g_V_aggregateXaX_byXageX_capXaX_unfold_mean
     Given the modern graph
     And the traversal of
@@ -93,6 +105,18 @@ Feature: Step - mean()
       | result |
       | d[30.75].d |
 
+  # null values are ignored in mean() which is similar to sum aggregation works in SQL
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_mean
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("age").cap("a").unfold().mean()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[30.75].d |
+
   # if all values are null then the result is null
   Scenario: g_V_aggregateXaX_byXfooX_meanXlocalX
     Given the modern graph
@@ -101,6 +125,16 @@ Feature: Step - mean()
       g.V().aggregate("a").by("foo").cap("a").mean(Scope.local)
       """
     When iterated to list
+    Then the result should be empty
+
+  # if all values are null then the result is null
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_meanXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("foo").cap("a").mean(Scope.local)
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | null |
@@ -113,6 +147,16 @@ Feature: Step - mean()
       g.V().aggregate("a").by("foo").cap("a").unfold().mean()
       """
     When iterated to list
+    Then the result should be empty
+
+  # if all values are null then the result is null
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("foo").cap("a").unfold().mean()
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | null |
diff --git a/gremlin-test/features/map/Min.feature b/gremlin-test/features/map/Min.feature
index 193e340..3146aa9 100644
--- a/gremlin-test/features/map/Min.feature
+++ b/gremlin-test/features/map/Min.feature
@@ -71,6 +71,17 @@ Feature: Step - min()
       | result |
       | d[27].i |
 
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_minXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("age").cap("a").min(Scope.local)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[27].i |
+
   Scenario: g_V_aggregateXaX_byXageX_capXaX_unfold_min
     Given the modern graph
     And the traversal of
@@ -82,6 +93,17 @@ Feature: Step - min()
       | result |
       | d[27].i |
 
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_min
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("age").cap("a").unfold().min()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[27].i |
+
   Scenario: g_V_aggregateXaX_byXfooX_capXaX_minXlocalX
     Given the modern graph
     And the traversal of
@@ -89,6 +111,15 @@ Feature: Step - min()
       g.V().aggregate("a").by("foo").cap("a").min(Scope.local)
       """
     When iterated to list
+    Then the result should be empty
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_minXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("foo").cap("a").min(Scope.local)
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | null |
@@ -100,6 +131,15 @@ Feature: Step - min()
       g.V().aggregate("a").by("foo").cap("a").unfold().min()
       """
     When iterated to list
+    Then the result should be empty
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_min
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("foo").cap("a").unfold().min()
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | null |
diff --git a/gremlin-test/features/map/Order.feature b/gremlin-test/features/map/Order.feature
index 7fb57b2..e1298e6 100644
--- a/gremlin-test/features/map/Order.feature
+++ b/gremlin-test/features/map/Order.feature
@@ -310,7 +310,49 @@ Feature: Step - order()
       | v[josh]   |
       | v[peter] |
 
-  # tests order().by() where a property isn't present to ensure null comes first
+  Scenario: g_V_order_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().order().by("age")
+      """
+    When iterated to list
+    Then the result should be ordered
+      | result |
+      | v[vadas] |
+      | v[marko] |
+      | v[josh]   |
+      | v[peter] |
+
+  Scenario: g_V_fold_orderXlocalX_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().fold().order(local).by("age")
+      """
+    When iterated next
+    Then the result should be ordered
+      | result |
+      | v[vadas] |
+      | v[marko] |
+      | v[josh]   |
+      | v[peter] |
+
+
+  Scenario: g_V_fold_orderXlocalX_byXage_descX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().fold().order(local).by("age", desc)
+      """
+    When iterated next
+    Then the result should be ordered
+      | result |
+      | v[peter] |
+      | v[josh] |
+      | v[marko] |
+      | v[vadas] |
+
   Scenario: g_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX
     Given the modern graph
     And the traversal of
@@ -320,6 +362,21 @@ Feature: Step - order()
     When iterated to list
     Then the result should be ordered
       | result |
+      | v[vadas] |
+      | v[marko] |
+      | v[josh]   |
+      | v[peter] |
+
+  # tests order().by() where a property isn't present to ensure null comes first
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().or(hasLabel("person"),has("software","name","lop")).order().by("age")
+      """
+    When iterated to list
+    Then the result should be ordered
+      | result |
       | v[lop] |
       | v[vadas] |
       | v[marko] |
@@ -526,4 +583,19 @@ Feature: Step - order()
       | m[{"name":"marko"}] |
       | m[{"t[label]":"person"}] |
       | m[{"t[id]":"v[marko].id"}] |
-      | m[{"age":29}] |
\ No newline at end of file
+      | m[{"age":29}] |
+
+  Scenario: g_VX1X_elementMap_orderXlocalX_byXkeys_ascXunfold
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).elementMap().order(Scope.local).by(Column.keys, Order.asc).unfold()
+      """
+    When iterated to list
+    Then the result should be ordered
+      | result |
+      | m[{"age":29}] |
+      | m[{"t[id]":"v[marko].id"}] |
+      | m[{"t[label]":"person"}] |
+      | m[{"name":"marko"}] |
\ No newline at end of file
diff --git a/gremlin-test/features/map/Path.feature b/gremlin-test/features/map/Path.feature
index e4e9f2e..a5e5605 100644
--- a/gremlin-test/features/map/Path.feature
+++ b/gremlin-test/features/map/Path.feature
@@ -120,6 +120,20 @@ Feature: Step - path()
     When iterated to list
     Then the result should be unordered
       | result |
+      | p[d[29].i,d[27].i] |
+      | p[d[29].i,d[32].i] |
+
+  @GraphComputerVerificationReferenceOnly
+  Scenario: g_withStrategiesXProductiveByStrategyX_VX1X_out_path_byXageX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V(vid1).out().path().by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
       | p[d[29].i,null] |
       | p[d[29].i,d[27].i] |
       | p[d[29].i,d[32].i] |
diff --git a/gremlin-test/features/map/Project.feature b/gremlin-test/features/map/Project.feature
index bbcf9a4..645450f 100644
--- a/gremlin-test/features/map/Project.feature
+++ b/gremlin-test/features/map/Project.feature
@@ -82,6 +82,24 @@ Feature: Step - project()
     When iterated to list
     Then the result should be unordered
       | result |
+      | m[{"a":"d[1].l"}] |
+      | m[{"a":"d[3].l"}] |
+      | m[{"a":"d[0].l", "b":"d[29].i"}] |
+      | m[{"a":"d[1].l", "b":"d[27].i"}] |
+      | m[{"a":"d[1].l", "b":"d[32].i"}] |
+      | m[{"a":"d[0].l", "b":"d[35].i"}] |
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_projectXa_bX_byXinE_countX_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().project("a", "b").
+          by(__.inE().count()).
+          by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
       | m[{"a":"d[1].l", "b":null}] |
       | m[{"a":"d[3].l", "b":null}] |
       | m[{"a":"d[0].l", "b":"d[29].i"}] |
diff --git a/gremlin-test/features/map/Select.feature b/gremlin-test/features/map/Select.feature
index d334cbc..de599f2 100644
--- a/gremlin-test/features/map/Select.feature
+++ b/gremlin-test/features/map/Select.feature
@@ -819,8 +819,6 @@ Feature: Step - select()
       | result |
       | d[0.4].d |
 
-  # ProductiveBy
-  @GraphComputerVerificationReferenceOnly
   Scenario: g_V_asXaX_selectXaX_byXageX
     Given the modern graph
     And the traversal of
@@ -832,6 +830,35 @@ Feature: Step - select()
       | result |
       | d[29].i |
       | d[27].i |
+      | d[32].i |
+      | d[35].i |
+
+  Scenario: g_V_asXa_nX_selectXa_nX_byXageX_byXnameX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().as("a","n").select("a","n").by("age").by("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"a":"d[29].i","n":"marko"}] |
+      | m[{"a":"d[27].i","n":"vadas"}] |
+      | m[{"a":"d[32].i","n":"josh"}] |
+      | m[{"a":"d[35].i","n":"peter"}] |
+
+  @GraphComputerVerificationReferenceOnly
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_asXaX_selectXaX_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().as("a").select("a").by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[27].i |
       | null |
       | d[32].i |
       | null |
diff --git a/gremlin-test/features/map/Sum.feature b/gremlin-test/features/map/Sum.feature
index 44e59f7..cf1c3c9 100644
--- a/gremlin-test/features/map/Sum.feature
+++ b/gremlin-test/features/map/Sum.feature
@@ -82,6 +82,18 @@ Feature: Step - sum()
       | d[123].i |
 
   # null values are ignored in sum() which is similar to sum aggregation works in SQL
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_sumXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("age").cap("a").sum(Scope.local)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[123].i |
+
+  # null values are ignored in sum() which is similar to sum aggregation works in SQL
   Scenario: g_V_aggregateXaX_byXageX_capXaX_unfold_sum
     Given the modern graph
     And the traversal of
@@ -93,7 +105,18 @@ Feature: Step - sum()
       | result |
       | d[123].l |
 
-  # if all values are null then the result is null
+  # null values are ignored in sum() which is similar to sum aggregation works in SQL
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_sum
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("age").cap("a").unfold().sum()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[123].l |
+
   Scenario: g_V_aggregateXaX_byXfooX_sumXlocalX
     Given the modern graph
     And the traversal of
@@ -101,6 +124,16 @@ Feature: Step - sum()
       g.V().aggregate("a").by("foo").cap("a").sum(Scope.local)
       """
     When iterated to list
+    Then the result should be empty
+
+  # if all values are null then the result is null
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_sumXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("foo").cap("a").sum(Scope.local)
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | null |
@@ -113,6 +146,16 @@ Feature: Step - sum()
       g.V().aggregate("a").by("foo").cap("a").unfold().sum()
       """
     When iterated to list
+    Then the result should be empty
+
+  # if all values are null then the result is null
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_sum
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("a").by("foo").cap("a").unfold().sum()
+      """
+    When iterated to list
     Then the result should be unordered
       | result |
       | null |
diff --git a/gremlin-test/features/map/ValueMap.feature b/gremlin-test/features/map/ValueMap.feature
index bedd50a..8a985d2 100644
--- a/gremlin-test/features/map/ValueMap.feature
+++ b/gremlin-test/features/map/ValueMap.feature
@@ -211,4 +211,20 @@ Feature: Step - valueMap()
       | m[{"name": ["peter"], "age": [35]}] |
       | m[{"name": ["vadas"], "age": [27]}] |
       | m[{"name": ["lop"]}] |
-      | m[{"name": ["ripple"]}] |
\ No newline at end of file
+      | m[{"name": ["ripple"]}] |
+
+  Scenario: g_V_valueMapXname_ageX_byXisXxXXbyXunfoldX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().valueMap("name", "age").by(__.is("x")).by(__.unfold())
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"age": 29}] |
+      | m[{"age": 32}] |
+      | m[{"age": 35}] |
+      | m[{"age": 27}] |
+      | m[{}] |
+      | m[{}] |
\ No newline at end of file
diff --git a/gremlin-test/features/sideEffect/Aggregate.feature b/gremlin-test/features/sideEffect/Aggregate.feature
index 2f86692..b3de3ce 100644
--- a/gremlin-test/features/sideEffect/Aggregate.feature
+++ b/gremlin-test/features/sideEffect/Aggregate.feature
@@ -96,6 +96,50 @@ Feature: Step - aggregate()
       | d[32].i |
       | d[35].i |
 
+  Scenario: g_V_aggregateXxX_byXageX_capXxX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().aggregate("x").by("age").cap("x")
+      """
+    When iterated next
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[27].i |
+      | d[32].i |
+      | d[35].i |
+
+  Scenario: g_V_aggregateXlocal_xX_byXageX_capXxX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().aggregate(Scope.local, "x").by("age").cap("x")
+      """
+    When iterated next
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[27].i |
+      | d[32].i |
+      | d[35].i |
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXlocal_xX_byXageX_capXxX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate(Scope.local, "x").by("age").cap("x")
+      """
+    When iterated next
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[27].i |
+      | d[32].i |
+      | d[35].i |
+      | null    |
+      | null    |
+
   Scenario: g_V_aggregateXlocal_a_nameX_out_capXaX
     Given the modern graph
     And the traversal of
@@ -177,13 +221,24 @@ Feature: Step - aggregate()
       | result |
       | d[32].i |
       | d[35].i |
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("x").by(__.values("age").is(P.gt(29))).cap("x")
+      """
+    When iterated next
+    Then the result should be unordered
+      | result |
+      | d[32].i |
+      | d[35].i |
       | null |
       | null |
       | null |
       | null |
 
-  # ProductiveBy
-  @GraphComputerVerificationReferenceOnly
+  @GraphComputerVerificationStarGraphExceeded
   Scenario: g_V_aggregateXxX_byXout_order_byXnameXX_capXxX
     Given the modern graph
     And the traversal of
@@ -196,6 +251,20 @@ Feature: Step - aggregate()
       | v[josh] |
       | v[lop] |
       | v[lop] |
+
+  @GraphComputerVerificationReferenceOnly
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXout_order_byXnameXX_capXxX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().aggregate("x").by(__.out().order().by("name")).cap("x")
+      """
+    When iterated next
+    Then the result should be unordered
+      | result |
+      | v[josh] |
+      | v[lop] |
+      | v[lop] |
       | null |
       | null |
       | null |
\ No newline at end of file
diff --git a/gremlin-test/features/sideEffect/Group.feature b/gremlin-test/features/sideEffect/Group.feature
index e7c83a7..c33ab11 100644
--- a/gremlin-test/features/sideEffect/Group.feature
+++ b/gremlin-test/features/sideEffect/Group.feature
@@ -38,8 +38,30 @@ Feature: Step - group()
     When iterated to list
     Then the result should be unordered
       | result |
+      | m[{"d[35].i":"l[v[peter]]", "d[27].i":"l[v[vadas]]", "d[32].i": "l[v[josh]]", "d[29].i":"l[v[marko]]"}] |
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_group_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().group().by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
       | m[{"null":"l[v[lop],v[ripple]]", "d[35].i":"l[v[peter]]", "d[27].i":"l[v[vadas]]", "d[32].i": "l[v[josh]]", "d[29].i":"l[v[marko]]"}] |
 
+  Scenario: g_V_group_byXnameX_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().group().by("name").by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"ripple":[], "peter":"l[d[35].i]", "vadas":"l[d[27].i]", "josh": "l[d[32].i]", "lop":[], "marko":"l[d[29].i]"}] |
+
   Scenario: g_V_group_byXnameX_by
     Given the modern graph
     And the traversal of
diff --git a/gremlin-test/features/sideEffect/GroupCount.feature b/gremlin-test/features/sideEffect/GroupCount.feature
index 98de286..3545c23 100644
--- a/gremlin-test/features/sideEffect/GroupCount.feature
+++ b/gremlin-test/features/sideEffect/GroupCount.feature
@@ -29,6 +29,28 @@ Feature: Step - groupCount()
       | result |
       | m[{"ripple":"d[1].l", "lop":"d[3].l"}] |
 
+  Scenario: g_V_groupCount_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().groupCount().by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"d[27].i":"d[1].l", "d[29].i":"d[1].l", "d[32].i":"d[1].l", "d[35].i":"d[1].l"}] |
+
+  Scenario: g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.withStrategies(ProductiveByStrategy).V().groupCount().by("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"null":"d[2].l", "d[27].i":"d[1].l", "d[29].i":"d[1].l", "d[32].i":"d[1].l", "d[35].i":"d[1].l"}] |
+
   Scenario: g_V_outXcreatedX_name_groupCount
     Given the modern graph
     And the traversal of
diff --git a/gremlin-test/features/sideEffect/Sack.feature b/gremlin-test/features/sideEffect/Sack.feature
index 0ce1ad0..0e18d5c 100644
--- a/gremlin-test/features/sideEffect/Sack.feature
+++ b/gremlin-test/features/sideEffect/Sack.feature
@@ -98,6 +98,20 @@ Feature: Step - sack()
       | d[1.0].m |
       | d[1.0].m |
 
+  Scenario: g_V_sackXassignX_byXageX_sack
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().sack(assign).by("age").sack()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[27].i |
+      | d[32].i |
+      | d[35].i |
+
   Scenario: g_withSackXBigInteger_TEN_powX1000X_assignX_V_localXoutXknowsX_barrierXnormSackXX_inXknowsX_barrier_sack
     Given an unsupported test
     Then nothing should happen because
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java
index 35bb75e..161f787 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupTest.java
@@ -150,11 +150,10 @@ public abstract class DedupTest extends AbstractGremlinProcessTest {
         final Traversal<Vertex, String> traversal = get_g_V_order_byXname_descX_barrier_dedup_age_name();
         printTraversalForm(traversal);
         final List<String> names = traversal.toList();
-        assertEquals(5, names.size());
+        assertEquals(4, names.size());
         assertTrue(names.contains("marko"));
         assertTrue(names.contains("vadas"));
         assertTrue(names.contains("josh"));
-        assertTrue(names.contains("ripple"));
         assertTrue(names.contains("peter"));
         assertFalse(traversal.hasNext());
     }
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTest.java
index dd02b59..0dc5d10 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTest.java
@@ -50,6 +50,8 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in;
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.not;
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.or;
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -413,7 +415,7 @@ public abstract class WhereTest extends AbstractGremlinProcessTest {
     public void g_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name() {
         final Traversal<Vertex, String> traversal = get_g_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name(convertToVertexId(graph, "lop"));
         printTraversalForm(traversal);
-        checkResults(Arrays.asList("lop", "lop", "lop", "ripple"), traversal);
+        assertThat(traversal.hasNext(), is(false));
     }
 
 
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxTest.java
index bb85fa2..1bfc79e 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxTest.java
@@ -105,7 +105,6 @@ public abstract class MaxTest extends AbstractGremlinProcessTest {
     public void g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX() {
         final Traversal<Vertex, Comparable> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX();
         printTraversalForm(traversal);
-        assertNull(traversal.next());
         assertFalse(traversal.hasNext());
     }
 
@@ -114,7 +113,6 @@ public abstract class MaxTest extends AbstractGremlinProcessTest {
     public void g_V_aggregateXaX_byXfooX_capXaX_unfold_max() {
         final Traversal<Vertex, Comparable> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_unfold_max();
         printTraversalForm(traversal);
-        assertNull(traversal.next());
         assertFalse(traversal.hasNext());
     }
 
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanTest.java
index 0908646..a55918b 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanTest.java
@@ -91,7 +91,6 @@ public abstract class MeanTest extends AbstractGremlinProcessTest {
     public void g_V_aggregateXaX_byXfooX_meanXlocalX() {
         final Traversal<Vertex, Double> traversal = get_g_V_aggregateXaX_byXfooX_meanXlocalX();
         printTraversalForm(traversal);
-        assertNull(traversal.next());
         assertFalse(traversal.hasNext());
     }
 
@@ -100,7 +99,6 @@ public abstract class MeanTest extends AbstractGremlinProcessTest {
     public void g_V_aggregateXaX_byXfooX_capXaX_unfold_mean() {
         final Traversal<Vertex, Double> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_unfold_mean();
         printTraversalForm(traversal);
-        assertNull(traversal.next());
         assertFalse(traversal.hasNext());
     }
 
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinTest.java
index 51c2e2a..383a833 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinTest.java
@@ -131,7 +131,6 @@ public abstract class MinTest extends AbstractGremlinProcessTest {
     public void g_V_aggregateXaX_byXfooX_capXaX_minXlocalX() {
         final Traversal<Vertex, Comparable> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_minXlocalX();
         printTraversalForm(traversal);
-        assertNull(traversal.next());
         assertFalse(traversal.hasNext());
     }
 
@@ -140,7 +139,6 @@ public abstract class MinTest extends AbstractGremlinProcessTest {
     public void g_V_aggregateXaX_byXfooX_capXaX_unfold_min() {
         final Traversal<Vertex, Comparable> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_unfold_min();
         printTraversalForm(traversal);
-        assertNull(traversal.next());
         assertFalse(traversal.hasNext());
     }
 
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderTest.java
index b41fe70..088273f 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderTest.java
@@ -311,7 +311,7 @@ public abstract class OrderTest extends AbstractGremlinProcessTest {
     public void g_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX() {
         final Traversal<Vertex, Vertex> traversal = get_g_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX();
         printTraversalForm(traversal);
-        checkResults(Arrays.asList(convertToVertex(graph, "lop"), convertToVertex(graph, "vadas"), convertToVertex(graph, "marko"), convertToVertex(graph, "josh"), convertToVertex(graph, "peter")), traversal);
+        checkResults(Arrays.asList(convertToVertex(graph, "vadas"), convertToVertex(graph, "marko"), convertToVertex(graph, "josh"), convertToVertex(graph, "peter")), traversal);
     }
 
     @Test
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathTest.java
index d60b2cf..e4b0640 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PathTest.java
@@ -95,8 +95,8 @@ public abstract class PathTest extends AbstractGremlinProcessTest {
             assertTrue(path.get(1) == null || path.get(1).equals(27) || path.get(1).equals(32));
             ages.add(path.get(1));
         }
-        assertEquals(3, counter);
-        assertEquals(3, ages.size());
+        assertEquals(2, counter);
+        assertEquals(2, ages.size());
     }
 
     @Test
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectTest.java
index c073eb1..11c49c2 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectTest.java
@@ -27,9 +27,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
 import static org.junit.Assert.assertEquals;
@@ -64,13 +68,32 @@ public abstract class ProjectTest extends AbstractGremlinProcessTest {
     public void g_V_projectXa_bX_byXinE_countX_byXageX() {
         final Traversal<Vertex, Map<String, Number>> traversal = get_g_V_projectXa_bX_byXinE_countX_byXageX();
         printTraversalForm(traversal);
-        checkResults(makeMapList(2,
-                "a", 3l, "b", null,
-                "a", 1L, "b", null,
-                "a", 0L, "b", 29,
-                "a", 1L, "b", 27,
-                "a", 1L, "b", 32,
-                "a", 0L, "b", 35), traversal);
+
+        final List<Map<String, Number>> a = new ArrayList<>();
+        a.add(new HashMap<String,Number>() {{
+            put("a", 3L);
+        }});
+        a.add(new HashMap<String,Number>() {{
+            put("a", 1L);
+        }});
+        a.add(new HashMap<String,Number>() {{
+            put("a", 0L);
+            put("b", 29);
+        }});
+        a.add(new HashMap<String,Number>() {{
+            put("a", 1L);
+            put("b", 27);
+        }});
+        a.add(new HashMap<String,Number>() {{
+            put("a", 1L);
+            put("b", 32);
+        }});
+        a.add(new HashMap<String,Number>() {{
+            put("a", 0L);
+            put("b", 35);
+        }});
+
+        checkResults(a, traversal);
     }
 
     @Test
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
index 4b450ca..dd8b7ca 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectTest.java
@@ -898,7 +898,7 @@ public abstract class SelectTest extends AbstractGremlinProcessTest {
     public void g_V_asXaX_selectXaX_byXageX() {
         final Traversal<Vertex, Object> traversal = get_g_V_asXaX_selectXaX_byXageX();
         printTraversalForm(traversal);
-        checkResults(Arrays.asList(29, 27, null, 32, null, 35), traversal);
+        checkResults(Arrays.asList(29, 27, 32, 35), traversal);
     }
 
     @Test
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumTest.java
index 6d45fed..ccee6a8 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumTest.java
@@ -119,7 +119,6 @@ public abstract class SumTest extends AbstractGremlinProcessTest {
     public void g_V_aggregateXaX_byXfooX_sumXlocalX() {
         final Traversal<Vertex, Number> traversal = get_g_V_aggregateXaX_byXfooX_sumXlocalX();
         printTraversalForm(traversal);
-        assertNull(traversal.next());
         assertFalse(traversal.hasNext());
     }
 
@@ -127,8 +126,7 @@ public abstract class SumTest extends AbstractGremlinProcessTest {
     @LoadGraphWith(MODERN)
     public void g_V_aggregateXaX_byXfooX_capXaX_unfold_sum() {
         final Traversal<Vertex, Number> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_unfold_sum();
-        printTraversalForm(traversal);
-        assertNull(traversal.next());
+        printTraversalForm(traversal);;
         assertFalse(traversal.hasNext());
     }
 
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateTest.java
index f3661e9..cf5eb31 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateTest.java
@@ -242,7 +242,7 @@ public abstract class AggregateTest extends AbstractGremlinProcessTest {
         printTraversalForm(traversal);
         final Collection<Integer> ages = traversal.next();
         assertFalse(traversal.hasNext());
-        assertThat(ages, containsInAnyOrder(null, null, null, null, 32, 35));
+        assertThat(ages, containsInAnyOrder(32, 35));
     }
 
     @Test
@@ -252,7 +252,7 @@ public abstract class AggregateTest extends AbstractGremlinProcessTest {
         printTraversalForm(traversal);
         final Collection<Vertex> ages = traversal.next();
         assertFalse(traversal.hasNext());
-        assertThat(ages, containsInAnyOrder(null, null, null, convertToVertex("lop"), convertToVertex("josh"), convertToVertex("lop")));
+        assertThat(ages, containsInAnyOrder(convertToVertex("lop"), convertToVertex("josh"), convertToVertex("lop")));
     }
 
     public static class Traversals extends AggregateTest {
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupTest.java
index c7f86d7..897f2d2 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupTest.java
@@ -123,7 +123,7 @@ public abstract class GroupTest extends AbstractGremlinProcessTest {
         printTraversalForm(traversal);
 
         final Map<Integer, Collection<Vertex>> map = traversal.next();
-        assertEquals(5, map.size());
+        assertEquals(4, map.size());
         map.forEach((key, values) -> {
             if (null == key)
                 assertEquals(2, values.size());
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeTest.java
index a9a4b1b..72ee0f9 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeTest.java
@@ -52,6 +52,8 @@ public abstract class TreeTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, Tree> get_g_VX1X_out_out_tree_byXnameX(final Object v1Id);
 
+    public abstract Traversal<Vertex, Tree> get_g_V_out_tree_byXageX();
+
     public abstract Traversal<Vertex, Tree> get_g_VX1X_out_out_treeXaX_byXnameX_both_both_capXaX(final Object v1Id);
 
     public abstract Traversal<Vertex, Tree> get_g_V_out_out_out_tree();
@@ -66,6 +68,25 @@ public abstract class TreeTest extends AbstractGremlinProcessTest {
 
     @Test
     @LoadGraphWith(MODERN)
+    public void g_V_out_tree_byXageX() {
+        final Traversal<Vertex, Tree> traversal = get_g_V_out_tree_byXageX();
+        printTraversalForm(traversal);
+
+        final Tree tree = traversal.next();
+        assertFalse(traversal.hasNext());
+        assertEquals(3, tree.size());
+        assertTrue(tree.containsKey(29));
+        assertEquals(2, ((Map) tree.get(29)).size());
+        assertTrue(((Map) tree.get(29)).containsKey(32));
+        assertTrue(((Map) tree.get(29)).containsKey(27));
+        assertTrue(tree.containsKey(35));
+        assertEquals(0, ((Map) tree.get(35)).size());
+        assertTrue(tree.containsKey(32));
+        assertEquals(0, ((Map) tree.get(32)).size());
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
     public void g_VX1X_out_out_treeXaX_byXnameX_both_both_capXaX() {
         final Traversal<Vertex, Tree> traversal = get_g_VX1X_out_out_treeXaX_byXnameX_both_both_capXaX(convertToVertexId("marko"));
         printTraversalForm(traversal);
@@ -153,6 +174,10 @@ public abstract class TreeTest extends AbstractGremlinProcessTest {
             return g.V(v1Id).out().out().tree().by("name");
         }
 
+        public Traversal<Vertex, Tree> get_g_V_out_tree_byXageX() {
+            return g.V().out().tree().by("age");
+        }
+
         @Override
         public Traversal<Vertex, Tree> get_g_VX1X_out_out_treeXaX_byXnameX_both_both_capXaX(final Object v1Id) {
             return g.V(v1Id).out().out().tree("a").by("name").both().both().cap("a");

[tinkerpop] 01/02: TINKERPOP-2635 Add fail() step

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2635
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 2948ae6c024bedaa932f5ad974bfa2abf9e47f61
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Tue Nov 30 13:57:30 2021 -0500

    TINKERPOP-2635 Add fail() step
---
 CHANGELOG.asciidoc                                 |   2 +
 docs/src/dev/developer/for-committers.asciidoc     |   1 +
 docs/src/reference/the-traversal.asciidoc          |  37 ++++++
 docs/src/upgrade/release-3.6.x.asciidoc            |  23 +++-
 .../tinkerpop/gremlin/console/Console.groovy       | 124 +++++++++++++++------
 .../language/grammar/GremlinBaseVisitor.java       |  13 +++
 .../language/grammar/TraversalMethodVisitor.java   |  17 +++
 .../gremlin/process/traversal/Failure.java         | 101 +++++++++++++++++
 .../gremlin/process/traversal/Traverser.java       |   9 ++
 .../traversal/dsl/graph/GraphTraversal.java        |  54 +++++++--
 .../gremlin/process/traversal/dsl/graph/__.java    |  14 +++
 .../traversal/step/sideEffect/FailStep.java        |  87 +++++++++++++++
 .../traverser/B_LP_NL_O_S_SE_SL_Traverser.java     |   5 +
 .../traverser/B_LP_O_S_SE_SL_Traverser.java        |   1 -
 .../traverser/B_NL_O_S_SE_SL_Traverser.java        |   1 -
 .../traversal/traverser/B_O_S_SE_SL_Traverser.java |   7 ++
 .../traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java  |   6 +
 .../traverser/LP_NL_O_OB_S_SE_SL_Traverser.java    |   6 +
 .../traverser/LP_O_OB_S_SE_SL_Traverser.java       |   1 -
 .../traverser/NL_O_OB_S_SE_SL_Traverser.java       |   6 +
 .../traverser/O_OB_S_SE_SL_Traverser.java          |   9 +-
 .../Process/Traversal/GraphTraversal.cs            |  27 +++++
 .../src/Gremlin.Net/Process/Traversal/__.cs        |  16 +++
 .../Gherkin/CommonSteps.cs                         |  67 ++++++++---
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |   5 +
 .../apache/tinkerpop/gremlin/driver/Tokens.java    |  15 +++
 .../gremlin/driver/message/ResponseStatusCode.java |  11 ++
 .../ast/VarAsBindingASTTransformation.groovy       |   3 +
 .../lib/process/graph-traversal.js                 |  11 ++
 .../test/cucumber/feature-steps.js                 |  14 ++-
 .../gremlin-javascript/test/cucumber/gremlin.js    |   3 +
 gremlin-language/src/main/antlr4/Gremlin.g4        |   6 +
 .../gremlin_python/process/graph_traversal.py      |  14 +++
 .../src/main/python/radish/feature_steps.py        |  24 +++-
 gremlin-python/src/main/python/radish/gremlin.py   |   3 +
 .../gremlin/server/handler/AbstractSession.java    |  31 ++++--
 .../gremlin/server/op/AbstractEvalOpProcessor.java |  25 +++--
 .../gremlin/server/op/AbstractOpProcessor.java     |   9 +-
 .../server/op/session/SessionOpProcessor.java      |  67 ++++++++---
 .../server/op/traversal/TraversalOpProcessor.java  |  47 +++++---
 .../gremlin/server/GremlinServerIntegrateTest.java |  16 +++
 gremlin-test/features/sideEffect/Fail.feature      |  46 ++++++++
 .../tinkerpop/gremlin/features/StepDefinition.java |  40 ++++++-
 43 files changed, 895 insertions(+), 129 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index f610897..703ba95 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,8 @@ limitations under the License.
 
 * Changed TinkerGraph to allow identifiers to be heterogeneous when filtering.
 * Prevented values of `T` to `property()` from being `null`.
+* Added `fail()` step.
+* Improved Gherkin test framework to allow for asserting traversal exceptions as a behavior.
 * Fixed query indentation for profile metrics where indent levels were not being respected.
 * `TraversalOpProcessor` no longer accepts a `String` representation of `Bytecode` for the "gremlin" argument which was left to support older versions of the drivers.
 * Removed requirement that "ids" used to filter vertices and edges need to be all of a single type.
diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc
index ccddf9c..4114259 100644
--- a/docs/src/dev/developer/for-committers.asciidoc
+++ b/docs/src/dev/developer/for-committers.asciidoc
@@ -448,6 +448,7 @@ The "Then" options handle the assertion of the result. There are several options
 * "the result should have a count of _xxx_" - assumes a list value in the result and counts the number of values
 in it
 * "the result should be empty" - no results
+* "the traversal will raise an error" - an exception is thrown as a result of traversal iteration
 * "the result should be ordered" - the exact results and should appear in the order presented
 * "the result should be unordered" - the exact results but can appear any order
 * "the result should be of" - results can be any of the specified values and in any order (use when guarantees
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index c888553..0e7b0ed 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -1228,6 +1228,43 @@ g.V().hasLabel('person').outE().identity().inV().count().is(gt(5)).explain()
 
 For traversal profiling information, please see <<profile-step,`profile()`>>-step.
 
+[[fail-step]]
+=== Fail Step
+
+The `fail()`-step provides a way to force a traversal to immediately fail with an exception. This feature is often
+helpful during debugging purposes and for validating certain conditions prior to continuing with traversal execution.
+
+[source,text]
+----
+gremlin> g.V().has('person','name','peter').fold().
+......1>   coalesce(unfold(),
+......2>            fail('peter should exist')).
+......3>   property('k',100)
+==>v[6]
+gremlin> g.V().has('person','name','stephen').fold().
+......1>   coalesce(unfold(),
+......2>            fail('stephen should exist')).
+......3>   property('k',100)
+fail() Step Triggered
+===========================================================================================================================
+Message > stephen should exist
+Traverser> []
+  Bulk   > 1
+Traversal> fail()
+Parent   > CoalesceStep [V().has("person","name","stephen").fold().coalesce(__.unfold(),__.fail()).property("k",(int) 100)]
+Metadata > {}
+===========================================================================================================================
+----
+
+The code example above exemplifies the latter use case where there is essentially an assertion that there is a vertex
+with a particular "name" value prior to updating the property "k" and explicitly failing when that vertex is not found.
+
+*Additional References*
+
+link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#fail--++[`fail()`],
+link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#fail-java.lang.String-++[`fail(String)`]
+
+
 [[fold-step]]
 === Fold Step
 
diff --git a/docs/src/upgrade/release-3.6.x.asciidoc b/docs/src/upgrade/release-3.6.x.asciidoc
index 0f9a7b3..b4ae8d2 100644
--- a/docs/src/upgrade/release-3.6.x.asciidoc
+++ b/docs/src/upgrade/release-3.6.x.asciidoc
@@ -47,7 +47,28 @@ It is worth noting that `gremlin-groovy` utilized the DSL annotations to constru
 link:https://tinkerpop.apache.org/docs/3.6.0/reference/#credentials-dsl[Credentials DSL] so the `gremlin-annotations`
 package is now explicitly associated to `gremlin-groovy` but as an `<optional>` dependency.
 
-See:link:https://issues.apache.org/jira/browse/TINKERPOP-2411[TINKERPOP-2411]
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2411[TINKERPOP-2411]
+
+==== fail() Step
+
+The new `fail()` step provides a way to immediately terminate a traversal with a runtime exception. In the Gremlin
+Console, the exception will be rendered as follows which helps provide some context to the failure:
+
+[source,text]
+----
+gremlin> g.V().fail("nope!")
+fail() Step Triggered
+=====================
+Message > nope!
+Traverser> v[1]
+  Bulk   > 1
+Traversal> V().fail()
+Metadata > {}
+=====================
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2635[TINKERPOP-2635],
+link:https://tinkerpop.apache.org/docs/3.6.0/reference/#fail-step[Reference Documentation]
 
 ==== Null for T
 
diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
index c47bbf3..bf5fe0c 100644
--- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
+++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
@@ -36,6 +36,13 @@ import org.apache.tinkerpop.gremlin.jsr223.CoreGremlinPlugin
 import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
 import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer
 import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException
+import org.apache.tinkerpop.gremlin.process.traversal.Failure
+import org.apache.tinkerpop.gremlin.process.traversal.Step
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep
+import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_LP_NL_O_P_S_SE_SL_Traverser
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation
 import org.apache.tinkerpop.gremlin.structure.Edge
 import org.apache.tinkerpop.gremlin.structure.T
@@ -346,47 +353,54 @@ class Console {
         if (err instanceof Throwable) {
             try {
                 final Throwable e = (Throwable) err
-                String message = e.getMessage()
-                if (null != message) {
-                    message = message.replace("startup failed:", "")
-                    io.err.println(Colorizer.render(Preferences.errorColor, message.trim()))
+
+                // special rendering for Failure
+                if ((err instanceof Failure)) {
+                    def fail = (Failure) err
+                    io.err.println(Colorizer.render(Preferences.errorColor, fail.format()))
                 } else {
-                    io.err.println(Colorizer.render(Preferences.errorColor,e))
-                }
+                    String message = e.getMessage()
+                    if (null != message) {
+                        message = message.replace("startup failed:", "")
+                        io.err.println(Colorizer.render(Preferences.errorColor, message.trim()))
+                    } else {
+                        io.err.println(Colorizer.render(Preferences.errorColor, e))
+                    }
 
-                // provide a hint in the case of a stackoverflow as it can be common when running large Gremlin
-                // scripts and it isn't immediately apparent what the error might mean in this context especially
-                // if the user isn't familiar with the JVM. it really can only be a hint since we can't be completely
-                // sure it arose as a result of a long Gremlin traversal.
-                if (err instanceof StackOverflowError) {
-                    io.err.println(Colorizer.render(Preferences.errorColor,
-                            "A StackOverflowError can indicate that the Gremlin traversal being executed is too long. If " +
-                                    "you have a single Gremlin statement that is \"long\", you may break it up into " +
-                                    "multiple separate commands, re-write the traversal to operate on a stream of " +
-                                    "input via inject() rather than literals, or attempt to increase the -Xss setting" +
-                                    "of the Gremlin Console by modifying gremlin.sh."));
-                }
+                    // provide a hint in the case of a stackoverflow as it can be common when running large Gremlin
+                    // scripts and it isn't immediately apparent what the error might mean in this context especially
+                    // if the user isn't familiar with the JVM. it really can only be a hint since we can't be completely
+                    // sure it arose as a result of a long Gremlin traversal.
+                    if (err instanceof StackOverflowError) {
+                        io.err.println(Colorizer.render(Preferences.errorColor,
+                                "A StackOverflowError can indicate that the Gremlin traversal being executed is too long. If " +
+                                        "you have a single Gremlin statement that is \"long\", you may break it up into " +
+                                        "multiple separate commands, re-write the traversal to operate on a stream of " +
+                                        "input via inject() rather than literals, or attempt to increase the -Xss setting" +
+                                        "of the Gremlin Console by modifying gremlin.sh."));
+                    }
 
-                if (interactive) {
-                    io.err.println(Colorizer.render(Preferences.infoColor,"Type ':help' or ':h' for help."))
-                    io.err.print(Colorizer.render(Preferences.errorColor, "Display stack trace? [yN]"))
-                    io.err.flush()
-                    String line = new BufferedReader(io.in).readLine()
-                    if (null == line)
-                        line = ""
-                    io.err.print(line.trim())
-                    io.err.println()
-                    if (line.trim().equals("y") || line.trim().equals("Y")) {
-                        if (err instanceof RemoteException && err.remoteStackTrace.isPresent()) {
-                            io.err.print(err.remoteStackTrace.get())
-                            io.err.flush()
-                        } else {
-                            e.printStackTrace(io.err)
+                    if (interactive) {
+                        io.err.println(Colorizer.render(Preferences.infoColor, "Type ':help' or ':h' for help."))
+                        io.err.print(Colorizer.render(Preferences.errorColor, "Display stack trace? [yN]"))
+                        io.err.flush()
+                        String line = new BufferedReader(io.in).readLine()
+                        if (null == line)
+                            line = ""
+                        io.err.print(line.trim())
+                        io.err.println()
+                        if (line.trim().equals("y") || line.trim().equals("Y")) {
+                            if (err instanceof RemoteException && err.remoteStackTrace.isPresent()) {
+                                io.err.print(err.remoteStackTrace.get())
+                                io.err.flush()
+                            } else {
+                                e.printStackTrace(io.err)
+                            }
                         }
+                    } else {
+                        e.printStackTrace(io.err)
+                        System.exit(1)
                     }
-                } else {
-                    e.printStackTrace(io.err)
-                    System.exit(1)
                 }
             } catch (Exception ignored) {
                 io.err.println(Colorizer.render(Preferences.errorColor, "An undefined error has occurred: " + err))
@@ -402,6 +416,44 @@ class Console {
         return null
     }
 
+    private def writeTraverserToErrorLines(Traverser t, List errorLines) {
+        // every traverser has an object so toString() that. pad with spaces to cover "side-effects" width
+        errorLines << "Traverser> $t"
+
+        def optGenerator = t.asAdmin().generator
+        if (optGenerator.isPresent()) {
+            def width = "Traverser".length()
+            def generator = optGenerator.get()
+            if (generator.providedRequirements.contains(TraverserRequirement.BULK)) {
+                errorLines << "  Bulk".padRight(width) + "> " + t.bulk()
+            }
+
+            if (generator.providedRequirements.contains(TraverserRequirement.SACK)) {
+                errorLines << "  Sack".padRight(width) + "> " + t.sack()
+            }
+
+            if (generator.providedRequirements.contains(TraverserRequirement.PATH)) {
+                errorLines << "  Path".padRight(width) + "> " + t.path()
+            }
+
+            if (generator.providedRequirements.contains(TraverserRequirement.SINGLE_LOOP) ||
+                    generator.providedRequirements.contains(TraverserRequirement.NESTED_LOOP)) {
+                // flatten loops/names if present
+                def loopNames = t.asAdmin().loopNames
+                def loopsLine = loopNames.isEmpty() ? t.loops() : loopNames.collect { [(it): t.loops(it)]}
+                errorLines << "  Loops".padRight(width) + "> " + loopsLine
+            }
+
+            if (generator.providedRequirements.contains(TraverserRequirement.SIDE_EFFECTS)) {
+                // convert side-effects to a map
+                def sideEffects = t.asAdmin().sideEffects
+                def keys = sideEffects.keys()
+                errorLines << "  S/E".padRight(width) + "> " + keys.collectEntries { [(it): sideEffects.get(it)]}
+            }
+
+        }
+    }
+
     private static String buildResultPrompt() {
         final String groovyshellProperty = System.getProperty("gremlin.prompt")
         if (groovyshellProperty != null)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
index 95f2874..faef075 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinBaseVisitor.java
@@ -348,6 +348,19 @@ public class GremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> implement
 	 * {@inheritDoc}
 	 */
 	@Override public T visitTraversalMethod_emit_Traversal(final GremlinParser.TraversalMethod_emit_TraversalContext ctx) { notImplemented(ctx); return null; }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public T visitTraversalMethod_fail_Empty(final GremlinParser.TraversalMethod_fail_EmptyContext ctx) { notImplemented(ctx); return null; }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public T visitTraversalMethod_fail_String(final GremlinParser.TraversalMethod_fail_StringContext ctx) { notImplemented(ctx); return null; }
+
 	/**
 	 * {@inheritDoc}
 	 */
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
index 67258ce..7682232 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
@@ -29,6 +29,7 @@ import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 
+import java.util.Map;
 import java.util.function.BiFunction;
 
 import static org.apache.tinkerpop.gremlin.process.traversal.SackFunctions.Barrier.normSack;
@@ -453,6 +454,22 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal>
      * {@inheritDoc}
      */
     @Override
+    public Traversal visitTraversalMethod_fail_Empty(final GremlinParser.TraversalMethod_fail_EmptyContext ctx) {
+        return this.graphTraversal.fail();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Traversal visitTraversalMethod_fail_String(final GremlinParser.TraversalMethod_fail_StringContext ctx) {
+        return this.graphTraversal.fail(GenericLiteralVisitor.getStringLiteral(ctx.stringLiteral()));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public GraphTraversal visitTraversalMethod_filter_Predicate(final GremlinParser.TraversalMethod_filter_PredicateContext ctx) {
         return graphTraversal.filter(TraversalPredicateVisitor.getInstance().visitTraversalPredicate(ctx.traversalPredicate()));
     }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Failure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Failure.java
new file mode 100644
index 0000000..8e11c87
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Failure.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.FailStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public interface Failure {
+
+    static Translator.ScriptTranslator TRANSLATOR = GroovyTranslator.of("");
+
+    String getMessage();
+
+    Map<String,Object> getMetadata();
+
+    Traverser.Admin getTraverser();
+
+    Traversal.Admin getTraversal();
+
+    /**
+     * Gets the {@code Failure} as a formatted string representation.
+     */
+    public default String format() {
+        final List<String> lines = new ArrayList<>();
+        final Step parentStep = (Step) getTraversal().getParent();
+
+        lines.add(String.format("Message  > %s", getMessage()));
+        lines.add(String.format("Traverser> %s", getTraverser().toString()));
+
+        final TraverserGenerator generator = getTraversal().getTraverserGenerator();
+        final Traverser.Admin traverser = getTraverser();
+        if (generator.getProvidedRequirements().contains(TraverserRequirement.BULK)) {
+            lines.add(String.format("  Bulk   > %s", traverser.bulk()));
+        }
+        if (generator.getProvidedRequirements().contains(TraverserRequirement.SACK)) {
+            lines.add(String.format("  Sack   > %s", traverser.sack()));
+        }
+        if (generator.getProvidedRequirements().contains(TraverserRequirement.PATH)) {
+            lines.add(String.format("  Path   > %s", traverser.path()));
+        }
+        if (generator.getProvidedRequirements().contains(TraverserRequirement.SINGLE_LOOP) ||
+                generator.getProvidedRequirements().contains(TraverserRequirement.NESTED_LOOP) ) {
+            final Set<String> loopNames = traverser.getLoopNames();
+            final String loopsLine = loopNames.isEmpty() ?
+                    String.valueOf(traverser.asAdmin().loops()) :
+                    loopNames.stream().collect(Collectors.toMap(loopName -> loopName, traverser::loops)).toString();
+            lines.add(String.format("  Loops  > %s", loopsLine));
+        }
+        if (generator.getProvidedRequirements().contains(TraverserRequirement.SIDE_EFFECTS)) {
+            final TraversalSideEffects tse = traverser.getSideEffects();
+            final Set<String> keys = tse.keys();
+            lines.add(String.format("  S/E    > %s", keys.stream().collect(Collectors.toMap(k -> k, tse::get))));
+        }
+
+        // removes the starting period so that "__.out()" simply presents as "out()"
+        lines.add(String.format("Traversal> %s", TRANSLATOR.translate(getTraversal()).getScript().substring(1)));
+
+        // not sure there is a situation where fail() would be used where it was not wrapped in a parent,
+        // but on the odd case that it is it can be handled
+        if (parentStep != EmptyStep.instance()) {
+            lines.add(String.format("Parent   > %s [%s]",
+                    parentStep.getClass().getSimpleName(), TRANSLATOR.translate(parentStep.getTraversal()).getScript().substring(1)));
+        }
+
+        lines.add(String.format("Metadata > %s", getMetadata()));
+
+        final int longestLineLength = lines.stream().mapToInt(String::length).max().getAsInt();
+        final String separatorLine = String.join("", Collections.nCopies(longestLineLength, "="));
+        lines.add(0, separatorLine);
+        lines.add(0, "fail() Step Triggered");
+        lines.add(separatorLine);
+
+        return String.join(System.lineSeparator(), lines);
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java
index fc58c62..19eb689 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java
@@ -22,7 +22,9 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.LoopsStep;
 import org.apache.tinkerpop.gremlin.structure.util.Attachable;
 
 import java.io.Serializable;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
 
@@ -325,5 +327,12 @@ public interface Traverser<T> extends Serializable, Comparable<Traverser<T>>, Cl
          * @return the set of tags associated with the traverser.
          */
         public Set<String> getTags();
+
+        /**
+         * If this traverser supports loops then return the loop names if any.
+         */
+        public default Set<String> getLoopNames() {
+            return Collections.emptySet();
+        }
     }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
index 8d11664..891ead0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PageRank
 import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PeerPressureVertexProgramStep;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ProgramVertexProgramStep;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ShortestPathVertexProgramStep;
+import org.apache.tinkerpop.gremlin.process.traversal.Failure;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
@@ -126,6 +127,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.UnfoldStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AggregateGlobalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.FailStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupCountSideEffectStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupSideEffectStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IdentityStep;
@@ -1254,6 +1256,18 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
+     * Filter all traversers in the traversal. This step has narrow use cases and is primarily intended for use as a
+     * signal to remote servers that {@link #iterate()} was called. While it may be directly used, it is often a sign
+     * that a traversal should be re-written in another form.
+     *
+     * @return the updated traversal with respective {@link NoneStep}.
+     */
+    @Override
+    default GraphTraversal<S, E> none() {
+        return (GraphTraversal<S, E>) Traversal.super.none();
+    }
+
+    /**
      * Ensures that at least one of the provided traversals yield a result.
      *
      * @param orTraversals filter traversals where at least one must be satisfied
@@ -2154,6 +2168,33 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
+     * When triggered, immediately throws a {@code RuntimeException} which implements the {@link Failure} interface.
+     * The traversal will be terminated as a result.
+     *
+     * @return the traversal with an appended {@link FailStep}.
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#fail-step" target="_blank">Reference Documentation - Fail Step</a>
+     * @since 3.6.0
+     */
+    public default GraphTraversal<S, E> fail() {
+        this.asAdmin().getBytecode().addStep(Symbols.fail);
+        return this.asAdmin().addStep(new FailStep<>(this.asAdmin()));
+    }
+
+    /**
+     * When triggered, immediately throws a {@code RuntimeException} which implements the {@link Failure} interface.
+     * The traversal will be terminated as a result.
+     *
+     * @param message the error message to include in the exception
+     * @return the traversal with an appended {@link FailStep}.
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#fail-step" target="_blank">Reference Documentation - Fail Step</a>
+     * @since 3.6.0
+     */
+    public default GraphTraversal<S, E> fail(final String message) {
+        this.asAdmin().getBytecode().addStep(Symbols.fail, message);
+        return this.asAdmin().addStep(new FailStep<>(this.asAdmin(), message));
+    }
+
+    /**
      * Aggregates the emanating paths into a {@link Tree} data structure.
      *
      * @param sideEffectKey the name of the side-effect key that will hold the tree
@@ -2222,18 +2263,6 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
-     * Filter all traversers in the traversal. This step has narrow use cases and is primarily intended for use as a
-     * signal to remote servers that {@link #iterate()} was called. While it may be directly used, it is often a sign
-     * that a traversal should be re-written in another form.
-     *
-     * @return the updated traversal with respective {@link NoneStep}.
-     */
-    @Override
-    default GraphTraversal<S, E> none() {
-        return (GraphTraversal<S, E>) Traversal.super.none();
-    }
-
-    /**
      * Sets a {@link Property} value and related meta properties if supplied, if supported by the {@link Graph}
      * and if the {@link Element} is a {@link VertexProperty}.  This method is the long-hand version of
      * {@link #property(Object, Object, Object...)} with the difference that the {@link VertexProperty.Cardinality}
@@ -3156,6 +3185,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         @Deprecated
         public static final String store = "store";
         public static final String aggregate = "aggregate";
+        public static final String fail = "fail";
         public static final String subgraph = "subgraph";
         public static final String barrier = "barrier";
         public static final String index = "index";
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
index aab83f4..08a4c07 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
@@ -912,6 +912,20 @@ public class __ {
     }
 
     /**
+     * @see GraphTraversal#fail()
+     */
+    public static <A>  GraphTraversal<A, A> fail() {
+        return __.<A>start().fail();
+    }
+
+    /**
+     * @see GraphTraversal#fail(String)
+     */
+    public static <A>  GraphTraversal<A, A> fail(final String message) {
+        return __.<A>start().fail(message);
+    }
+
+    /**
      * @see GraphTraversal#group(String)
      */
     public static <A> GraphTraversal<A, A> group(final String sideEffectKey) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/FailStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/FailStep.java
new file mode 100644
index 0000000..48741f8
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/FailStep.java
@@ -0,0 +1,87 @@
+/*
+ * 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.Failure;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Triggers an immediate failure of the traversal by throwing a {@code RuntimeException}. The exception thrown must
+ * implement the {@link Failure} interface.
+ */
+public class FailStep<S> extends SideEffectStep<S> {
+
+    protected String message;
+    protected Map<String,Object> metadata;
+
+    public FailStep(final Traversal.Admin traversal) {
+        this(traversal, "fail() step triggered");
+    }
+
+    public FailStep(final Traversal.Admin traversal, final String message) {
+        this(traversal, message, Collections.emptyMap());
+    }
+
+    public FailStep(final Traversal.Admin traversal, final String message, final Map<String,Object> metadata) {
+        super(traversal);
+        this.message = message;
+        this.metadata = metadata;
+    }
+
+    @Override
+    protected void sideEffect(final Traverser.Admin<S> traverser) {
+        throw new FailException(traversal, traverser, message, metadata);
+    }
+
+    /**
+     * Default {@link Failure} implementation that is thrown by {@link FailStep}.
+     */
+    public static class FailException extends RuntimeException implements Failure {
+        private final Map<String,Object> metadata;
+        private final Traversal.Admin traversal;
+        private final Traverser.Admin traverser;
+
+        public FailException(final Traversal.Admin traversal, final Traverser.Admin traverser,
+                             final String message, final Map<String,Object> metadata) {
+            super(message);
+            this.metadata = metadata;
+            this.traversal = traversal;
+            this.traverser = traverser;
+        }
+
+        @Override
+        public Map<String, Object> getMetadata() {
+            return metadata;
+        }
+
+        @Override
+        public Traverser.Admin getTraverser() {
+            return traverser;
+        }
+
+        @Override
+        public Traversal.Admin getTraversal() {
+            return traversal;
+        }
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java
index 59287ac..401bfd2 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter;
 
 import java.util.Iterator;
+import java.util.Set;
 import java.util.Stack;
 
 public class B_LP_NL_O_S_SE_SL_Traverser<T> extends B_LP_O_S_SE_SL_Traverser<T> {
@@ -65,6 +66,10 @@ public class B_LP_NL_O_S_SE_SL_Traverser<T> extends B_LP_O_S_SE_SL_Traverser<T>
                 this.loopNames.put(loopName, lc);
         }
     }
+    @Override
+    public Set<String> getLoopNames() {
+        return loopNames.keySet();
+    }
 
     @Override
     public void incrLoops() {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java
index 3a2aa38..1687909 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_O_S_SE_SL_Traverser.java
@@ -19,7 +19,6 @@
 package org.apache.tinkerpop.gremlin.process.traversal.traverser;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
-import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.ImmutablePath;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java
index 20ed1be..6e18d12 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java
@@ -18,7 +18,6 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.traverser;
 
-import org.apache.commons.collections.MapIterator;
 import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java
index 57fa47a..904d8c7 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java
@@ -22,7 +22,9 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 
+import java.util.Collections;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -72,6 +74,11 @@ public class B_O_S_SE_SL_Traverser<T> extends B_O_Traverser<T> {
     }
 
     @Override
+    public Set<String> getLoopNames() {
+        return Collections.singleton(loopName);
+    }
+
+    @Override
     public void initialiseLoops(final String stepLabel , final String loopName){
         this.loopName = loopName;
     }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java
index 3cb1dd3..77157b8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter;
 
 import java.util.Iterator;
+import java.util.Set;
 import java.util.Stack;
 
 public class LP_NL_O_OB_P_S_SE_SL_Traverser<T> extends LP_O_OB_P_S_SE_SL_Traverser<T> {
@@ -130,6 +131,11 @@ public class LP_NL_O_OB_P_S_SE_SL_Traverser<T> extends LP_O_OB_P_S_SE_SL_Travers
         super.merge(other);
     }
 
+    @Override
+    public Set<String> getLoopNames() {
+        return loopNames.keySet();
+    }
+
     /////////////////
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java
index a9cd7fb..14c2fe3 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter;
 
 import java.util.Iterator;
+import java.util.Set;
 import java.util.Stack;
 
 public class LP_NL_O_OB_S_SE_SL_Traverser<T> extends LP_O_OB_S_SE_SL_Traverser<T> {
@@ -58,6 +59,11 @@ public class LP_NL_O_OB_S_SE_SL_Traverser<T> extends LP_O_OB_S_SE_SL_Traverser<T
     }
 
     @Override
+    public Set<String> getLoopNames() {
+        return loopNames.keySet();
+    }
+
+    @Override
     public void initialiseLoops(final String stepLabel, final String loopName) {
         if (this.nestedLoops.empty() || !this.nestedLoops.peek().hasLabel(stepLabel)) {
             final LabelledCounter lc = new LabelledCounter(stepLabel, (short) 0);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_S_SE_SL_Traverser.java
index f6bb9cc..97038fe 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_O_OB_S_SE_SL_Traverser.java
@@ -20,7 +20,6 @@
 package org.apache.tinkerpop.gremlin.process.traversal.traverser;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
-import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.ImmutablePath;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java
index 72f43fe..c30e893 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter;
 
 import java.util.Iterator;
+import java.util.Set;
 import java.util.Stack;
 
 public class NL_O_OB_S_SE_SL_Traverser<T> extends O_OB_S_SE_SL_Traverser<T> {
@@ -130,6 +131,11 @@ public class NL_O_OB_S_SE_SL_Traverser<T> extends O_OB_S_SE_SL_Traverser<T> {
         super.merge(other);
     }
 
+    @Override
+    public Set<String> getLoopNames() {
+        return loopNames.keySet();
+    }
+
     /////////////////
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/O_OB_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/O_OB_S_SE_SL_Traverser.java
index 601bcda..73dc644 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/O_OB_S_SE_SL_Traverser.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/O_OB_S_SE_SL_Traverser.java
@@ -23,7 +23,9 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 
+import java.util.Collections;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -78,13 +80,18 @@ public class O_OB_S_SE_SL_Traverser<T> extends O_Traverser<T> {
 
     @Override
     public int loops(final String loopName) {
-        if (loopName == null || this.loopName != null && this.loopName.equals(loopName))
+        if (loopName == null || this.loopName.equals(loopName))
             return this.loops;
         else
             throw new IllegalArgumentException("Loop name not defined: " + loopName);
     }
 
     @Override
+    public Set<String> getLoopNames() {
+        return Collections.singleton(loopName);
+    }
+
+    @Override
     public void initialiseLoops(final String stepLabel , final String loopName){
         this.loopName = loopName;
     }
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index 328e506..ebd1cdf 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -543,6 +543,33 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Adds the fail step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, E> Fail ()
+        {
+            Bytecode.AddStep("fail");
+            return Wrap<S, E>(this);
+        }
+
+        /// <summary>
+        ///     Adds the fail step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, E> Fail (string msg)
+        {
+            Bytecode.AddStep("fail", msg);
+            return Wrap<S, E>(this);
+        }
+
+        /// <summary>
+        ///     Adds the fail step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, E> Fail (string msg, IDictionary<string,object> m)
+        {
+            Bytecode.AddStep("fail", msg, m);
+            return Wrap<S, E>(this);
+        }
+
+        /// <summary>
         ///     Adds the filter step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
         public GraphTraversal<S, E> Filter (IPredicate predicate)
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
index fa1c027..acad588 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
@@ -364,6 +364,22 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the fail step to that traversal.
+        /// </summary>
+        public static GraphTraversal<object, object> Fail()
+        {
+            return new GraphTraversal<object, object>().Fail();
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the fail step to that traversal.
+        /// </summary>
+        public static GraphTraversal<object, object> Fail(string msg)
+        {
+            return new GraphTraversal<object, object>().Fail(msg);
+        }
+
+        /// <summary>
         ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the filter step to that traversal.
         /// </summary>
         public static GraphTraversal<object, object> Filter(IPredicate predicate)
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
index 11e71c8..be8973c 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
@@ -47,6 +47,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
         private readonly IDictionary<string, object> _parameters = new Dictionary<string, object>();
         private ITraversal _traversal;
         private object[] _result;
+        private Exception _error = null;
         private static readonly JsonSerializerOptions JsonDeserializingOptions = new JsonSerializerOptions
             {PropertyNamingPolicy = JsonNamingPolicy.CamelCase};
         
@@ -157,11 +158,18 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
             }
             ITraversal t = _traversal;
             var list = new List<object>();
-            while (t.MoveNext())
+            try
             {
-                list.Add(t.Current);
+                while (t.MoveNext())
+                {
+                    list.Add(t.Current);
+                }
+                _result = list.ToArray();
+            }
+            catch (Exception ex)
+            {
+                _error = ex;
             }
-            _result = list.ToArray();
         }
 
         [When("iterated next")]
@@ -171,26 +179,44 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
             {
                 throw new InvalidOperationException("Traversal should be set before iterating");
             }
-            _traversal.MoveNext();
-            var result = _traversal.Current;
-            switch (result)
+
+            try
             {
-                case null:
-                    _result = null;
-                    return;
-                case object[] arrayResult:
-                    _result = arrayResult;
-                    return;
-                case IEnumerable enumerableResult:
-                    _result = enumerableResult.Cast<object>().ToArray();
-                    return;
+                _traversal.MoveNext();
+                var result = _traversal.Current;
+                switch (result)
+                {
+                    case null:
+                        _result = null;
+                        return;
+                    case object[] arrayResult:
+                        _result = arrayResult;
+                        return;
+                    case IEnumerable enumerableResult:
+                        _result = enumerableResult.Cast<object>().ToArray();
+                        return;
+                }
+            }
+            catch (Exception ex)
+            {
+                _error = ex;
             }
-            throw new InvalidCastException($"Can not convert instance of {result.GetType()} to object[]");
+        }
+
+        [Then("the traversal will raise an error")]
+        public void TraversalWillRaiseError()
+        {
+            Assert.NotNull(_error);
+
+            // consume the error now that it has been asserted
+            _error = null;
         }
 
         [Then("the result should be (\\w+)")]
         public void AssertResult(string characterizedAs, DataTable table = null)
         {
+            assertThatNoErrorWasThrown();
+
             var ordered = characterizedAs == "ordered";
             switch (characterizedAs)
             {
@@ -230,12 +256,16 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
         [Then("the result should have a count of (\\d+)")]
         public void AssertCount(int count)
         {
+            assertThatNoErrorWasThrown();
+
             Assert.Equal(count, _result.Length);
         }
 
         [Then("the graph should return (\\d+) for count of (.+)")]
         public void AssertTraversalCount(int expectedCount, string traversalText)
         {
+            assertThatNoErrorWasThrown();
+
             if (traversalText.StartsWith("\""))
             {
                 traversalText = traversalText.Substring(1, traversalText.Length - 2);
@@ -258,6 +288,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
             
         }
 
+        private void assertThatNoErrorWasThrown()
+        {
+            if (_error != null) throw _error;
+        }
+
         private static object ToMap(string stringMap, string graphName)
         {
             var jsonMap = JsonSerializer.Deserialize<JsonElement>(stringMap, JsonDeserializingOptions);
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 40bae6a..8977404 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -456,7 +456,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_name_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Max<object>()}}, 
                {"g_V_age_fold_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Fold().Max<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXageX_capXaX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Max<object>(Scope.Local)}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Max<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXageX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Max<object>()}}, 
+               {"g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy(productiveKeys: new List<object> {})).V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Max<object>()}}, 
                {"g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Max<object>(Scope.Local)}}, 
                {"g_V_aggregateXaX_byXfooX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Max<object>()}}, 
                {"g_V_foo_fold_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Max<object>(Scope.Local)}}, 
@@ -715,6 +717,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_V_aggregateXlocal_aX_byXoutEXcreatedX_countX_out_out_aggregateXlocal_aX_byXinEXcreatedX_weight_sumX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate(Scope.Local,"a").By(__.OutE("created").Count()).Out().Out().Aggregate(Scope.Local,"a").By(__.InE("created").Values<object>("weight").Sum<object>()).Cap<object>("a")}}, 
                {"g_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("x").By(__.Values<object>("age").Is(P.Gt(29))).Cap<object>("x")}}, 
                {"g_V_aggregateXxX_byXout_order_byXnameXX_capXxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("x").By(__.Out().Order().By("name")).Cap<object>("x")}}, 
+               {"g_V_fail", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fail()}}, 
+               {"g_V_failXmsgX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fail("msg")}}, 
+               {"g_V_unionXout_failX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Union<object>(__.Out(),__.Fail())}}, 
                {"g_V_group_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By("name")}}, 
                {"g_V_group_byXageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By("age")}}, 
                {"g_V_group_byXnameX_by", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Group<object,object>().By("name").By()}}, 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java
index c5936a2..ba9b6fd 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java
@@ -19,6 +19,7 @@
 package org.apache.tinkerpop.gremlin.driver;
 
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.process.traversal.Failure;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 
@@ -96,8 +97,22 @@ public final class Tokens {
 
     public static final String VAL_TRAVERSAL_SOURCE_ALIAS = "g";
 
+    /**
+     * The value of this key holds a string representation of the data held by a {@link Failure} as produced by
+     * {@link Failure#format()}.
+     */
+    public static final String STATUS_ATTRIBUTE_FAIL_STEP_MESSAGE = "failStepMessage";
+
+    /**
+     * Refers to the hierarchy of exception names for a particular exception thrown on the server.
+     */
     public static final String STATUS_ATTRIBUTE_EXCEPTIONS = "exceptions";
+
+    /**
+     * Refers to the stacktrace for an exception thrown on the server
+     */
     public static final String STATUS_ATTRIBUTE_STACK_TRACE = "stackTrace";
+
     /**
      * A {@link ResultSet#statusAttributes()} key for user-facing warnings.
      * <p>
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseStatusCode.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseStatusCode.java
index 4f43f65..88fce82 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseStatusCode.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseStatusCode.java
@@ -18,6 +18,9 @@
  */
 package org.apache.tinkerpop.gremlin.driver.message;
 
+import org.apache.tinkerpop.gremlin.process.traversal.Failure;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -114,6 +117,14 @@ public enum ResponseStatusCode {
     SERVER_ERROR(500),
 
     /**
+     * A server error that is produced when the {@link GraphTraversal#fail()} step is triggered. The returned exception
+     * will include information consistent with the {@link Failure} interface.
+     *
+     * @since 3.6.0
+     */
+    SERVER_ERROR_FAIL_STEP(595),
+
+    /**
      * A server error that indicates that the client should retry the request. A graph will typically return this error
      * when a transaction fails due to a locking exception or some other sort of concurrent modification. In other
      * words, the request was likely valid but the state of the server at the particular time the request arrived
diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/VarAsBindingASTTransformation.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/VarAsBindingASTTransformation.groovy
index 5cee02b..1da973d 100644
--- a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/VarAsBindingASTTransformation.groovy
+++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/VarAsBindingASTTransformation.groovy
@@ -115,6 +115,9 @@ class VarAsBindingASTTransformation implements ASTTransformation {
                                 case GraphTraversal.Symbols.by:
                                     if (i == 1) bindingValue = new PropertyExpression(new ClassExpression(new ClassNode(Order)), "desc")
                                     break
+                                case GraphTraversal.Symbols.fail:
+                                    if (i == 1) bindingValue = new MethodCallExpression(new ClassExpression(new ClassNode(Collections)), "emptyMap", new TupleExpression())
+                                    break
                             }
                             def bindingExpression = createBindingFromVar(entry.text, bindingVariableName, bindingValue)
                             bindingExpression.sourcePosition = entry
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
index e45e0e2..6c49ec5 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
@@ -500,6 +500,16 @@ class GraphTraversal extends Traversal {
     this.bytecode.addStep('emit', args);
     return this;
   }
+
+  /**
+   * Graph traversal fa method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  fail(...args) {
+    this.bytecode.addStep('fail', args);
+    return this;
+  }
   
   /**
    * Graph traversal filter method.
@@ -1325,6 +1335,7 @@ const statics = {
   drop: (...args) => callOnEmptyTraversal('drop', args),
   elementMap: (...args) => callOnEmptyTraversal('elementMap', args),
   emit: (...args) => callOnEmptyTraversal('emit', args),
+  fail: (...args) => callOnEmptyTraversal('fail', args),
   filter: (...args) => callOnEmptyTraversal('filter', args),
   flatMap: (...args) => callOnEmptyTraversal('flatMap', args),
   fold: (...args) => callOnEmptyTraversal('fold', args),
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
index dd5c562..5205f85 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
@@ -128,7 +128,7 @@ Given(/^using the parameter (.+) defined as "(.+)"$/, function (paramName, strin
 });
 
 When('iterated to list', function () {
-  return this.traversal.toList().then(list => this.result = list);
+  return this.traversal.toList().then(list => this.result = list).catch(err => this.result = err);
 });
 
 When('iterated next', function () {
@@ -138,10 +138,16 @@ When('iterated next', function () {
       // Compare using the objects array
       this.result = this.result.objects;
     }
-  });
+  }).catch(err => this.result = err);
+});
+
+Then('the traversal will raise an error', function() {
+  expect(this.result).to.be.a.instanceof(Error);
 });
 
 Then(/^the result should be (\w+)$/, function assertResult(characterizedAs, resultTable) {
+  expect(this.result).to.not.be.a.instanceof(Error);
+
   if (characterizedAs === 'empty') {
     expect(this.result).to.be.empty;
     if (typeof resultTable === 'function'){
@@ -165,6 +171,8 @@ Then(/^the result should be (\w+)$/, function assertResult(characterizedAs, resu
 });
 
 Then(/^the graph should return (\d+) for count of "(.+)"$/, function (stringCount, traversalText) {
+  expect(this.result).to.not.be.a.instanceof(Error);
+
   const p = Object.assign({}, this.parameters);
   p.g = this.g;
   const traversal = gremlin[this.scenario].shift()(p);
@@ -174,6 +182,8 @@ Then(/^the graph should return (\d+) for count of "(.+)"$/, function (stringCoun
 });
 
 Then(/^the result should have a count of (\d+)$/, function (stringCount) {
+  expect(this.result).to.not.be.a.instanceof(Error);
+
   const expected = parseInt(stringCount, 10);
   if (!Array.isArray(this.result)) {
     let count = 0;
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 6f49924..d303f97 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -703,6 +703,9 @@ const gremlins = {
     g_V_aggregateXlocal_aX_byXoutEXcreatedX_countX_out_out_aggregateXlocal_aX_byXinEXcreatedX_weight_sumX: [function({g}) { return g.V().aggregate(Scope.local,"a").by(__.outE("created").count()).out().out().aggregate(Scope.local,"a").by(__.inE("created").values("weight").sum()).cap("a") }], 
     g_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX: [function({g}) { return g.V().aggregate("x").by(__.values("age").is(P.gt(29))).cap("x") }], 
     g_V_aggregateXxX_byXout_order_byXnameXX_capXxX: [function({g}) { return g.V().aggregate("x").by(__.out().order().by("name")).cap("x") }], 
+    g_V_fail: [function({g}) { return g.V().fail() }], 
+    g_V_failXmsgX: [function({g}) { return g.V().fail("msg") }], 
+    g_V_unionXout_failX: [function({g}) { return g.V().union(__.out(),__.fail()) }], 
     g_V_group_byXnameX: [function({g}) { return g.V().group().by("name") }], 
     g_V_group_byXageX: [function({g}) { return g.V().group().by("age") }], 
     g_V_group_byXnameX_by: [function({g}) { return g.V().group().by("name").by() }], 
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4
index a1ebb53..4cbc8a4 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -240,6 +240,7 @@ traversalMethod
 	| traversalMethod_subgraph
 	| traversalMethod_sum
 	| traversalMethod_tail
+	| traversalMethod_fail
 	| traversalMethod_timeLimit
 	| traversalMethod_times
 	| traversalMethod_to
@@ -668,6 +669,11 @@ traversalMethod_tail
 	| 'tail' LPAREN integerLiteral RPAREN #traversalMethod_tail_long
 	;
 
+traversalMethod_fail
+	: 'fail' LPAREN RPAREN #traversalMethod_fail_Empty
+	| 'fail' LPAREN stringLiteral RPAREN #traversalMethod_fail_String
+	;
+
 traversalMethod_timeLimit
 	: 'timeLimit' LPAREN integerLiteral RPAREN
 	;
diff --git a/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py
index b56988b..770821e 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py
@@ -253,6 +253,10 @@ class GraphTraversal(Traversal):
         self.bytecode.add_step("emit", *args)
         return self
 
+    def fail(self, *args):
+        self.bytecode.add_step("fail", *args)
+        return self
+
     def filter_(self, *args):
         self.bytecode.add_step("filter", *args)
         return self
@@ -679,6 +683,10 @@ class __(object, metaclass=MagicType):
         return cls.graph_traversal(None, None, Bytecode()).emit(*args)
 
     @classmethod
+    def fail(cls, *args):
+        return cls.graph_traversal(None, None, Bytecode()).fail(*args)
+
+    @classmethod
     def filter_(cls, *args):
         return cls.graph_traversal(None, None, Bytecode()).filter_(*args)
 
@@ -1039,6 +1047,10 @@ def emit(*args):
     return __.emit(*args)
 
 
+def fail(*args):
+    return __.fail(*args)
+
+
 def filter_(*args):
     return __.filter_(*args)
 
@@ -1355,6 +1367,8 @@ statics.add_static('elementMap', elementMap)
 
 statics.add_static('emit', emit)
 
+statics.add_static('fail', fail)
+
 statics.add_static('filter_', filter_)
 
 statics.add_static('flatMap', flatMap)
diff --git a/gremlin-python/src/main/python/radish/feature_steps.py b/gremlin-python/src/main/python/radish/feature_steps.py
index 9fd8cf6..a1f76ba 100644
--- a/gremlin-python/src/main/python/radish/feature_steps.py
+++ b/gremlin-python/src/main/python/radish/feature_steps.py
@@ -95,8 +95,12 @@ def translate_traversal(step):
 def iterate_the_traversal(step):
     if step.context.ignore:
         return
-    
-    step.context.result = list(map(lambda x: _convert_results(x), step.context.traversal.toList()))
+
+    try:
+        step.context.result = list(map(lambda x: _convert_results(x), step.context.traversal.toList()))
+        step.context.failed = False
+    except:
+        step.context.failed = True
 
 
 @when("iterated next")
@@ -104,14 +108,24 @@ def next_the_traversal(step):
     if step.context.ignore:
         return
 
-    step.context.result = list(map(lambda x: _convert_results(x), step.context.traversal.next()))
+    try:
+        step.context.result = list(map(lambda x: _convert_results(x), step.context.traversal.next()))
+        step.context.failed = False
+    except:
+        step.context.failed = True
+
 
+@then("the traversal will raise an error")
+def raise_an_error(step):
+    assert_that(step.context.failed, equal_to(True))
 
 @then("the result should be {characterized_as:w}")
 def assert_result(step, characterized_as):
     if step.context.ignore:
         return
 
+    assert_that(step.context.failed, equal_to(False))
+
     if characterized_as == "empty":        # no results
         assert_that(len(step.context.result), equal_to(0))
     elif characterized_as == "ordered":    # results asserted in the order of the data table
@@ -129,6 +143,8 @@ def assert_side_effects(step, count, traversal_string):
     if step.context.ignore:
         return
 
+    assert_that(step.context.failed, equal_to(False))
+
     p = step.context.traversal_params if hasattr(step.context, "traversal_params") else {}
     p['g'] = step.context.g
     t = step.context.traversals.pop(0)(**p)
@@ -141,6 +157,8 @@ def assert_count(step, count):
     if step.context.ignore:
         return
 
+    assert_that(step.context.failed, equal_to(False))
+
     assert_that(len(list(step.context.result)), equal_to(count))
 
 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 652d491..13900d6 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -688,6 +688,9 @@ world.gremlins = {
     'g_V_aggregateXlocal_aX_byXoutEXcreatedX_countX_out_out_aggregateXlocal_aX_byXinEXcreatedX_weight_sumX': [(lambda g:g.V().aggregate(Scope.local,'a').by(__.outE('created').count()).out().out().aggregate(Scope.local,'a').by(__.inE('created').weight.sum_()).cap('a'))], 
     'g_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX': [(lambda g:g.V().aggregate('x').by(__.age.is_(P.gt(29))).cap('x'))], 
     'g_V_aggregateXxX_byXout_order_byXnameXX_capXxX': [(lambda g:g.V().aggregate('x').by(__.out().order().by('name')).cap('x'))], 
+    'g_V_fail': [(lambda g:g.V().fail())], 
+    'g_V_failXmsgX': [(lambda g:g.V().fail('msg'))], 
+    'g_V_unionXout_failX': [(lambda g:g.V().union(__.out(),__.fail()))], 
     'g_V_group_byXnameX': [(lambda g:g.V().group().by('name'))], 
     'g_V_group_byXageX': [(lambda g:g.V().group().by('age'))], 
     'g_V_group_byXnameX_by': [(lambda g:g.V().group().by('name').by())], 
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
index 65c26be..ed63dc6 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
@@ -35,6 +35,7 @@ import org.apache.tinkerpop.gremlin.groovy.jsr223.TimedInterruptTimeoutException
 import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
 import org.apache.tinkerpop.gremlin.jsr223.JavaTranslator;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Failure;
 import org.apache.tinkerpop.gremlin.process.traversal.GraphOp;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
@@ -278,14 +279,20 @@ public abstract class AbstractSession implements Session, AutoCloseable {
     protected void handleException(final SessionTask sessionTask, final Throwable t) throws SessionException {
         if (t instanceof SessionException) throw (SessionException) t;
 
-        final Optional<Throwable> possibleTemporaryException = determineIfTemporaryException(t);
-        if (possibleTemporaryException.isPresent()) {
-            final Throwable temporaryException = possibleTemporaryException.get();
-            throw new SessionException(temporaryException.getMessage(), t,
-                    ResponseMessage.build(sessionTask.getRequestMessage())
-                            .code(ResponseStatusCode.SERVER_ERROR_TEMPORARY)
-                            .statusMessage(temporaryException.getMessage())
-                            .statusAttributeException(temporaryException).create());
+        final Optional<Throwable> possibleSpecialException = determineIfSpecialException(t);
+        if (possibleSpecialException.isPresent()) {
+            final Throwable special = possibleSpecialException.get();
+            final ResponseMessage.Builder specialResponseMsg = ResponseMessage.build(sessionTask.getRequestMessage()).
+                    statusMessage(special.getMessage()).
+                    statusAttributeException(special);
+            if (special instanceof TemporaryException) {
+                specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_TEMPORARY);
+            } else if (special instanceof Failure) {
+                final Failure failure = (Failure) special;
+                specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_FAIL_STEP).
+                        statusAttribute(Tokens.STATUS_ATTRIBUTE_FAIL_STEP_MESSAGE, failure.format());
+            }
+            throw new SessionException(special.getMessage(), specialResponseMsg.create());
         }
 
         final Throwable root = ExceptionUtils.getRootCause(t);
@@ -373,12 +380,12 @@ public abstract class AbstractSession implements Session, AutoCloseable {
     }
 
     /**
-     * Check if any exception in the chain is TemporaryException then we should respond with the right error code so
-     * that the client knows to retry.
+     * Check if any exception in the chain is {@link TemporaryException} or {@link Failure} then respond with the
+     * right error code so that the client knows to retry.
      */
-    protected Optional<Throwable> determineIfTemporaryException(final Throwable ex) {
+    protected static Optional<Throwable> determineIfSpecialException(final Throwable ex) {
         return Stream.of(ExceptionUtils.getThrowables(ex)).
-                filter(i -> i instanceof TemporaryException).findFirst();
+                filter(i -> i instanceof TemporaryException || i instanceof Failure).findFirst();
     }
 
     /**
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java
index 2a8ecbe..cbd14a1 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
 import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.TimedInterruptTimeoutException;
+import org.apache.tinkerpop.gremlin.process.traversal.Failure;
 import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.Pop;
@@ -38,6 +39,7 @@ import org.apache.tinkerpop.gremlin.server.Context;
 import org.apache.tinkerpop.gremlin.server.GremlinServer;
 import org.apache.tinkerpop.gremlin.server.Settings;
 import org.apache.tinkerpop.gremlin.server.util.MetricManager;
+import org.apache.tinkerpop.gremlin.structure.util.TemporaryException;
 import org.apache.tinkerpop.gremlin.util.function.ThrowingConsumer;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.codehaus.groovy.control.MultipleCompilationErrorsException;
@@ -272,13 +274,22 @@ public abstract class AbstractEvalOpProcessor extends AbstractOpProcessor {
                 timerContext.stop();
 
                 if (t != null) {
-                    // if any exception in the chain is TemporaryException then we should respond with the right error
-                    // code so that the client knows to retry
-                    final Optional<Throwable> possibleTemporaryException = determineIfTemporaryException(t);
-                    if (possibleTemporaryException.isPresent()) {
-                        ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TEMPORARY)
-                                .statusMessage(possibleTemporaryException.get().getMessage())
-                                .statusAttributeException(possibleTemporaryException.get()).create());
+                    // if any exception in the chain is TemporaryException or Failure then we should respond with the
+                    // right error code so that the client knows to retry
+                    final Optional<Throwable> possibleSpecialException = determineIfSpecialException(t);
+                    if (possibleSpecialException.isPresent()) {
+                        final Throwable special = possibleSpecialException.get();
+                        final ResponseMessage.Builder specialResponseMsg = ResponseMessage.build(msg).
+                                statusMessage(special.getMessage()).
+                                statusAttributeException(special);
+                        if (special instanceof TemporaryException) {
+                            specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_TEMPORARY);
+                        } else if (special instanceof Failure) {
+                            final Failure failure = (Failure) special;
+                            specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_FAIL_STEP).
+                                    statusAttribute(Tokens.STATUS_ATTRIBUTE_FAIL_STEP_MESSAGE, failure.format());
+                        }
+                        ctx.writeAndFlush(specialResponseMsg.create());
                     } else if (t instanceof OpProcessorException) {
                         ctx.writeAndFlush(((OpProcessorException) t).getResponseMessage());
                     } else if (t instanceof TimedInterruptTimeoutException) {
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
index 1498c51..c11af3b 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
 import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
+import org.apache.tinkerpop.gremlin.process.traversal.Failure;
 import org.apache.tinkerpop.gremlin.server.Context;
 import org.apache.tinkerpop.gremlin.server.GraphManager;
 import org.apache.tinkerpop.gremlin.server.OpProcessor;
@@ -67,12 +68,12 @@ public abstract class AbstractOpProcessor implements OpProcessor {
     }
 
     /**
-     * Check if any exception in the chain is TemporaryException then we should respond with the right error code so
-     * that the client knows to retry.
+     * Check if any exception in the chain is {@link TemporaryException} or {@link Failure} then respond with the
+     * right error code so that the client knows to retry.
      */
-    protected static Optional<Throwable> determineIfTemporaryException(final Throwable ex) {
+    protected static Optional<Throwable> determineIfSpecialException(final Throwable ex) {
         return Stream.of(ExceptionUtils.getThrowables(ex)).
-                filter(i -> i instanceof TemporaryException).findFirst();
+                filter(i -> i instanceof TemporaryException || i instanceof Failure).findFirst();
     }
 
     /**
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
index 6a18645..a5df693 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
@@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyCompilerGremlinPlugin;
 import org.apache.tinkerpop.gremlin.jsr223.JavaTranslator;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Failure;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.util.BytecodeHelper;
@@ -46,6 +47,7 @@ import org.apache.tinkerpop.gremlin.server.util.TraverserIterator;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion;
+import org.apache.tinkerpop.gremlin.structure.util.TemporaryException;
 import org.apache.tinkerpop.gremlin.util.function.ThrowingConsumer;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
 import org.slf4j.Logger;
@@ -419,13 +421,22 @@ public class SessionOpProcessor extends AbstractEvalOpProcessor {
                     if (ex instanceof UndeclaredThrowableException)
                         t = t.getCause();
 
-                    // if any exception in the chain is TemporaryException then we should respond with the right error
-                    // code so that the client knows to retry
-                    final Optional<Throwable> possibleTemporaryException = determineIfTemporaryException(ex);
-                    if (possibleTemporaryException.isPresent()) {
-                        context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TEMPORARY)
-                                .statusMessage(possibleTemporaryException.get().getMessage())
-                                .statusAttributeException(possibleTemporaryException.get()).create());
+                    // if any exception in the chain is TemporaryException or Failure then we should respond with the
+                    // right error code so that the client knows to retry
+                    final Optional<Throwable> possibleSpecialException = determineIfSpecialException(ex);
+                    if (possibleSpecialException.isPresent()) {
+                        final Throwable special = possibleSpecialException.get();
+                        final ResponseMessage.Builder specialResponseMsg = ResponseMessage.build(msg).
+                                statusMessage(special.getMessage()).
+                                statusAttributeException(special);
+                        if (special instanceof TemporaryException) {
+                            specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_TEMPORARY);
+                        } else if (special instanceof Failure) {
+                            final Failure failure = (Failure) special;
+                            specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_FAIL_STEP).
+                                    statusAttribute(Tokens.STATUS_ATTRIBUTE_FAIL_STEP_MESSAGE, failure.format());
+                        }
+                        context.writeAndFlush(specialResponseMsg.create());
                     } else if (t instanceof InterruptedException || t instanceof TraversalInterruptedException) {
                         final String errorMessage = String.format("A timeout occurred during traversal evaluation of [%s] - consider increasing the limit given to evaluationTimeout", msg);
                         logger.warn(errorMessage);
@@ -441,11 +452,22 @@ public class SessionOpProcessor extends AbstractEvalOpProcessor {
                     onError(graph, context);
                 }
             } catch (Exception ex) {
-                final Optional<Throwable> possibleTemporaryException = determineIfTemporaryException(ex);
-                if (possibleTemporaryException.isPresent()) {
-                    context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TEMPORARY)
-                            .statusMessage(possibleTemporaryException.get().getMessage())
-                            .statusAttributeException(possibleTemporaryException.get()).create());
+                // if any exception in the chain is TemporaryException or Failure then we should respond with the
+                // right error code so that the client knows to retry
+                final Optional<Throwable> possibleSpecialException = determineIfSpecialException(ex);
+                if (possibleSpecialException.isPresent()) {
+                    final Throwable special = possibleSpecialException.get();
+                    final ResponseMessage.Builder specialResponseMsg = ResponseMessage.build(msg).
+                            statusMessage(special.getMessage()).
+                            statusAttributeException(special);
+                    if (special instanceof TemporaryException) {
+                        specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_TEMPORARY);
+                    } else if (special instanceof Failure) {
+                        final Failure failure = (Failure) special;
+                        specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_FAIL_STEP).
+                                statusAttribute(Tokens.STATUS_ATTRIBUTE_FAIL_STEP_MESSAGE, failure.format());
+                    }
+                    context.writeAndFlush(specialResponseMsg.create());
                 } else {
                     logger.warn(String.format("Exception processing a Traversal on request [%s].", msg.getRequestId()), ex);
                     context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
@@ -501,11 +523,22 @@ public class SessionOpProcessor extends AbstractEvalOpProcessor {
                                 .create());
 
                     } catch (Exception ex) {
-                        final Optional<Throwable> possibleTemporaryException = determineIfTemporaryException(ex);
-                        if (possibleTemporaryException.isPresent()) {
-                            context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TEMPORARY)
-                                    .statusMessage(possibleTemporaryException.get().getMessage())
-                                    .statusAttributeException(possibleTemporaryException.get()).create());
+                        // if any exception in the chain is TemporaryException or Failure then we should respond with the
+                        // right error code so that the client knows to retry
+                        final Optional<Throwable> possibleSpecialException = determineIfSpecialException(ex);
+                        if (possibleSpecialException.isPresent()) {
+                            final Throwable special = possibleSpecialException.get();
+                            final ResponseMessage.Builder specialResponseMsg = ResponseMessage.build(msg).
+                                    statusMessage(special.getMessage()).
+                                    statusAttributeException(special);
+                            if (special instanceof TemporaryException) {
+                                specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_TEMPORARY);
+                            } else if (special instanceof Failure) {
+                                final Failure failure = (Failure) special;
+                                specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_FAIL_STEP).
+                                        statusAttribute(Tokens.STATUS_ATTRIBUTE_FAIL_STEP_MESSAGE, failure.format());
+                            }
+                            context.writeAndFlush(specialResponseMsg.create());
                         } else {
                             logger.warn(String.format("Exception processing a Traversal on request [%s].", msg.getRequestId()), ex);
                             context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java
index 80be01f..620332f 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java
@@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
 import org.apache.tinkerpop.gremlin.jsr223.JavaTranslator;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Failure;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.util.BytecodeHelper;
@@ -38,13 +39,13 @@ import org.apache.tinkerpop.gremlin.server.OpProcessor;
 import org.apache.tinkerpop.gremlin.server.Settings;
 import org.apache.tinkerpop.gremlin.server.auth.AuthenticatedUser;
 import org.apache.tinkerpop.gremlin.server.handler.Frame;
-import org.apache.tinkerpop.gremlin.server.handler.SessionException;
 import org.apache.tinkerpop.gremlin.server.handler.StateKey;
 import org.apache.tinkerpop.gremlin.server.op.AbstractOpProcessor;
 import org.apache.tinkerpop.gremlin.server.op.OpProcessorException;
 import org.apache.tinkerpop.gremlin.server.util.MetricManager;
 import org.apache.tinkerpop.gremlin.server.util.TraverserIterator;
 import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.util.TemporaryException;
 import org.apache.tinkerpop.gremlin.util.function.ThrowingConsumer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -222,13 +223,22 @@ public class TraversalOpProcessor extends AbstractOpProcessor {
                     if (ex instanceof UndeclaredThrowableException)
                         t = t.getCause();
 
-                    // if any exception in the chain is TemporaryException then we should respond with the right error
-                    // code so that the client knows to retry
-                    final Optional<Throwable> possibleTemporaryException = determineIfTemporaryException(ex);
-                    if (possibleTemporaryException.isPresent()) {
-                        context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TEMPORARY)
-                                .statusMessage(possibleTemporaryException.get().getMessage())
-                                .statusAttributeException(possibleTemporaryException.get()).create());
+                    // if any exception in the chain is TemporaryException or Failure then we should respond with the
+                    // right error code so that the client knows to retry
+                    final Optional<Throwable> possibleSpecialException = determineIfSpecialException(ex);
+                    if (possibleSpecialException.isPresent()) {
+                        final Throwable special = possibleSpecialException.get();
+                        final ResponseMessage.Builder specialResponseMsg = ResponseMessage.build(msg).
+                                statusMessage(special.getMessage()).
+                                statusAttributeException(special);
+                        if (special instanceof TemporaryException) {
+                            specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_TEMPORARY);
+                        } else if (special instanceof Failure) {
+                            final Failure failure = (Failure) special;
+                            specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_FAIL_STEP).
+                                    statusAttribute(Tokens.STATUS_ATTRIBUTE_FAIL_STEP_MESSAGE, failure.format());
+                        }
+                        context.writeAndFlush(specialResponseMsg.create());
                     } else if (t instanceof InterruptedException || t instanceof TraversalInterruptedException) {
                         final String errorMessage = String.format("A timeout occurred during traversal evaluation of [%s] - consider increasing the limit given to evaluationTimeout", msg);
                         logger.warn(errorMessage);
@@ -244,11 +254,22 @@ public class TraversalOpProcessor extends AbstractOpProcessor {
                     onError(graph, context);
                 }
             } catch (Exception ex) {
-                final Optional<Throwable> possibleTemporaryException = determineIfTemporaryException(ex);
-                if (possibleTemporaryException.isPresent()) {
-                    context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TEMPORARY)
-                            .statusMessage(possibleTemporaryException.get().getMessage())
-                            .statusAttributeException(possibleTemporaryException.get()).create());
+                // if any exception in the chain is TemporaryException or Failure then we should respond with the
+                // right error code so that the client knows to retry
+                final Optional<Throwable> possibleSpecialException = determineIfSpecialException(ex);
+                if (possibleSpecialException.isPresent()) {
+                    final Throwable special = possibleSpecialException.get();
+                    final ResponseMessage.Builder specialResponseMsg = ResponseMessage.build(msg).
+                            statusMessage(special.getMessage()).
+                            statusAttributeException(special);
+                    if (special instanceof TemporaryException) {
+                        specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_TEMPORARY);
+                    } else if (special instanceof Failure) {
+                        final Failure failure = (Failure) special;
+                        specialResponseMsg.code(ResponseStatusCode.SERVER_ERROR_FAIL_STEP).
+                                statusAttribute(Tokens.STATUS_ATTRIBUTE_FAIL_STEP_MESSAGE, failure.format());
+                    }
+                    context.writeAndFlush(specialResponseMsg.create());
                 } else {
                     logger.warn(String.format("Exception processing a Traversal on request [%s].", msg.getRequestId()), ex);
                     context.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
index 2f88ddc..dd9b682 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
@@ -1083,4 +1083,20 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
             assertEquals(ResponseStatusCode.SERVER_ERROR_TEMPORARY, ((ResponseException) t).getResponseStatusCode());
         }
     }
+
+    @Test
+    public void shouldGenerateFailureErrorResponseStatusCode() throws Exception {
+        final Cluster cluster = TestClientFactory.build().create();
+        final Client client = cluster.connect();
+
+        try {
+            client.submit("g.inject(0).fail('make it stop')").all().get();
+            fail("Should have tanked since we used fail() step");
+        } catch (Exception ex) {
+            final Throwable t = ex.getCause();
+            assertThat(t, instanceOf(ResponseException.class));
+            assertEquals("make it stop", t.getMessage());
+            assertEquals(ResponseStatusCode.SERVER_ERROR_FAIL_STEP, ((ResponseException) t).getResponseStatusCode());
+        }
+    }
 }
diff --git a/gremlin-test/features/sideEffect/Fail.feature b/gremlin-test/features/sideEffect/Fail.feature
new file mode 100644
index 0000000..53f2d56
--- /dev/null
+++ b/gremlin-test/features/sideEffect/Fail.feature
@@ -0,0 +1,46 @@
+# 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.
+
+@StepClassSideEffect @StepFail
+Feature: Step - fail()
+
+  Scenario: g_V_fail
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().fail()
+      """
+    When iterated to list
+    Then the traversal will raise an error
+
+  Scenario: g_V_failXmsgX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().fail("msg")
+      """
+    When iterated to list
+    Then the traversal will raise an error
+
+  Scenario: g_V_unionXout_failX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().union(out(), fail())
+      """
+    When iterated to list
+    Then the traversal will raise an error
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
index 6b644a0..daeff68 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
@@ -53,6 +53,7 @@ import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 import static org.hamcrest.core.Every.everyItem;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
 import java.math.BigDecimal;
@@ -81,6 +82,7 @@ public final class StepDefinition {
     private final Map<String, String> stringParameters = new HashMap<>();
     private Traversal traversal;
     private Object result;
+    private Throwable error;
     private static final Pattern edgeTripletPattern = Pattern.compile("(.+)-(.+)->(.+)");
     private static final Pattern ioPattern = Pattern.compile("g\\.io\\(\"(.*)\"\\).*");
     private List<Pair<Pattern, Function<String,String>>> stringMatcherConverters = new ArrayList<Pair<Pattern, Function<String,String>>>() {{
@@ -204,6 +206,7 @@ public final class StepDefinition {
         }
 
         if (result != null) result = null;
+        if (error != null) error = null;
     }
 
     @After
@@ -243,16 +246,26 @@ public final class StepDefinition {
 
     @When("iterated to list")
     public void iteratedToList() {
-        result = traversal.toList();
+        try {
+            result = traversal.toList();
+        } catch (Exception ex) {
+            error = ex;
+        }
     }
 
     @When("iterated next")
     public void iteratedNext() {
-        result = traversal.next();
+        try {
+            result = traversal.next();
+        } catch (Exception ex) {
+            error = ex;
+        }
     }
 
     @Then("the result should be unordered")
     public void theResultShouldBeUnordered(final DataTable dataTable) {
+        assertThatNoErrorWasThrown();
+
         final List<Object> actual = translateResultsToActual();
 
         // account for header in dataTable size
@@ -265,6 +278,8 @@ public final class StepDefinition {
 
     @Then("the result should be ordered")
     public void theResultShouldBeOrdered(final DataTable dataTable) {
+        assertThatNoErrorWasThrown();
+
         final List<Object> actual = translateResultsToActual();
 
         // account for header in dataTable size
@@ -277,6 +292,9 @@ public final class StepDefinition {
 
     @Then("the result should be of")
     public void theResultShouldBeOf(final DataTable dataTable) {
+        assertThatNoErrorWasThrown();
+
+
         final List<Object> actual = translateResultsToActual();
 
         // skip the header in the dataTable
@@ -286,6 +304,8 @@ public final class StepDefinition {
 
     @Then("the result should have a count of {int}")
     public void theResultShouldHaveACountOf(final Integer val) {
+        assertThatNoErrorWasThrown();
+
         if (result instanceof Iterable)
             assertEquals(val.intValue(), IteratorUtils.count((Iterable) result));
         else if (result instanceof Map)
@@ -296,15 +316,27 @@ public final class StepDefinition {
 
     @Then("the graph should return {int} for count of {string}")
     public void theGraphShouldReturnForCountOf(final Integer count, final String gremlin) {
+        assertThatNoErrorWasThrown();
+
         assertEquals(count.longValue(), ((GraphTraversal) parseGremlin(applyParameters(gremlin))).count().next());
     }
 
     @Then("the result should be empty")
     public void theResultShouldBeEmpty() {
+        assertThatNoErrorWasThrown();
+
         assertThat(result, instanceOf(Collection.class));
         assertEquals(0, IteratorUtils.count((Collection) result));
     }
 
+    @Then("the traversal will raise an error")
+    public void theTraversalWillRaiseAnError() {
+        assertNotNull(error);
+
+        // consume the error now that it has been asserted
+        error = null;
+    }
+
     //////////////////////////////////////////////
 
     @Given("an unsupported test")
@@ -319,6 +351,10 @@ public final class StepDefinition {
 
     //////////////////////////////////////////////
 
+    private void assertThatNoErrorWasThrown() {
+        if (error != null) throw new RuntimeException(error);
+    }
+
     private Traversal parseGremlin(final String script) {
         final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString(script));
         final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer));