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 2018/06/01 14:51:53 UTC

[1/2] tinkerpop git commit: TINKERPOP-1975 Introduced with() step and Configuring interface [Forced Update!]

Repository: tinkerpop
Updated Branches:
  refs/heads/TINKERPOP-1967 6ca93b234 -> ee40274f7 (forced update)


TINKERPOP-1975 Introduced with() step and Configuring interface

Moved Parameterizing to the package containing the other step interfaces. Changed interface hierarchy with Parameterizing and Mutating now that Configuring is present. Both of those changes make this body of work a breaking change to existing APIs though it should really only affect graph providers and even then the fix is quite simple as described in the upgrade documentation.


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

Branch: refs/heads/TINKERPOP-1967
Commit: fc0b2dc5c2f0ecce9a1690df36dc9b061dad5d1b
Parents: 23b766a
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu May 31 12:55:13 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Jun 1 10:01:46 2018 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |  4 ++
 docs/src/reference/the-traversal.asciidoc       | 30 ++++++--
 docs/src/upgrade/release-3.4.x.asciidoc         | 49 +++++++++++++
 .../tinkerpop/gremlin/jsr223/CoreImports.java   |  2 +
 .../step/map/PageRankVertexProgramStep.java     | 75 ++++++++++++++++++--
 .../step/map/PeerPressureVertexProgramStep.java | 74 +++++++++++++++++--
 .../process/traversal/Parameterizing.java       | 37 ----------
 .../traversal/dsl/graph/GraphTraversal.java     | 24 ++++++-
 .../process/traversal/step/Configuring.java     | 41 +++++++++++
 .../process/traversal/step/Mutating.java        |  5 +-
 .../process/traversal/step/Parameterizing.java  | 39 ++++++++++
 .../process/traversal/step/filter/DropStep.java |  9 ++-
 .../traversal/step/map/AddEdgeStartStep.java    |  6 +-
 .../process/traversal/step/map/AddEdgeStep.java |  7 +-
 .../traversal/step/map/AddVertexStartStep.java  |  7 +-
 .../traversal/step/map/AddVertexStep.java       |  7 +-
 .../step/sideEffect/AddPropertyStep.java        |  9 +--
 .../process/traversal/step/util/Parameters.java |  2 +
 .../strategy/decoration/ElementIdStrategy.java  |  2 +-
 .../strategy/decoration/PartitionStrategy.java  |  6 +-
 .../traversal/dsl/graph/GraphTraversalTest.java |  2 +-
 .../Process/Traversal/GraphTraversal.cs         |  4 +-
 gremlin-javascript/glv/generate.groovy          |  3 +-
 .../lib/process/graph-traversal.js              |  2 +-
 gremlin-python/glv/generate.groovy              |  1 +
 .../gremlin_python/process/graph_traversal.py   |  2 +-
 .../traversal/step/map/PageRankTest.java        | 41 +++++++++++
 .../traversal/step/map/PeerPressureTest.java    | 31 +++++++-
 28 files changed, 427 insertions(+), 94 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 92d00b2..964bfda 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,10 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 
 This release also includes changes from <<release-3-3-3, 3.3.3>>.
 
+* Moved `Parameterizing` interface to the `org.apache.tinkerpop.gremlin.process.traversal.step` package with other marker interfaces of its type.
+* Replaced `Parameterizing.addPropertyMutations()` with `Configuring.configure()`.
+* Changed interface hierarchy for `Parameterizing` and `Mutating` interfaces as they are tightly related.
+* Introduced the `with()` step modulator which can supply configuration options to `Configuring` steps.
 * Added `supportsUpsert()` option to `VertexFeatures` and `EdgeFeatures`.
 * `min()` and `max()` now support all types implementing `Comparable`.
 * Change the `toString()` of `Path` to be standardized as other graph elements are.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index b3e5ef5..0d7d67f 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -1705,11 +1705,15 @@ g = graph.traversal().withComputer()
 g.V().pageRank().by('pageRank').values('pageRank')
 g.V().hasLabel('person').
   pageRank().
-    by(outE('knows')).
-    by('friendRank').
+    with(PageRank.EDGES, __.outE('knows')).
+    with(PageRank.PROPERTY_NAME, 'friendRank').
   order().by('friendRank',desc).valueMap('name','friendRank')
 ----
 
+Note the use of the `with()` modulating step which provides configuration options to the algorithm. It takes
+configuration keys from the static `PageRank` inner class on `PageRankVertexProgramStep` and is automatically
+imported to the Gremlin Console.
+
 The <<explain-step,`explain()`>>-step can be used to understand how the traversal is compiled into multiple `GraphComputer` jobs.
 
 [gremlin-groovy,modern]
@@ -1717,8 +1721,8 @@ The <<explain-step,`explain()`>>-step can be used to understand how the traversa
 g = graph.traversal().withComputer()
 g.V().hasLabel('person').
   pageRank().
-    by(outE('knows')).
-    by('friendRank').
+    with(PageRank.EDGES, __.outE('knows')).
+    with(PageRank.PROPERTY_NAME, 'friendRank').
   order().by('friendRank',desc).valueMap('name','friendRank').explain()
 ----
 
@@ -1812,10 +1816,17 @@ IMPORTANT: The `peerPressure()`-step is a `VertexComputing`-step and as such, ca
 g = graph.traversal().withComputer()
 g.V().peerPressure().by('cluster').values('cluster')
 g.V().hasLabel('person').
-  peerPressure().by('cluster').
-  group().by('cluster').by('name')
+  peerPressure().
+    with(PeerPressure.PROPERTY_NAME, 'cluster').
+  group().
+    by('cluster').
+    by('name')
 ----
 
+Note the use of the `with()` modulating step which provides configuration options to the algorithm. It takes
+configuration keys from the static `PeerPressure` inner class on `PeerPressureVertexProgramStep` and is automatically
+imported to the Gremlin Console.
+
 *Additional References*
 
 link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#peerPressure--++[`peerPressure()`]
@@ -2970,6 +2981,13 @@ link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/grem
 link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#where-org.apache.tinkerpop.gremlin.process.traversal.Traversal-++[`where(Traversal)`],
 link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/P.html++[`P`]
 
+[[with-step]]
+=== With Step
+
+The `with()`-step is not an actual step, but is instead a "step modulator" which modifies the behavior of the step
+prior to it. The `with()`-step provides additional "configuration" information to steps that implement the `Configuring`
+interface. Steps that allow for this type of modulation will explicitly state so in their documentation.
+
 [[a-note-on-predicates]]
 == A Note on Predicates
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/docs/src/upgrade/release-3.4.x.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.4.x.asciidoc b/docs/src/upgrade/release-3.4.x.asciidoc
index 3c881c3..f99a6d5 100644
--- a/docs/src/upgrade/release-3.4.x.asciidoc
+++ b/docs/src/upgrade/release-3.4.x.asciidoc
@@ -29,6 +29,42 @@ Please see the link:https://github.com/apache/tinkerpop/blob/3.4.0/CHANGELOG.asc
 
 === Upgrading for Users
 
+==== with() Step
+
+This version of TinkerPop introduces the `with()` step to Gremlin. It isn't really a step but is instead a step
+modulator. This modulator allows the step it is modifying to accept configurations that can be used to alter the
+behavior of the step itself. A good example of its usage is shown with the revised syntax of the `pageRank()` step
+which now uses `with()` to replace the old `by()` options:
+
+[groovy]
+----
+g.V().hasLabel('person').
+  pageRank().
+    with(PageRank.EDGES, __.outE('knows')).
+    with(PageRank.PROPERTY_NAME, 'friendRank').
+  order().
+    by('friendRank',desc).
+  valueMap('name','friendRank')
+----
+
+A similar change was made for `peerPressure()` step:
+
+[groovy]
+----
+g.V().hasLabel('person').
+  peerPressure().
+    with(PeerPressure.PROPERTY_NAME, 'cluster').
+  group().
+    by('cluster').
+    by('name')
+----
+
+Note that the `by()` modulators still work, but should be considered deprecated and open for removal in a future
+release where breaking changes are allowed.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1975[TINKERPOP-1975],
+link:http://tinkerpop.apache.org/docs/current/reference/#with-step[Reference Documentation]
+
 ==== Removal of Giraph Support
 
 Support for Giraph has been removed as of this version. There were a number of reasons for this decision which were
@@ -198,6 +234,19 @@ See: link:https://issues.apache.org/jira/browse/TINKERPOP-1522[TINKERPOP-1522]
 
 ==== Graph Database Providers
 
+===== Configuring Interface
+
+There were some changes to interfaces that were related to `Step`. A new `Configuring` interface was added that was
+helpful in the implementation of the `with()` step modulator. This new interface extends the `Parameterizing` interface
+(which moved to the `org.apache.tinkerpop.gremlin.process.traversal.step` package with the other step interfaces) and
+in turn is extended by the `Mutating` interface. Making this change meant that the `Mutating.addPropertyMutations()`
+method could be removed in favor of the new `Configuring.configure()` method.
+
+All of the changes above basically mean, that if the `Mutating` interface was being used in prior versions, the
+`addPropertyMutations()` method simply needs to be changed to `configure()`.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1975[TINKERPOP-1975]
+
 ===== Removed hadoop-gremlin Test Artifact
 
 The `hadoop-gremlin` module no longer generates a test jar that can be used as a test dependency in other modules.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
index 8839dcd..368b92d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
@@ -48,6 +48,7 @@ import org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankMa
 import org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgram;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.MemoryTraversalSideEffects;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PageRankVertexProgramStep;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy;
 import org.apache.tinkerpop.gremlin.process.remote.RemoteConnection;
@@ -260,6 +261,7 @@ public final class CoreImports {
         CLASS_IMPORTS.add(PeerPressureVertexProgram.class);
         CLASS_IMPORTS.add(PageRankMapReduce.class);
         CLASS_IMPORTS.add(PageRankVertexProgram.class);
+        CLASS_IMPORTS.add(PageRankVertexProgramStep.PageRank.class);
         CLASS_IMPORTS.add(GraphFilterStrategy.class);
         CLASS_IMPORTS.add(TraversalVertexProgram.class);
         CLASS_IMPORTS.add(VertexProgramStrategy.class);

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PageRankVertexProgramStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PageRankVertexProgramStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PageRankVertexProgramStep.java
index 2f67aeb..1c5b364 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PageRankVertexProgramStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PageRankVertexProgramStep.java
@@ -23,12 +23,16 @@ import org.apache.tinkerpop.gremlin.process.computer.GraphFilter;
 import org.apache.tinkerpop.gremlin.process.computer.Memory;
 import org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgram;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.lambda.HaltedTraversersCountTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TimesModulating;
 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.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.PureTraversal;
 import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -42,9 +46,12 @@ import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public final class PageRankVertexProgramStep extends VertexProgramStep implements TraversalParent, ByModulating, TimesModulating {
+public final class PageRankVertexProgramStep extends VertexProgramStep
+        implements TraversalParent, ByModulating, TimesModulating, Configuring {
 
+    private Parameters parameters = new Parameters();
     private PureTraversal<Vertex, Edge> edgeTraversal;
     private String pageRankProperty = PageRankVertexProgram.PAGE_RANK;
     private int times = 20;
@@ -53,23 +60,59 @@ public final class PageRankVertexProgramStep extends VertexProgramStep implement
     public PageRankVertexProgramStep(final Traversal.Admin traversal, final double alpha) {
         super(traversal);
         this.alpha = alpha;
-        this.modulateBy(__.<Vertex>outE().asAdmin());
+        this.configure(PageRank.EDGES, __.<Vertex>outE().asAdmin());
     }
 
     @Override
+    public void configure(final Object... keyValues) {
+        if (keyValues[0].equals(PageRank.EDGES)) {
+            if (!(keyValues[1] instanceof Traversal))
+                throw new IllegalArgumentException("PageRank.EDGES requires a Traversal as its argument");
+            this.edgeTraversal = new PureTraversal<>(((Traversal<Vertex,Edge>) keyValues[1]).asAdmin());
+            this.integrateChild(this.edgeTraversal.get());
+        } else if (keyValues[0].equals(PageRank.PROPERTY_NAME)) {
+            if (!(keyValues[1] instanceof String))
+                throw new IllegalArgumentException("PageRank.PROPERTY_NAME requires a String as its argument");
+            this.pageRankProperty = (String) keyValues[1];
+        } else if (keyValues[0].equals(PageRank.TIMES)) {
+            if (!(keyValues[1] instanceof Integer))
+                throw new IllegalArgumentException("PageRank.TIMES requires an Integer as its argument");
+            this.times = (int) keyValues[1];
+        }else {
+            this.parameters.set(this, keyValues);
+        }
+    }
+
+    @Override
+    public Parameters getParameters() {
+        return parameters;
+    }
+
+    /**
+     * @deprecated As of release 3.4.0, replaced by {@link #configure(Object...)}
+     */
+    @Deprecated
+    @Override
     public void modulateBy(final Traversal.Admin<?, ?> edgeTraversal) {
-        this.edgeTraversal = new PureTraversal<>((Traversal.Admin<Vertex, Edge>) edgeTraversal);
-        this.integrateChild(this.edgeTraversal.get());
+        configure(PageRank.EDGES, edgeTraversal);
     }
 
+    /**
+     * @deprecated As of release 3.4.0, replaced by {@link #configure(Object...)}
+     */
+    @Deprecated
     @Override
     public void modulateBy(final String pageRankProperty) {
-        this.pageRankProperty = pageRankProperty;
+        configure(PageRank.PROPERTY_NAME, pageRankProperty);
     }
 
+    /**
+     * @deprecated As of release 3.4.0, replaced by {@link #configure(Object...)}
+     */
+    @Deprecated
     @Override
     public void modulateTimes(int times) {
-        this.times = times;
+        configure(PageRank.TIMES, times);
     }
 
     @Override
@@ -118,4 +161,24 @@ public final class PageRankVertexProgramStep extends VertexProgramStep implement
     public int hashCode() {
         return super.hashCode() ^ this.edgeTraversal.hashCode() ^ this.pageRankProperty.hashCode() ^ this.times;
     }
+
+    /**
+     * Configuration options to be passed to the {@link GraphTraversal#with(String, Object)} step.
+     */
+    public static class PageRank {
+        /**
+         * Configures number of iterations that the algorithm should run.
+         */
+        public static final String TIMES = Graph.Hidden.hide("tinkerpop.pageRank.times");
+
+        /**
+         * Configures the edge to traverse when calculating the pagerank.
+         */
+        public static final String EDGES = Graph.Hidden.hide("tinkerpop.pageRank.edges");
+
+        /**
+         * Configures the name of the property within which to store the pagerank value.
+         */
+        public static final String PROPERTY_NAME = Graph.Hidden.hide("tinkerpop.pageRank.propertyName");
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PeerPressureVertexProgramStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PeerPressureVertexProgramStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PeerPressureVertexProgramStep.java
index 47d4160..be65f42 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PeerPressureVertexProgramStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/PeerPressureVertexProgramStep.java
@@ -25,10 +25,13 @@ import org.apache.tinkerpop.gremlin.process.computer.clustering.peerpressure.Pee
 import org.apache.tinkerpop.gremlin.process.computer.traversal.lambda.HaltedTraversersCountTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TimesModulating;
 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.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.PureTraversal;
 import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -42,16 +45,44 @@ import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public final class PeerPressureVertexProgramStep extends VertexProgramStep implements TraversalParent, ByModulating, TimesModulating {
+public final class PeerPressureVertexProgramStep extends VertexProgramStep
+        implements TraversalParent, ByModulating, TimesModulating, Configuring {
 
+    private Parameters parameters = new Parameters();
     private PureTraversal<Vertex, Edge> edgeTraversal;
     private String clusterProperty = PeerPressureVertexProgram.CLUSTER;
     private int times = 30;
 
     public PeerPressureVertexProgramStep(final Traversal.Admin traversal) {
         super(traversal);
-        this.modulateBy(__.<Vertex>outE().asAdmin());
+        this.configure(PeerPressure.EDGES, __.<Vertex>outE().asAdmin());
+    }
+
+    @Override
+    public void configure(final Object... keyValues) {
+        if (keyValues[0].equals(PeerPressureVertexProgramStep.PeerPressure.EDGES)) {
+            if (!(keyValues[1] instanceof Traversal))
+                throw new IllegalArgumentException("PeerPressure.EDGES requires a Traversal as its argument");
+            this.edgeTraversal = new PureTraversal<>(((Traversal<Vertex,Edge>) keyValues[1]).asAdmin());
+            this.integrateChild(this.edgeTraversal.get());
+        } else if (keyValues[0].equals(PeerPressureVertexProgramStep.PeerPressure.PROPERTY_NAME)) {
+            if (!(keyValues[1] instanceof String))
+                throw new IllegalArgumentException("PeerPressure.PROPERTY_NAME requires a String as its argument");
+            this.clusterProperty = (String) keyValues[1];
+        } else if (keyValues[0].equals(PeerPressureVertexProgramStep.PeerPressure.TIMES)) {
+            if (!(keyValues[1] instanceof Integer))
+                throw new IllegalArgumentException("PeerPressure.TIMES requires an Integer as its argument");
+            this.times = (int) keyValues[1];
+        }else {
+            this.parameters.set(this, keyValues);
+        }
+    }
+
+    @Override
+    public Parameters getParameters() {
+        return parameters;
     }
 
     @Override
@@ -59,20 +90,31 @@ public final class PeerPressureVertexProgramStep extends VertexProgramStep imple
         return super.hashCode() ^ this.edgeTraversal.hashCode() ^ this.clusterProperty.hashCode() ^ this.times;
     }
 
+    /**
+     * @deprecated As of release 3.4.0, replaced by {@link #configure(Object...)}
+     */
+    @Deprecated
     @Override
     public void modulateBy(final Traversal.Admin<?, ?> edgeTraversal) {
-        this.edgeTraversal = new PureTraversal<>((Traversal.Admin<Vertex, Edge>) edgeTraversal);
-        this.integrateChild(this.edgeTraversal.get());
+        configure(PeerPressure.EDGES, edgeTraversal);
     }
 
+    /**
+     * @deprecated As of release 3.4.0, replaced by {@link #configure(Object...)}
+     */
+    @Deprecated
     @Override
     public void modulateBy(final String clusterProperty) {
-        this.clusterProperty = clusterProperty;
+        configure(PeerPressure.PROPERTY_NAME, clusterProperty);
     }
 
+    /**
+     * @deprecated As of release 3.4.0, replaced by {@link #configure(Object...)}
+     */
+    @Deprecated
     @Override
     public void modulateTimes(int times) {
-        this.times = times;
+        configure(PeerPressure.TIMES, times);
     }
 
     @Override
@@ -115,4 +157,24 @@ public final class PeerPressureVertexProgramStep extends VertexProgramStep imple
         super.setTraversal(parentTraversal);
         this.integrateChild(this.edgeTraversal.get());
     }
+
+    /**
+     * Configuration options to be passed to the {@link GraphTraversal#with(String, Object)} step.
+     */
+    public static class PeerPressure {
+        /**
+         * Configures number of iterations that the algorithm should run.
+         */
+        public static final String TIMES = Graph.Hidden.hide("tinkerpop.peerPressure.times");
+
+        /**
+         * Configures the edge to traverse when determining clusters.
+         */
+        public static final String EDGES = Graph.Hidden.hide("tinkerpop.peerPressure.edges");
+
+        /**
+         * Configures the name of the property within which to store the cluster value.
+         */
+        public static final String PROPERTY_NAME = Graph.Hidden.hide("tinkerpop.peerPressure.propertyName");
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Parameterizing.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Parameterizing.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Parameterizing.java
deleted file mode 100644
index 9b7e088..0000000
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Parameterizing.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tinkerpop.gremlin.process.traversal;
-
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
-
-/**
- * An interface for {@link Step} implementations that hold a {@link Parameters} object, which fold in arguments from
- * other steps. It is typically used on mutating steps, such as {@code addV()}, where calls to {@code property(k,v)}
- * are folded in as parameters to that add vertex step.
- *
- * @author Marko A. Rodriguez (http://markorodriguez.com)
- * @author Stephen Mallette (http://stephen.genoprime.com)
- */
-public interface Parameterizing {
-
-    /**
-     * Gets the parameters on the step.
-     */
-    public Parameters getParameters();
-}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
----------------------------------------------------------------------
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 0fd3599..7d1e7e4 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
@@ -36,6 +36,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.lambda.LoopTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.PredicateTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.TrueTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
 import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TimesModulating;
@@ -2074,10 +2075,10 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         final Step endStep = this.asAdmin().getEndStep();
         if ((endStep instanceof AddVertexStep || endStep instanceof AddEdgeStep || endStep instanceof AddVertexStartStep || endStep instanceof AddEdgeStartStep) &&
                 keyValues.length == 0 && null == cardinality) {
-            ((Mutating) endStep).addPropertyMutations(key, value);
+            ((Mutating) endStep).configure(key, value);
         } else {
             this.asAdmin().addStep(new AddPropertyStep(this.asAdmin(), cardinality, key, value));
-            ((AddPropertyStep) this.asAdmin().getEndStep()).addPropertyMutations(keyValues);
+            ((AddPropertyStep) this.asAdmin().getEndStep()).configure(keyValues);
         }
         return this;
     }
@@ -2496,6 +2497,24 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         return this.asAdmin().addStep(new LambdaCollectingBarrierStep<>(this.asAdmin(), (Consumer) barrierConsumer, Integer.MAX_VALUE));
     }
 
+    //// WITH-MODULATOR
+
+    /**
+     * Provides a configuration to a step in the form of a key and value pair. The key of the configuration must be
+     * step specific and therefore a configuration could be supplied that is not known to be valid until execution.
+     *
+     * @param key the key of the configuration to apply to a step
+     * @param value the value of the configuration to apply to a step
+     * @return the traversal with a modulated step
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#with-step" target="_blank">Reference Documentation - With Step</a>
+     * @since 3.4.0
+     */
+    public default GraphTraversal<S,E> with(final String key, final Object value) {
+        this.asAdmin().getBytecode().addStep(Symbols.with, key, value);
+        final Object[] configPair = { key, value };
+        ((Configuring) this.asAdmin().getEndStep()).configure(configPair);
+        return this;
+    }
 
     //// BY-MODULATORS
 
@@ -2801,6 +2820,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         public static final String program = "program";
 
         public static final String by = "by";
+        public static final String with = "with";
         public static final String times = "times";
         public static final String as = "as";
         public static final String option = "option";

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Configuring.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Configuring.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Configuring.java
new file mode 100644
index 0000000..4ac6280
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Configuring.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+
+/**
+ * Identifies a {@link Step} as one that can accept configurations via the {@link GraphTraversal#with(String, Object)}
+ * step modulator. The nature of the configuration allowed is specific to the implementation.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface Configuring extends Parameterizing {
+
+    /**
+     * Accept a configuration to the {@link Step}. Note that this interface extends {@link Parameterizing} and so
+     * there is an expectation that the {@link Step} implementation will have a {@link Parameters} instance that will
+     * house any values passed to this method. Storing these configurations in {@link Parameters} is not a requirement
+     * however, IF the configuration is an expected option for the step and can be stored on a member field that can
+     * be accessed on the step by more direct means (i.e. like a getter method).
+     */
+    public void configure(final Object... keyValues);
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
index 50e415a..14553d9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
@@ -28,13 +28,10 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  * @author Matt Frantz (http://github.com/mhfrantz)
  */
-public interface Mutating<E extends Event> {
+public interface Mutating<E extends Event> extends Configuring {
 
     /**
      * Gets the callback registry for events that the step raises.
      */
     public CallbackRegistry<E> getMutatingCallbackRegistry();
-
-    public void addPropertyMutations(final Object... keyValues);
-
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Parameterizing.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Parameterizing.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Parameterizing.java
new file mode 100644
index 0000000..9d8dc22
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Parameterizing.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+
+/**
+ * An interface for {@link Step} implementations that hold a {@link Parameters} object, which fold in arguments from
+ * other steps. It is typically used on mutating steps, such as {@code addV()}, where calls to {@code property(k,v)}
+ * are folded in as parameters to that add vertex step, but it may be used for any step that could benefit from
+ * modulation.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface Parameterizing {
+
+    /**
+     * Gets the parameters on the step.
+     */
+    public Parameters getParameters();
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
index cd95e48..e7e14bb 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
@@ -94,7 +95,13 @@ public final class DropStep<S> extends FilterStep<S> implements Mutating<Event>
     /**
      * This method doesn't do anything as {@code drop()} doesn't take property mutation arguments.
      */
-    public void addPropertyMutations(final Object... keyValues) {
+    @Override
+    public void configure(final Object... keyValues) {
         // do nothing
     }
+
+    @Override
+    public Parameters getParameters() {
+        return Parameters.EMPTY;
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
index 73f69a0..c6a2ef1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
@@ -19,7 +19,7 @@
 
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -51,7 +51,7 @@ import java.util.Set;
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
 public final class AddEdgeStartStep extends AbstractStep<Edge, Edge>
-        implements Mutating<Event.EdgeAddedEvent>, TraversalParent, Parameterizing, Scoping, FromToModulating {
+        implements Mutating<Event.EdgeAddedEvent>, TraversalParent, Scoping, FromToModulating {
 
     private static final String FROM = Graph.Hidden.hide("from");
     private static final String TO = Graph.Hidden.hide("to");
@@ -86,7 +86,7 @@ public final class AddEdgeStartStep extends AbstractStep<Edge, Edge>
     }
 
     @Override
-    public void addPropertyMutations(final Object... keyValues) {
+    public void configure(final Object... keyValues) {
         this.parameters.set(this, keyValues);
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
index 81afd03..27197f4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
@@ -18,7 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
@@ -37,7 +37,6 @@ import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.Attachable;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
-import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
 import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 
 import java.util.List;
@@ -48,7 +47,7 @@ import java.util.Set;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public final class AddEdgeStep<S> extends MapStep<S, Edge>
-        implements Mutating<Event.EdgeAddedEvent>, TraversalParent, Parameterizing, Scoping, FromToModulating {
+        implements Mutating<Event.EdgeAddedEvent>, TraversalParent, Scoping, FromToModulating {
 
     private static final String FROM = Graph.Hidden.hide("from");
     private static final String TO = Graph.Hidden.hide("to");
@@ -82,7 +81,7 @@ public final class AddEdgeStep<S> extends MapStep<S, Edge>
     }
 
     @Override
-    public void addPropertyMutations(final Object... keyValues) {
+    public void configure(final Object... keyValues) {
         this.parameters.set(this, keyValues);
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
index 920fa45..f47e631 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
@@ -18,7 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -37,7 +37,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementExce
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
-import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
 
 import java.util.List;
 import java.util.Set;
@@ -47,7 +46,7 @@ import java.util.Set;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public final class AddVertexStartStep extends AbstractStep<Vertex, Vertex>
-        implements Mutating<Event.VertexAddedEvent>, TraversalParent, Parameterizing, Scoping {
+        implements Mutating<Event.VertexAddedEvent>, TraversalParent, Scoping {
 
     private Parameters parameters = new Parameters();
     private boolean first = true;
@@ -79,7 +78,7 @@ public final class AddVertexStartStep extends AbstractStep<Vertex, Vertex>
     }
 
     @Override
-    public void addPropertyMutations(final Object... keyValues) {
+    public void configure(final Object... keyValues) {
         this.parameters.set(this, keyValues);
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index 13e2fc8..e555277 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -18,7 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
@@ -33,7 +33,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequire
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
-import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
 
 import java.util.List;
 import java.util.Set;
@@ -43,7 +42,7 @@ import java.util.Set;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public final class AddVertexStep<S> extends MapStep<S, Vertex>
-        implements Mutating<Event.VertexAddedEvent>, TraversalParent, Parameterizing, Scoping {
+        implements Mutating<Event.VertexAddedEvent>, TraversalParent, Scoping {
 
     private Parameters parameters = new Parameters();
     private CallbackRegistry<Event.VertexAddedEvent> callbackRegistry;
@@ -74,7 +73,7 @@ public final class AddVertexStep<S> extends MapStep<S, Vertex>
     }
 
     @Override
-    public void addPropertyMutations(final Object... keyValues) {
+    public void configure(final Object... keyValues) {
         this.parameters.set(this, keyValues);
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
index 7509f86..04a8414 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
@@ -18,7 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
@@ -37,9 +37,6 @@ import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
-import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
-import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
-import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
 
 import java.util.List;
 import java.util.Set;
@@ -48,7 +45,7 @@ import java.util.Set;
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
 public final class AddPropertyStep<S extends Element> extends SideEffectStep<S>
-        implements Mutating<Event.ElementPropertyChangedEvent>, TraversalParent, Parameterizing, Scoping {
+        implements Mutating<Event.ElementPropertyChangedEvent>, TraversalParent, Scoping {
 
     private Parameters parameters = new Parameters();
     private final VertexProperty.Cardinality cardinality;
@@ -76,7 +73,7 @@ public final class AddPropertyStep<S extends Element> extends SideEffectStep<S>
     }
 
     @Override
-    public void addPropertyMutations(final Object... keyValues) {
+    public void configure(final Object... keyValues) {
         this.parameters.set(this, keyValues);
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
index d368322..40d9330 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
@@ -48,6 +48,8 @@ import java.util.function.Supplier;
  */
 public final class Parameters implements Cloneable, Serializable {
 
+    public static final Parameters EMPTY = new Parameters();
+
     private static final Object[] EMPTY_ARRAY = new Object[0];
 
     private Map<Object, List<Object>> parameters = new HashMap<>();

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java
index c8f9d22..25a2833 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java
@@ -21,7 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.configuration.MapConfiguration;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
-import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
index 438d8b2..877c2df 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/PartitionStrategy.java
@@ -21,7 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.configuration.MapConfiguration;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
-import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Parameterizing;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
@@ -203,7 +203,7 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal
             // ends up being a Vertex or not.  AddPropertyStep currently chooses to simply not bother
             // to use the additional "property mutations" if the Element being mutated is a Edge or
             // VertexProperty
-            ((Mutating) step).addPropertyMutations(partitionKey, writePartition);
+            ((Mutating) step).configure(partitionKey, writePartition);
 
             if (includeMetaProperties) {
                 // GraphTraversal folds g.addV().property('k','v') to just AddVertexStep/AddVertexStartStep so this
@@ -216,7 +216,7 @@ public final class PartitionStrategy extends AbstractTraversalStrategy<Traversal
                         final VertexProperty.Cardinality cardinality = vertexFeatures.getCardinality((String) k);
                         v.forEach(o -> {
                             final AddPropertyStep addPropertyStep = new AddPropertyStep(traversal, cardinality, k, o);
-                            addPropertyStep.addPropertyMutations(partitionKey, writePartition);
+                            addPropertyStep.configure(partitionKey, writePartition);
                             addPropertyStepsToAppend.add(addPropertyStep);
 
                             // need to remove the parameter from the AddVertex/StartStep because it's now being added

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java
index 6decbe0..9009d0b 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java
@@ -42,7 +42,7 @@ import static org.junit.Assert.assertEquals;
 public class GraphTraversalTest {
     private static final Logger logger = LoggerFactory.getLogger(GraphTraversalTest.class);
 
-    private static Set<String> NO_GRAPH = new HashSet<>(Arrays.asList("asAdmin", "by", "option", "iterate", "to", "from", "profile", "pageRank", "peerPressure", "program", "none"));
+    private static Set<String> NO_GRAPH = new HashSet<>(Arrays.asList("asAdmin", "by", "with", "option", "iterate", "to", "from", "profile", "pageRank", "peerPressure", "program", "none"));
     private static Set<String> NO_ANONYMOUS = new HashSet<>(Arrays.asList("start", "__"));
     private static Set<String> IGNORES_BYTECODE = new HashSet<>(Arrays.asList("asAdmin", "iterate", "mapValues", "mapKeys"));
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index bb3d5d8..537cdbe 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -1679,9 +1679,9 @@ namespace Gremlin.Net.Process.Traversal
         /// <summary>
         ///     Adds the with step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
-        public GraphTraversal<S, E> With (StepConfiguration modulation)
+        public GraphTraversal<S, E> With (string key, object value)
         {
-            Bytecode.AddStep("with", modulation);
+            Bytecode.AddStep("with", key, value);
             return Wrap<S, E>(this);
         }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-javascript/glv/generate.groovy
----------------------------------------------------------------------
diff --git a/gremlin-javascript/glv/generate.groovy b/gremlin-javascript/glv/generate.groovy
index 8fd22e3..8778e89 100644
--- a/gremlin-javascript/glv/generate.groovy
+++ b/gremlin-javascript/glv/generate.groovy
@@ -28,7 +28,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__
 import java.lang.reflect.Modifier
 
 def toJsMap = ["in": "in_",
-               "from": "from_"]
+               "from": "from_",
+               "with": "with_"]
 
 def toJs = { symbol -> toJsMap.getOrDefault(symbol, symbol) }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
----------------------------------------------------------------------
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 901f9b0..f143542 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
@@ -1137,7 +1137,7 @@ class GraphTraversal extends Traversal {
    * @param {...Object} args
    * @returns {GraphTraversal}
    */
-  with(...args) {
+  with_(...args) {
     this.bytecode.addStep('with', args);
     return this;
   }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-python/glv/generate.groovy
----------------------------------------------------------------------
diff --git a/gremlin-python/glv/generate.groovy b/gremlin-python/glv/generate.groovy
index 58d0cd3..57cc9c9 100644
--- a/gremlin-python/glv/generate.groovy
+++ b/gremlin-python/glv/generate.groovy
@@ -30,6 +30,7 @@ def toPythonMap = ["global": "global_",
                    "as": "as_",
                    "in": "in_",
                    "and": "and_",
+                   "with": "with_",
                    "or": "or_",
                    "is": "is_",
                    "not": "not_",

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
index a492f9c..bb81d87 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
@@ -505,7 +505,7 @@ class GraphTraversal(Traversal):
         self.bytecode.add_step("where", *args)
         return self
 
-    def with(self, *args):
+    def with_(self, *args):
         self.bytecode.add_step("with", *args)
         return self
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java
index 07a2b04..325621e 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PageRankTest.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.LoadGraphWith;
 import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
 import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner;
 import org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PageRankVertexProgramStep.PageRank;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
@@ -63,6 +64,8 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasLabelXsoftwareX_hasXname_rippleX_pageRankX1X_byXinEXcreatedXX_timesX1X_byXpriorsX_inXcreatedX_unionXboth__identityX_valueMapXname_priorsX();
 
+    public abstract Traversal<Vertex, Map<Object, List<Vertex>>> get_g_V_outXcreatedX_groupXmX_byXlabelX_pageRankX1X_withXPROPERTY_NAME_pageRankXX_withXEDGES_inEXX_withXTIMES_1X_inXcreatedX_groupXmX_byXpageRankX_capXmX();
+
     public abstract Traversal<Vertex, Map<Object, List<Vertex>>> get_g_V_outXcreatedX_groupXmX_byXlabelX_pageRankX1X_byXpageRankX_byXinEX_timesX1X_inXcreatedX_groupXmX_byXpageRankX_capXmX();
 
     @Test
@@ -238,6 +241,39 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest {
         });
     }
 
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_outXcreatedX_groupXmX_byXlabelX_pageRankX1X_withXPROPERTY_NAME_pageRankXX_withXEDGES_inEXX_withXTIMES_1X_inXcreatedX_groupXmX_byXpageRankX_capXmX() {
+        // [{2.0=[v[4], v[4], v[4], v[4]], 1.0=[v[6], v[6], v[6], v[1], v[1], v[1]], software=[v[5], v[3], v[3], v[3]]}]
+        final Traversal<Vertex, Map<Object, List<Vertex>>> traversal = get_g_V_outXcreatedX_groupXmX_byXlabelX_pageRankX1X_withXPROPERTY_NAME_pageRankXX_withXEDGES_inEXX_withXTIMES_1X_inXcreatedX_groupXmX_byXpageRankX_capXmX();
+        printTraversalForm(traversal);
+        final Map<Object, List<Vertex>> map = traversal.next();
+        assertFalse(traversal.hasNext());
+        assertEquals(3, map.size());
+        assertTrue(map.containsKey("software"));
+        map.forEach((k, v) -> {
+            boolean found = false;
+            if (!k.equals("software") && v.size() == 4) {
+                assertEquals(2.0d, ((Number) k).doubleValue(), 0.01d);
+                assertEquals(4, v.stream().filter(vertex -> vertex.id().equals(convertToVertexId(graph, "josh"))).count());
+                found = true;
+            } else if (v.size() == 6) {
+                assertEquals(1.0d, ((Number) k).doubleValue(), 0.01d);
+                assertEquals(3, v.stream().filter(vertex -> vertex.id().equals(convertToVertexId(graph, "peter"))).count());
+                assertEquals(3, v.stream().filter(vertex -> vertex.id().equals(convertToVertexId(graph, "marko"))).count());
+                found = true;
+            } else if (v.size() == 4) {
+                assertEquals("software", k);
+                assertEquals(3, v.stream().filter(vertex -> vertex.id().equals(convertToVertexId(graph, "lop"))).count());
+                assertEquals(1, v.stream().filter(vertex -> vertex.id().equals(convertToVertexId(graph, "ripple"))).count());
+                found = true;
+            }
+
+            if (!found)
+                fail("There are too many key/values: " + k + "--" + v);
+        });
+    }
+
     public static class Traversals extends PageRankTest {
 
         @Override
@@ -284,5 +320,10 @@ public abstract class PageRankTest extends AbstractGremlinProcessTest {
         public Traversal<Vertex, Map<Object, List<Vertex>>> get_g_V_outXcreatedX_groupXmX_byXlabelX_pageRankX1X_byXpageRankX_byXinEX_timesX1X_inXcreatedX_groupXmX_byXpageRankX_capXmX() {
             return g.V().out("created").group("m").by(T.label).pageRank(1.0).by("pageRank").by(__.inE()).times(1).in("created").group("m").by("pageRank").cap("m");
         }
+
+        @Override
+        public Traversal<Vertex, Map<Object, List<Vertex>>> get_g_V_outXcreatedX_groupXmX_byXlabelX_pageRankX1X_withXPROPERTY_NAME_pageRankXX_withXEDGES_inEXX_withXTIMES_1X_inXcreatedX_groupXmX_byXpageRankX_capXmX() {
+            return g.V().out("created").group("m").by(T.label).pageRank(1.0).with(PageRank.PROPERTY_NAME, "pageRank").with(PageRank.EDGES, __.inE()).with(PageRank.TIMES, 1).in("created").group("m").by("pageRank").cap("m");
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/fc0b2dc5/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java
index f9615d5..c76941b 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PeerPressureTest.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.LoadGraphWith;
 import org.apache.tinkerpop.gremlin.TestHelper;
 import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
 import org.apache.tinkerpop.gremlin.process.computer.clustering.peerpressure.PeerPressureVertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PeerPressureVertexProgramStep.PeerPressure;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -50,6 +51,8 @@ public abstract class PeerPressureTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX();
 
+    public abstract Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXEDGES_outEX_withXPROPERTY_NAME_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX();
+
     @Test
     @LoadGraphWith(MODERN)
     public void g_V_peerPressure() {
@@ -100,7 +103,28 @@ public abstract class PeerPressureTest extends AbstractGremlinProcessTest {
         assertTrue(ids.contains(convertToVertexId("peter")));
     }
 
-
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXEDGES_outEX_withXPROPERTY_NAME_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() {
+        TestHelper.assumeNonDeterministic();
+        final Traversal<Vertex, Map<String, List<Object>>> traversal = get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXEDGES_outEX_withXPROPERTY_NAME_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX();
+        printTraversalForm(traversal);
+        final List<Map<String, List<Object>>> results = traversal.toList();
+        assertEquals(6, results.size());
+        final Map<String, Object> clusters = new HashMap<>();
+        results.forEach(m -> clusters.put((String) m.get("name").get(0), m.get("cluster").get(0)));
+        assertEquals(2, results.get(0).size());
+        assertEquals(6, clusters.size());
+        assertEquals(clusters.get("josh"), clusters.get("ripple"));
+        assertEquals(clusters.get("josh"), clusters.get("lop"));
+        final Set<Object> ids = new HashSet<>(clusters.values());
+        assertEquals(4, ids.size());
+        assertTrue(ids.contains(convertToVertexId("marko")));
+        assertTrue(ids.contains(convertToVertexId("vadas")));
+        assertTrue(ids.contains(convertToVertexId("josh")));
+        assertTrue(ids.contains(convertToVertexId("peter")));
+    }
+    
     public static class Traversals extends PeerPressureTest {
 
         @Override
@@ -117,5 +141,10 @@ public abstract class PeerPressureTest extends AbstractGremlinProcessTest {
         public Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_byXoutEX_byXclusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() {
             return g.V().has("name", "ripple").in("created").peerPressure().by(__.outE()).by("cluster").repeat(__.union(__.identity(), __.both())).times(2).dedup().valueMap("name", "cluster");
         }
+
+        @Override
+        public Traversal<Vertex, Map<String, List<Object>>> get_g_V_hasXname_rippleX_inXcreatedX_peerPressure_withXEDGES_outEX_withXPROPERTY_NAME_clusterX_repeatXunionXidentity__bothX_timesX2X_dedup_valueMapXname_clusterX() {
+            return g.V().has("name", "ripple").in("created").peerPressure().with(PeerPressure.EDGES,__.outE()).with(PeerPressure.PROPERTY_NAME, "cluster").repeat(__.union(__.identity(), __.both())).times(2).dedup().valueMap("name", "cluster");
+        }
     }
 }
\ No newline at end of file


[2/2] tinkerpop git commit: TINKERPOP-1967 Added connectedComponent() step

Posted by sp...@apache.org.
TINKERPOP-1967 Added connectedComponent() step

Deprecated the recipe for "Connected Components" but left the old content present as I felt it had educational value.


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

Branch: refs/heads/TINKERPOP-1967
Commit: ee40274f71d6a9ca89caf82bc92b742144ef3af7
Parents: fc0b2dc
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu May 17 14:44:01 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Jun 1 10:51:38 2018 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 docs/src/recipes/connected-components.asciidoc  |   8 +-
 docs/src/reference/the-graphcomputer.asciidoc   |   6 +
 docs/src/reference/the-traversal.asciidoc       |  27 +++
 docs/src/upgrade/release-3.4.x.asciidoc         |  32 +++
 .../ConnectedComponentVertexProgram.java        | 217 +++++++++++++++++++
 .../ConnectedComponentVertexProgramStep.java    |  69 ++++++
 .../gremlin/process/remote/RemoteGraph.java     |   4 +
 .../traversal/dsl/graph/GraphTraversal.java     |  15 ++
 .../traversal/dsl/graph/GraphTraversalTest.java |   2 +-
 .../Process/Traversal/GraphTraversal.cs         |   9 +
 .../lib/process/graph-traversal.js              |  10 +
 .../gremlin_python/process/graph_traversal.py   |   4 +
 .../gremlin/process/ProcessComputerSuite.java   |   2 +
 .../step/map/ConnectedComponentTest.java        |  85 ++++++++
 .../computer/SparkHadoopGraphProvider.java      |   2 +
 16 files changed, 491 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 964bfda..5a2e3b6 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -29,6 +29,7 @@ This release also includes changes from <<release-3-3-3, 3.3.3>>.
 * Replaced `Parameterizing.addPropertyMutations()` with `Configuring.configure()`.
 * Changed interface hierarchy for `Parameterizing` and `Mutating` interfaces as they are tightly related.
 * Introduced the `with()` step modulator which can supply configuration options to `Configuring` steps.
+* Added `connectedComponent()` step and related `VertexProgram`.
 * Added `supportsUpsert()` option to `VertexFeatures` and `EdgeFeatures`.
 * `min()` and `max()` now support all types implementing `Comparable`.
 * Change the `toString()` of `Path` to be standardized as other graph elements are.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/docs/src/recipes/connected-components.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/recipes/connected-components.asciidoc b/docs/src/recipes/connected-components.asciidoc
index 46d61eb..70abdbd 100644
--- a/docs/src/recipes/connected-components.asciidoc
+++ b/docs/src/recipes/connected-components.asciidoc
@@ -18,7 +18,13 @@ limitations under the License.
 == Connected Components
 
 Gremlin can be used to find link:https://en.wikipedia.org/wiki/Connected_component_(graph_theory)[connected components]
-in a graph. Consider the following graph which has three connected components:
+in a graph. As of TinkerPop 3.4.0, the process has been simplified to the `connectedComponent()`-step which is
+described in more detail in the link:
+link:http://tinkerpop.apache.org/docs/x.y.z/reference/#connectedcomponent-step[Reference Documentation].
+
+The `connectedComponent()`-step replaces the original recipe described below from earlier versions of TinkerPop,
+however the algorithm from that old recipe remains interesting for educational purposes and has thus not been removed.
+The original recipe considers the following graph which has three connected components:
 
 image:connected-components.png[width=600]
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/docs/src/reference/the-graphcomputer.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-graphcomputer.asciidoc b/docs/src/reference/the-graphcomputer.asciidoc
index 4bf39d0..566651b 100644
--- a/docs/src/reference/the-graphcomputer.asciidoc
+++ b/docs/src/reference/the-graphcomputer.asciidoc
@@ -403,6 +403,12 @@ g.V().peerPressure().by('cluster').valueMap()
 g.V().peerPressure().by(outE('knows')).by('cluster').valueMap()
 ----
 
+[[connectedcomponentvertexprogram]]
+=== ConnectedComponentVertexProgram
+
+The `ConnectedComponentVertexProgram` identifies link:https://en.wikipedia.org/wiki/Connected_component_(graph_theory)[Connected Component]
+instances in a graph. See <<connectedcomponent-step,`connectedComponent()`>>-step for more information.
+
 [[bulkdumpervertexprogram]]
 === BulkDumperVertexProgram
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 0d7d67f..edc5c28 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -608,6 +608,33 @@ g.V().coin(1.0)
 
 link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#coin-double-++[`coin(double)`]
 
+[[connectedcomponent-step]]
+=== ConnectedComponent Step
+
+The `connectedComponent()` step performs a computation to identify link:https://en.wikipedia.org/wiki/Connected_component_(graph_theory)[Connected Component]
+instances in a graph. When this step completes, the vertices will be labelled with a component identifier to denote
+the component to which they are associated.
+
+IMPORTANT: The `connectedComponent()`-step is a `VertexComputing`-step and as such, can only be used against a graph that supports `GraphComputer` (OLAP).
+
+[gremlin-groovy,modern]
+----
+g = graph.traversal().withComputer()
+g.V().connectedComponent().by('component').
+  project('name','component').
+    by('name').
+    by('component')
+g.V().hasLabel('person').
+  connectedComponent().by('component').
+  project('name','component').
+    by('name').
+    by('component')
+----
+
+*Additional References*
+
+link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#connectedComponent--++[`connectedComponent()`]
+
 [[constant-step]]
 === Constant Step
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/docs/src/upgrade/release-3.4.x.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.4.x.asciidoc b/docs/src/upgrade/release-3.4.x.asciidoc
index f99a6d5..4400aed 100644
--- a/docs/src/upgrade/release-3.4.x.asciidoc
+++ b/docs/src/upgrade/release-3.4.x.asciidoc
@@ -65,6 +65,38 @@ release where breaking changes are allowed.
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-1975[TINKERPOP-1975],
 link:http://tinkerpop.apache.org/docs/current/reference/#with-step[Reference Documentation]
 
+==== connectedComponent() Step
+
+In prior version of TinkerPop, it was recommended that the identification of
+link:https://en.wikipedia.org/wiki/Connected_component_(graph_theory)[Connected Component] instances in a graph be
+computed by way of a reasonably complex bit of Gremlin that looked something like this:
+
+[source,groovy]
+----
+g.V().emit(cyclicPath().or().not(both())).repeat(both()).until(cyclicPath()).
+  path().aggregate("p").
+  unfold().dedup().
+  map(__.as("v").select("p").unfold().
+         filter(unfold().where(eq("v"))).
+         unfold().dedup().order().by(id).fold()).
+  dedup()
+----
+
+The above approach had a number of drawbacks that included a large execution cost as well as incompatibilities in OLAP.
+To simplify usage of this commonly use graph algorithm, TinkerPop 3.4.0 introduces the `connectedComponent()` step
+which reduces the above operation to:
+
+[source,groovy]
+----
+g.withComputer().V().connectedComponent()
+----
+
+It is important to note that this step does require the use of a `GraphComputer` to work, as it utilizes a
+`VertexProgram` behind the scenes.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1967[TINKERPOP-1967],
+link:http://tinkerpop.apache.org/docs/x.y.z/reference/#connectedcomponent-step[Reference Documentation]
+
 ==== Removal of Giraph Support
 
 Support for Giraph has been removed as of this version. There were a number of reasons for this decision which were

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/clustering/connected/ConnectedComponentVertexProgram.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/clustering/connected/ConnectedComponentVertexProgram.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/clustering/connected/ConnectedComponentVertexProgram.java
new file mode 100644
index 0000000..47fb628
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/clustering/connected/ConnectedComponentVertexProgram.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.computer.clustering.connected;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationUtils;
+import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
+import org.apache.tinkerpop.gremlin.process.computer.Memory;
+import org.apache.tinkerpop.gremlin.process.computer.MemoryComputeKey;
+import org.apache.tinkerpop.gremlin.process.computer.MessageScope;
+import org.apache.tinkerpop.gremlin.process.computer.Messenger;
+import org.apache.tinkerpop.gremlin.process.computer.VertexComputeKey;
+import org.apache.tinkerpop.gremlin.process.computer.VertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.util.AbstractVertexProgramBuilder;
+import org.apache.tinkerpop.gremlin.process.traversal.Operator;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Identifies "Connected Component" instances in a graph by assigning a component identifier (the lexicographically
+ * least string value of the vertex in the component) to each vertex.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+public class ConnectedComponentVertexProgram implements VertexProgram<String> {
+    private static final MessageScope.Local<?> scope = MessageScope.Local.of(__::bothE);
+    private static final Set<MessageScope> scopes = new HashSet<>(Collections.singletonList(scope));
+
+    public static final String COMPONENT = "gremlin.connectedComponentVertexProgram.component";
+    private static final String PROPERTY = "gremlin.connectedComponentVertexProgram.property";
+    private static final String HAS_HALTED = "gremlin.connectedComponentVertexProgram.hasHalted";
+    private static final String VOTE_TO_HALT = "gremlin.connectedComponentVertexProgram.voteToHalt";
+
+    private static final Set<MemoryComputeKey> MEMORY_COMPUTE_KEYS = Collections.singleton(MemoryComputeKey.of(VOTE_TO_HALT, Operator.and, false, true));
+    private String property = COMPONENT;
+    private boolean hasHalted = false;
+    private Configuration configuration;
+
+
+    private ConnectedComponentVertexProgram() {}
+
+    @Override
+    public void loadState(final Graph graph, final Configuration config) {
+        configuration = new BaseConfiguration();
+        if (config != null) {
+            ConfigurationUtils.copy(config, configuration);
+        }
+        this.property = configuration.getString(PROPERTY, COMPONENT);
+        this.hasHalted = configuration.getBoolean(HAS_HALTED, false);
+    }
+
+    @Override
+    public void storeState(final Configuration config) {
+        VertexProgram.super.storeState(config);
+        if (configuration != null) {
+            ConfigurationUtils.copy(configuration, config);
+        }
+    }
+
+    @Override
+    public void setup(final Memory memory) {
+        memory.set(VOTE_TO_HALT, true);
+    }
+
+    @Override
+    public void execute(final Vertex vertex, final Messenger<String> messenger, final Memory memory) {
+        if (memory.isInitialIteration()) {
+            // on the first pass, just initialize the component to its own id then pass it to all adjacent vertices
+            // for evaluation
+            vertex.property(VertexProperty.Cardinality.single, property, vertex.id().toString());
+
+            // no need to send messages from traversers that were filtered - happens for stuff like
+            // g.V().hasLabel('software').connectedComponent() (i.e. when there is a TraversalVertexProgram in the mix
+            // halting traversers. only want to send messages from traversers that are still hanging about after
+            // the filter. the unfiltered vertices can only react to messages sent to them. of course, this can
+            // lead to weirdness in results. 
+            if (vertex.edges(Direction.BOTH).hasNext() && !(hasHalted && !vertex.property(TraversalVertexProgram.HALTED_TRAVERSERS).isPresent())) {
+                // since there was message passing we don't want to halt on the first round. this should only trigger
+                // a single pass finish if the graph is completely disconnected (technically, it won't even really
+                // work in cases where halted traversers come into play
+                messenger.sendMessage(scope, vertex.id().toString());
+                memory.add(VOTE_TO_HALT, false);
+            }
+        } else {
+            // by the second iteration all vertices that matter should have a component assigned
+            String currentComponent = vertex.value(property);
+            boolean different = false;
+
+            // iterate through messages received and determine if there is a component that has a lesser value than
+            // the currently assigned one
+            final Iterator<String> componentIterator = messenger.receiveMessages();
+            while(componentIterator.hasNext()) {
+                final String candidateComponent = componentIterator.next();
+                if (candidateComponent.compareTo(currentComponent) < 0) {
+                    currentComponent = candidateComponent;
+                    different = true;
+                }
+            }
+
+            // if there is a better component then assign it and notify adjacent vertices. triggering the message
+            // passing should not halt future executions
+            if (different) {
+                vertex.property(VertexProperty.Cardinality.single, property, currentComponent);
+                messenger.sendMessage(scope, currentComponent);
+                memory.add(VOTE_TO_HALT, false);
+            }
+        }
+    }
+
+    @Override
+    public Set<VertexComputeKey> getVertexComputeKeys() {
+        return new HashSet<>(Collections.singletonList(VertexComputeKey.of(property, false)));
+    }
+
+    @Override
+    public Set<MemoryComputeKey> getMemoryComputeKeys() {
+        return MEMORY_COMPUTE_KEYS;
+    }
+
+    @Override
+    public boolean terminate(final Memory memory) {
+        final boolean voteToHalt = memory.<Boolean>get(VOTE_TO_HALT);
+        if (voteToHalt) {
+            return true;
+        } else {
+            // it is basically always assumed that the program will want to halt, but if message passing occurs, the
+            // program will want to continue, thus reset false values to true for future iterations
+            memory.set(VOTE_TO_HALT, true);
+            return false;
+        }
+    }
+
+    @Override
+    public Set<MessageScope> getMessageScopes(final Memory memory) {
+        return scopes;
+    }
+
+    @Override
+    public GraphComputer.ResultGraph getPreferredResultGraph() {
+        return GraphComputer.ResultGraph.NEW;
+    }
+
+    @Override
+    public GraphComputer.Persist getPreferredPersist() {
+        return GraphComputer.Persist.VERTEX_PROPERTIES;
+    }
+
+
+    @Override
+    @SuppressWarnings("CloneDoesntCallSuperClone,CloneDoesntDeclareCloneNotSupportedException")
+    public ConnectedComponentVertexProgram clone() {
+        return this;
+    }
+
+    @Override
+    public Features getFeatures() {
+        return new Features() {
+            @Override
+            public boolean requiresLocalMessageScopes() {
+                return true;
+            }
+
+            @Override
+            public boolean requiresVertexPropertyAddition() {
+                return true;
+            }
+        };
+    }
+
+    public static ConnectedComponentVertexProgram.Builder build() {
+        return new ConnectedComponentVertexProgram.Builder();
+    }
+
+    public static final class Builder extends AbstractVertexProgramBuilder<ConnectedComponentVertexProgram.Builder> {
+
+        private Builder() {
+            super(ConnectedComponentVertexProgram.class);
+        }
+
+        public ConnectedComponentVertexProgram.Builder hasHalted(final boolean hasHalted) {
+            this.configuration.setProperty(HAS_HALTED, hasHalted);
+            return this;
+        }
+
+        public ConnectedComponentVertexProgram.Builder property(final String key) {
+            this.configuration.setProperty(PROPERTY, key);
+            return this;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ConnectedComponentVertexProgramStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ConnectedComponentVertexProgramStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ConnectedComponentVertexProgramStep.java
new file mode 100644
index 0000000..39900ee
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ConnectedComponentVertexProgramStep.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.computer.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.computer.GraphFilter;
+import org.apache.tinkerpop.gremlin.process.computer.Memory;
+import org.apache.tinkerpop.gremlin.process.computer.clustering.connected.ConnectedComponentVertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.clustering.peerpressure.PeerPressureVertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class ConnectedComponentVertexProgramStep extends VertexProgramStep implements ByModulating {
+
+    private String clusterProperty = ConnectedComponentVertexProgram.COMPONENT;
+
+    public ConnectedComponentVertexProgramStep(final Traversal.Admin traversal) {
+        super(traversal);
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() ^ this.clusterProperty.hashCode();
+    }
+
+    @Override
+    public void modulateBy(final String clusterProperty) {
+        this.clusterProperty = clusterProperty;
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.stepString(this, this.clusterProperty, new GraphFilter(this.computer));
+    }
+
+    @Override
+    public ConnectedComponentVertexProgram generateProgram(final Graph graph, final Memory memory) {
+        return ConnectedComponentVertexProgram.build().
+                hasHalted(memory.exists(TraversalVertexProgram.HALTED_TRAVERSERS)).
+                property(this.clusterProperty).create(graph);
+    }
+
+    @Override
+    public ConnectedComponentVertexProgramStep clone() {
+        return (ConnectedComponentVertexProgramStep) super.clone();
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java
index da12114..d2093e4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java
@@ -57,6 +57,10 @@ import java.util.Iterator;
         method = "*",
         reason = "hmmmm")
 @Graph.OptOut(
+        test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ConnectedComponentTest",
+        method = "*",
+        reason = "hmmmm")
+@Graph.OptOut(
         test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.PageRankTest",
         method = "*",
         reason = "hmmmm")

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
----------------------------------------------------------------------
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 7d1e7e4..6fbe61d 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
@@ -19,6 +19,8 @@
 package org.apache.tinkerpop.gremlin.process.traversal.dsl.graph;
 
 import org.apache.tinkerpop.gremlin.process.computer.VertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.clustering.connected.ConnectedComponentVertexProgram;
+import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ConnectedComponentVertexProgramStep;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PageRankVertexProgramStep;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PeerPressureVertexProgramStep;
 import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ProgramVertexProgramStep;
@@ -2422,6 +2424,18 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
+     * Executes a Connected Component algorithm over the graph.
+     *
+     * @return the traversal with the appended {@link ConnectedComponentVertexProgram}
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#connectedcomponent-step" target="_blank">Reference Documentation - ConnectedComponent Step</a>
+     * @since 3.4.0
+     */
+    public default GraphTraversal<S, E> connectedComponent() {
+        this.asAdmin().getBytecode().addStep(Symbols.connectedComponent);
+        return this.asAdmin().addStep((Step<E, E>) new ConnectedComponentVertexProgramStep(this.asAdmin()));
+    }
+
+    /**
      * Executes an arbitrary {@link VertexProgram} over the graph.
      *
      * @return the traversal with the appended {@link ProgramVertexProgramStep}
@@ -2817,6 +2831,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
 
         public static final String pageRank = "pageRank";
         public static final String peerPressure = "peerPressure";
+        public static final String connectedComponent = "connectedComponent";
         public static final String program = "program";
 
         public static final String by = "by";

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java
index 9009d0b..7ef9027 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalTest.java
@@ -42,7 +42,7 @@ import static org.junit.Assert.assertEquals;
 public class GraphTraversalTest {
     private static final Logger logger = LoggerFactory.getLogger(GraphTraversalTest.class);
 
-    private static Set<String> NO_GRAPH = new HashSet<>(Arrays.asList("asAdmin", "by", "with", "option", "iterate", "to", "from", "profile", "pageRank", "peerPressure", "program", "none"));
+    private static Set<String> NO_GRAPH = new HashSet<>(Arrays.asList("asAdmin", "by", "with", "option", "iterate", "to", "from", "profile", "pageRank", "connectedComponent", "peerPressure", "program", "none"));
     private static Set<String> NO_ANONYMOUS = new HashSet<>(Arrays.asList("start", "__"));
     private static Set<String> IGNORES_BYTECODE = new HashSet<>(Arrays.asList("asAdmin", "iterate", "mapValues", "mapKeys"));
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index 537cdbe..a1936a7 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -401,6 +401,15 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Adds the connectedComponent step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, E> ConnectedComponent ()
+        {
+            Bytecode.AddStep("connectedComponent");
+            return Wrap<S, E>(this);
+        }
+
+        /// <summary>
         ///     Adds the constant step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
         public GraphTraversal<S, E2> Constant<E2> (E2 e)

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
----------------------------------------------------------------------
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 f143542..b18f515 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
@@ -343,6 +343,16 @@ class GraphTraversal extends Traversal {
   }
   
   /**
+   * Graph traversal connectedComponent method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  connectedComponent(...args) {
+    this.bytecode.addStep('connectedComponent', args);
+    return this;
+  }
+  
+  /**
    * Graph traversal constant method.
    * @param {...Object} args
    * @returns {GraphTraversal}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
index bb81d87..2410366 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
@@ -189,6 +189,10 @@ class GraphTraversal(Traversal):
         self.bytecode.add_step("coin", *args)
         return self
 
+    def connectedComponent(self, *args):
+        self.bytecode.add_step("connectedComponent", *args)
+        return self
+
     def constant(self, *args):
         self.bytecode.add_step("constant", *args)
         return self

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
index 3d51a94..c19d4c2 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
@@ -49,6 +49,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TailTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WhereTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.CoalesceTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConnectedComponentTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConstantTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.FlatMapTest;
@@ -140,6 +141,7 @@ public class ProcessComputerSuite extends AbstractGremlinSuite {
 
             // map
             CoalesceTest.Traversals.class,
+            ConnectedComponentTest.Traversals.class,
             ConstantTest.Traversals.class,
             CountTest.Traversals.class,
             FlatMapTest.Traversals.class,

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConnectedComponentTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConnectedComponentTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConnectedComponentTest.java
new file mode 100644
index 0000000..be5218d
--- /dev/null
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConnectedComponentTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.map;
+
+import org.apache.tinkerpop.gremlin.LoadGraphWith;
+import org.apache.tinkerpop.gremlin.process.computer.clustering.connected.ConnectedComponentVertexProgram;
+import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.Test;
+
+import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class ConnectedComponentTest extends AbstractGremlinProcessTest {
+
+    public abstract Traversal<Vertex, Vertex> get_g_V_connectedComponent();
+
+    public abstract Traversal<Vertex, Vertex> get_g_V_hasLabelXsoftwareX_connectedComponent();
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_connectedComponent() {
+        final Traversal<Vertex, Vertex> traversal = get_g_V_connectedComponent();
+        printTraversalForm(traversal);
+        int counter = 0;
+        while (traversal.hasNext()) {
+            final Vertex vertex = traversal.next();
+            counter++;
+            assertEquals("1", vertex.value(ConnectedComponentVertexProgram.COMPONENT));
+        }
+        assertEquals(6, counter);
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_hasLabelXsoftwareX_connectedComponent() {
+        final Traversal<Vertex, Vertex> traversal = get_g_V_hasLabelXsoftwareX_connectedComponent();
+        printTraversalForm(traversal);
+        int counter = 0;
+        while (traversal.hasNext()) {
+            final Vertex vertex = traversal.next();
+            final String name = vertex.value("name");
+            switch (name) {
+                case "lop":
+                case "ripple":
+                    assertEquals("3", vertex.value(ConnectedComponentVertexProgram.COMPONENT));
+                    break;
+            }
+            counter++;
+        }
+        assertEquals(2, counter);
+    }
+
+    public static class Traversals extends ConnectedComponentTest {
+        @Override
+        public Traversal<Vertex, Vertex> get_g_V_connectedComponent() {
+            return g.V().connectedComponent();
+        }
+
+        @Override
+        public Traversal<Vertex, Vertex> get_g_V_hasLabelXsoftwareX_connectedComponent() {
+            return g.V().hasLabel("software").connectedComponent();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee40274f/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkHadoopGraphProvider.java
----------------------------------------------------------------------
diff --git a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkHadoopGraphProvider.java b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkHadoopGraphProvider.java
index c778c6d..2f727c8 100644
--- a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkHadoopGraphProvider.java
+++ b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkHadoopGraphProvider.java
@@ -39,6 +39,7 @@ import org.apache.tinkerpop.gremlin.process.computer.Computer;
 import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
 import org.apache.tinkerpop.gremlin.process.computer.util.ComputerGraph;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConnectedComponentTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.PageRankTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.PeerPressureTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.ProgramTest;
@@ -115,6 +116,7 @@ public class SparkHadoopGraphProvider extends AbstractFileGraphProvider {
         if (null != loadGraphWith &&
                 !test.equals(ProgramTest.Traversals.class) &&
                 !test.equals(PageRankTest.Traversals.class) &&
+                !test.equals(ConnectedComponentTest.Traversals.class) &&
                 !test.equals(PeerPressureTest.Traversals.class) &&
                 !test.equals(FileSystemStorageCheck.class) &&
                 !testMethodName.equals("shouldSupportJobChaining") &&  // GraphComputerTest.shouldSupportJobChaining