TINKERPOP-1990 Implemented `ShortestPathVertexProgram` and 
`ShortestPathVertexProgramStep`.


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

Branch: refs/heads/TINKERPOP-1990
Commit: e1ba81b4b25c562050fae10b52ab67c5dec96232
Parents: 69b6f96
Author: Daniel Kuppitz <daniel_kupp...@hotmail.com>
Authored: Wed May 23 08:46:34 2018 -0400
Committer: Daniel Kuppitz <daniel_kupp...@hotmail.com>
Committed: Wed Aug 1 12:26:30 2018 -0700

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 docs/src/reference/the-graphcomputer.asciidoc   |  51 ++
 docs/src/reference/the-traversal.asciidoc       |  56 ++
 .../tinkerpop/gremlin/jsr223/CoreImports.java   |   4 +
 .../search/path/ShortestPathVertexProgram.java  | 581 +++++++++++++++++++
 .../traversal/step/map/ShortestPath.java        | 108 ++++
 .../step/map/ShortestPathVertexProgramStep.java | 174 ++++++
 .../gremlin/process/remote/RemoteGraph.java     |   8 +
 .../gremlin/process/traversal/Traversal.java    |  16 +-
 .../traversal/dsl/graph/GraphTraversal.java     |  20 +
 .../traversal/lambda/ColumnTraversal.java       |   8 +
 .../traversal/lambda/ConstantTraversal.java     |   9 +-
 .../traversal/lambda/ElementValueTraversal.java |  10 +-
 .../traversal/lambda/IdentityTraversal.java     |   7 +-
 .../process/traversal/lambda/LoopTraversal.java |   6 +
 .../traversal/lambda/TokenTraversal.java        |   8 +
 .../process/traversal/lambda/TrueTraversal.java |   5 +
 .../structure/io/graphson/GraphSONModule.java   |   2 +-
 .../gremlin/structure/io/gryo/GryoVersion.java  |  12 +-
 .../structure/io/gryo/UtilSerializers.java      |  15 +
 .../gremlin/structure/util/Attachable.java      |   3 +-
 .../traversal/dsl/graph/GraphTraversalTest.java |   2 +-
 .../Process/Traversal/GraphTraversal.cs         |  11 +-
 .../lib/process/graph-traversal.js              |  10 +
 .../gremlin_python/process/graph_traversal.py   |   4 +
 .../tinkerpop/gremlin/AbstractGremlinTest.java  |   6 +-
 .../process/AbstractGremlinProcessTest.java     |  11 +-
 .../gremlin/process/ProcessComputerSuite.java   |   5 +
 .../search/path/ShortestPathTestHelper.java     | 100 ++++
 .../path/ShortestPathVertexProgramTest.java     | 297 ++++++++++
 .../traversal/step/map/ShortestPathTest.java    | 353 +++++++++++
 pom.xml                                         |   3 +
 32 files changed, 1880 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 430f52d..4ffc6b7 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 
 This release also includes changes from <<release-3-3-3, 3.3.3>>.
 
+* Implemented `ShortestPathVertexProgram` and the `shortestPath()` step.
 * `AbstractGraphProvider` uses `g.io()` for loading test data.
 * Added the `io()` start step and `read()` and `write()` termination steps to 
the Gremlin language.
 * Added `GraphFeatures.supportsIoRead()` and `GraphFeatures.supportsIoWrite()`.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/docs/src/reference/the-graphcomputer.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-graphcomputer.asciidoc 
b/docs/src/reference/the-graphcomputer.asciidoc
index 1d7586c..3d7ec00 100644
--- a/docs/src/reference/the-graphcomputer.asciidoc
+++ b/docs/src/reference/the-graphcomputer.asciidoc
@@ -403,6 +403,57 @@ g.V().peerPressure().by('cluster').valueMap()
 g.V().peerPressure().by(outE('knows')).by('cluster').valueMap()
 ----
 
+[[shortestpathvertexprogram]]
+=== ShortestPathVertexProgram
+
+The `ShortestPathVertexProram` provides an easy way to find shortest 
non-cyclic paths in the graph. It provides several options to configure
+the output format, the start- and end-vertices, the direction, a custom 
distance function, as well as a distance limitation. By default it just
+finds all undirected, shortest paths in the graph.
+
+[gremlin-groovy,modern]
+----
+spvp = ShortestPathVertexProgram.build().create() <1>
+result = graph.compute().program(spvp).submit().get() <2>
+result.memory().get(ShortestPathVertexProgram.SHORTEST_PATHS) <3>
+----
+
+<1> Create a `ShortestPathVertexProgram` with its default configuration.
+<2> Execute the `ShortestPathVertexProgram`.
+<3> Get all shortest paths from the results memory.
+
+[gremlin-groovy,modern]
+----
+spvp = ShortestPathVertexProgram.build().includeEdges(true).create() <1>
+result = graph.compute().program(spvp).submit().get() <2>
+result.memory().get(ShortestPathVertexProgram.SHORTEST_PATHS) <3>
+----
+
+<1> Create a `ShortestPathVertexProgram` as before, but configure it to 
include edges in the result.
+<2> Execute the `ShortestPathVertexProgram`.
+<3> Get all shortest paths from the results memory.
+
+The `ShortestPathVertexProgram.Builder` provides the following configuration 
methods:
+
+[width="100%",cols="3,15,5",options="header"]
+|=========================================================
+| Method | Description | Default
+| `source(Traversal)` | Sets a filter traversal for the start vertices (e.g. 
`__.has('name','marko')`). | all vertices (`__.identity()`)
+| `target(Traversal)` | Sets a filter traversal for the end vertices. | all 
vertices
+| `edgeDirection(Direction)` | Sets the direction to traverse during the 
shortest path discovery. | `Direction.BOTH`
+| `edgeTraversal(Traversal)` | Sets a traversal that emits the edges to 
traverse from the current vertex. | `__.bothE()`
+| `distanceProperty(String)` | Sets the edge property to use for the distance 
calculations. | none
+| `distanceTraversal(Traversal)` | Sets the traversal that calculates the 
distance for the current edge. | `__.constant(1)`
+| `maxDistance(Traversal)` | Limits the shortest path distance. | none
+| `includeEdges(Boolean)` | Whether to include edges in shortest paths or not. 
| `false`
+|=========================================================
+
+IMPORTANT: If a maximum distance is provided, the discovery process will only 
stop to follow a path at this distance if there was no
+custom distance property or traversal provided. Custom distances can be 
negative, hence exceeding the maximum distance doesn't mean that there
+can't be any more valid paths. However, paths will be filtered at the end, 
when no more non-cyclic paths can be found. The bottom line is that
+custom distance properties or traversals can lead to much longer runtimes and 
a much higher memory consumption.
+
+Note that `GraphTraversal` provides a 
<<shortestpath-step,`shortestPath()`>>-step.
+
 [[bulkdumpervertexprogram]]
 [[clonevertexprogram]]
 === CloneVertexProgram

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-traversal.asciidoc 
b/docs/src/reference/the-traversal.asciidoc
index 7d385c8..f4a60fb 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -2663,6 +2663,62 @@ 
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/structure/Column.html++[`Column`],
 
link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/Pop.html++[`Pop`]
 
+[[shortestpath-step]]
+=== ShortestPath step
+
+The `shortestPath()`-step provides an easy way to find shortest non-cyclic 
paths in a graph. It is configurable
+using the `with()`-modulator with the options given below.
+
+[width="100%",cols="3,3,15,5",options="header"]
+|=========================================================
+| Key | Type | Description | Default
+| `target` | `Traversal` | Sets a filter traversal for the end vertices (e.g. 
`__.has('name','marko')`). | all vertices (`__.identity()`)
+| `edges` | `Traversal` or `Direction` | Sets a `Traversal` that emits the 
edges to traverse from the current vertex or the `Direction` to traverse during 
the shortest path discovery. | `Direction.BOTH`
+| `distance` | `Traversal` or `String` | Sets the `Traversal` that calculates 
the distance for the current edge or the name of an edge property to use for 
the distance calculations. | `__.constant(1)`
+| `maxDistance` | `Number` | Sets the distance limit for all shortest paths. | 
none
+| `includeEdges` | `Boolean` | Whether to include edges in the result or not. 
| `false`
+|=========================================================
+
+[gremlin-groovy,modern]
+----
+a = g.withComputer()
+a.V().shortestPath() <1>
+a.V().has('person','name','marko').shortestPath() <2>
+a.V().shortestPath().with(ShortestPath.target, __.has('name','peter')) <3>
+a.V().shortestPath().
+        with(ShortestPath.edges, Direction.IN).
+        with(ShortestPath.target, __.has('name','josh')) <4>
+a.V().has('person','name','marko').
+      shortestPath().
+        with(ShortestPath.target, __.has('name','josh')) <5>
+a.V().has('person','name','marko').
+      shortestPath().
+        with(ShortestPath.target, __.has('name','josh')).
+        with(ShortestPath.distance, 'weight') <6>
+a.V().has('person','name','marko').
+      shortestPath().
+        with(ShortestPath.target, __.has('name','josh')).
+        with(ShortestPath.includeEdges, true) <7>
+g.inject(a.V().shortestPath().
+           with(ShortestPath.distance, 'weight').
+           with(ShortestPath.includeEdges, true).
+           with(ShortestPath.maxDistance, 1).toList().toArray()).
+  map(unfold().values('name','weight').fold()) <8>
+----
+
+<1> Find all shortest paths.
+<2> Find all shortest paths from `marko`.
+<3> Find all shortest paths to `peter`.
+<4> Find all in-directed paths to `josh`.
+<5> Find all shortest paths from `marko` to `josh`.
+<6> Find all shortest paths from `marko` to `josh` using a custom distance 
property.
+<7> Find all shortest paths from `marko` to `josh` and include edges in the 
result.
+<8> Find all shortest paths using a custom distance property and limit the 
distance to 1. Inject the result into a OLTP GraphTraversal in order to be able 
to select properties from all elements in all paths.
+
+*Additional References*
+
+link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#shortestPath--++[`shortestPath()`]
+
 [[simplepath-step]]
 === SimplePath Step
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/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 72ad47a..9830cdd 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
@@ -47,10 +47,12 @@ import 
org.apache.tinkerpop.gremlin.process.computer.clustering.peerpressure.Clu
 import 
org.apache.tinkerpop.gremlin.process.computer.clustering.peerpressure.PeerPressureVertexProgram;
 import 
org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankMapReduce;
 import 
org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgram;
+import 
org.apache.tinkerpop.gremlin.process.computer.search.path.ShortestPathVertexProgram;
 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.PageRank;
 import 
org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PeerPressure;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ShortestPath;
 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;
@@ -268,6 +270,8 @@ public final class CoreImports {
         CLASS_IMPORTS.add(PageRank.class);
         CLASS_IMPORTS.add(PageRankMapReduce.class);
         CLASS_IMPORTS.add(PageRankVertexProgram.class);
+        CLASS_IMPORTS.add(ShortestPath.class);
+        CLASS_IMPORTS.add(ShortestPathVertexProgram.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/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java
new file mode 100644
index 0000000..549dff9
--- /dev/null
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java
@@ -0,0 +1,581 @@
+/*
+ * 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.search.path;
+
+import org.apache.commons.configuration.Configuration;
+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.traversal.step.map.ProgramVertexProgramStep;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.VertexProgramStep;
+import 
org.apache.tinkerpop.gremlin.process.computer.util.AbstractVertexProgramBuilder;
+import org.apache.tinkerpop.gremlin.process.traversal.Operator;
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.ImmutablePath;
+import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.IndexedTraverserSet;
+import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
+import org.apache.tinkerpop.gremlin.process.traversal.util.PureTraversal;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+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.reference.ReferenceFactory;
+import org.apache.tinkerpop.gremlin.util.NumberHelper;
+import org.javatuples.Pair;
+import org.javatuples.Triplet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+public class ShortestPathVertexProgram implements VertexProgram<Triplet<Path, 
Edge, Number>> {
+
+    @SuppressWarnings("WeakerAccess")
+    public static final String SHORTEST_PATHS = 
"gremlin.shortestPathVertexProgram.shortestPaths";
+
+    private static final String SOURCE_VERTEX_FILTER = 
"gremlin.shortestPathVertexProgram.sourceVertexFilter";
+    private static final String TARGET_VERTEX_FILTER = 
"gremlin.shortestPathVertexProgram.targetVertexFilter";
+    private static final String EDGE_TRAVERSAL = 
"gremlin.shortestPathVertexProgram.edgeTraversal";
+    private static final String DISTANCE_TRAVERSAL = 
"gremlin.shortestPathVertexProgram.distanceTraversal";
+    private static final String MAX_DISTANCE = 
"gremlin.shortestPathVertexProgram.maxDistance";
+    private static final String INCLUDE_EDGES = 
"gremlin.shortestPathVertexProgram.includeEdges";
+
+    private static final String STATE = 
"gremlin.shortestPathVertexProgram.state";
+    private static final String PATHS = 
"gremlin.shortestPathVertexProgram.paths";
+    private static final String VOTE_TO_HALT = 
"gremlin.shortestPathVertexProgram.voteToHalt";
+
+    private static final int SEARCH = 0;
+    private static final int COLLECT_PATHS = 1;
+    private static final int UPDATE_HALTED_TRAVERSERS = 2;
+
+    public static final PureTraversal<Vertex, ?> 
DEFAULT_VERTEX_FILTER_TRAVERSAL = new PureTraversal<>(
+            __.<Vertex> identity().asAdmin()); // todo: new 
IdentityTraversal<>()
+    public static final PureTraversal<Vertex, Edge> DEFAULT_EDGE_TRAVERSAL = 
new PureTraversal<>(__.bothE().asAdmin());
+    public static final PureTraversal<Edge, Number> DEFAULT_DISTANCE_TRAVERSAL 
= new PureTraversal<>(
+            __.<Edge> start().<Number> constant(1).asAdmin()); // todo: new 
ConstantTraversal<>(1)
+
+    private TraverserSet<Vertex> haltedTraversers;
+    private IndexedTraverserSet<Vertex, Vertex> haltedTraversersIndex;
+    private PureTraversal<?, ?> traversal;
+    private PureTraversal<Vertex, ?> sourceVertexFilterTraversal = 
DEFAULT_VERTEX_FILTER_TRAVERSAL.clone();
+    private PureTraversal<Vertex, ?> targetVertexFilterTraversal = 
DEFAULT_VERTEX_FILTER_TRAVERSAL.clone();
+    private PureTraversal<Vertex, Edge> edgeTraversal = 
DEFAULT_EDGE_TRAVERSAL.clone();
+    private PureTraversal<Edge, Number> distanceTraversal = 
DEFAULT_DISTANCE_TRAVERSAL.clone();
+    private Step<Vertex, Path> programStep;
+    private Number maxDistance;
+    private boolean distanceEqualsNumberOfHops;
+    private boolean includeEdges;
+    private boolean standalone;
+
+    private static final Set<VertexComputeKey> VERTEX_COMPUTE_KEYS = new 
HashSet<>(Arrays.asList(
+            VertexComputeKey.of(PATHS, true),
+            VertexComputeKey.of(TraversalVertexProgram.HALTED_TRAVERSERS, 
false)));
+
+    private final Set<MemoryComputeKey> memoryComputeKeys = new 
HashSet<>(Arrays.asList(
+            MemoryComputeKey.of(VOTE_TO_HALT, Operator.and, false, true),
+            MemoryComputeKey.of(STATE, Operator.assign, true, true)));
+
+    private ShortestPathVertexProgram() {
+
+    }
+
+    @Override
+    public void loadState(final Graph graph, final Configuration 
configuration) {
+
+        if (configuration.containsKey(SOURCE_VERTEX_FILTER))
+            this.sourceVertexFilterTraversal = 
PureTraversal.loadState(configuration, SOURCE_VERTEX_FILTER, graph);
+
+        if (configuration.containsKey(TARGET_VERTEX_FILTER))
+            this.targetVertexFilterTraversal = 
PureTraversal.loadState(configuration, TARGET_VERTEX_FILTER, graph);
+
+        if (configuration.containsKey(EDGE_TRAVERSAL))
+            this.edgeTraversal = PureTraversal.loadState(configuration, 
EDGE_TRAVERSAL, graph);
+
+        if (configuration.containsKey(DISTANCE_TRAVERSAL))
+            this.distanceTraversal = PureTraversal.loadState(configuration, 
DISTANCE_TRAVERSAL, graph);
+
+        if (configuration.containsKey(MAX_DISTANCE))
+            this.maxDistance = (Number) 
configuration.getProperty(MAX_DISTANCE);
+
+        this.distanceEqualsNumberOfHops = 
this.distanceTraversal.equals(DEFAULT_DISTANCE_TRAVERSAL);
+        this.includeEdges = configuration.getBoolean(INCLUDE_EDGES, false);
+        this.standalone = 
!configuration.containsKey(VertexProgramStep.ROOT_TRAVERSAL);
+
+        if (!this.standalone) {
+            this.traversal = PureTraversal.loadState(configuration, 
VertexProgramStep.ROOT_TRAVERSAL, graph);
+            final String programStepId = 
configuration.getString(ProgramVertexProgramStep.STEP_ID);
+            for (final Step step : this.traversal.get().getSteps()) {
+                if (step.getId().equals(programStepId)) {
+                    //noinspection unchecked
+                    this.programStep = step;
+                    break;
+                }
+            }
+        }
+
+        this.haltedTraversers = 
TraversalVertexProgram.loadHaltedTraversers(configuration);
+        this.haltedTraversersIndex = new IndexedTraverserSet<>(v -> v);
+        for (final Traverser.Admin<Vertex> traverser : this.haltedTraversers) {
+            this.haltedTraversersIndex.add(traverser.split());
+        }
+        this.memoryComputeKeys.add(MemoryComputeKey.of(SHORTEST_PATHS, 
Operator.addAll, true, !standalone));
+    }
+
+    @Override
+    public void storeState(final Configuration configuration) {
+        VertexProgram.super.storeState(configuration);
+        this.sourceVertexFilterTraversal.storeState(configuration, 
SOURCE_VERTEX_FILTER);
+        this.targetVertexFilterTraversal.storeState(configuration, 
TARGET_VERTEX_FILTER);
+        this.edgeTraversal.storeState(configuration, EDGE_TRAVERSAL);
+        this.distanceTraversal.storeState(configuration, DISTANCE_TRAVERSAL);
+        configuration.setProperty(INCLUDE_EDGES, this.includeEdges);
+        if (this.maxDistance != null)
+            configuration.setProperty(MAX_DISTANCE, maxDistance);
+        if (this.traversal != null) {
+            this.traversal.storeState(configuration, 
ProgramVertexProgramStep.ROOT_TRAVERSAL);
+            configuration.setProperty(ProgramVertexProgramStep.STEP_ID, 
this.programStep.getId());
+        }
+        TraversalVertexProgram.storeHaltedTraversers(configuration, 
this.haltedTraversers);
+    }
+
+    @Override
+    public Set<VertexComputeKey> getVertexComputeKeys() {
+        return VERTEX_COMPUTE_KEYS;
+    }
+
+    @Override
+    public Set<MemoryComputeKey> getMemoryComputeKeys() {
+        return memoryComputeKeys;
+    }
+
+    @Override
+    public Set<MessageScope> getMessageScopes(final Memory memory) {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public VertexProgram<Triplet<Path, Edge, Number>> clone() {
+        try {
+            final ShortestPathVertexProgram clone = 
(ShortestPathVertexProgram) super.clone();
+            if (null != this.edgeTraversal)
+                clone.edgeTraversal = this.edgeTraversal.clone();
+            if (null != this.sourceVertexFilterTraversal)
+                clone.sourceVertexFilterTraversal = 
this.sourceVertexFilterTraversal.clone();
+            if (null != this.targetVertexFilterTraversal)
+                clone.targetVertexFilterTraversal = 
this.targetVertexFilterTraversal.clone();
+            if (null != this.distanceTraversal)
+                clone.distanceTraversal = this.distanceTraversal.clone();
+            if (null != this.traversal) {
+                clone.traversal = this.traversal.clone();
+                for (final Step step : clone.traversal.get().getSteps()) {
+                    if (step.getId().equals(this.programStep.getId())) {
+                        //noinspection unchecked
+                        clone.programStep = step;
+                        break;
+                    }
+                }
+            }
+            return clone;
+        } catch (final CloneNotSupportedException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public GraphComputer.ResultGraph getPreferredResultGraph() {
+        return GraphComputer.ResultGraph.ORIGINAL;
+    }
+
+    @Override
+    public GraphComputer.Persist getPreferredPersist() {
+        return GraphComputer.Persist.NOTHING;
+    }
+
+    @Override
+    public void setup(final Memory memory) {
+        memory.set(VOTE_TO_HALT, true);
+        memory.set(STATE, SEARCH);
+    }
+
+    @Override
+    public void execute(final Vertex vertex, final Messenger<Triplet<Path, 
Edge, Number>> messenger, final Memory memory) {
+
+        switch (memory.<Integer>get(STATE)) {
+
+            case COLLECT_PATHS:
+                collectShortestPaths(vertex, memory);
+                return;
+
+            case UPDATE_HALTED_TRAVERSERS:
+                updateHaltedTraversers(vertex, memory);
+                return;
+        }
+
+        boolean voteToHalt = true;
+
+        if (memory.isInitialIteration()) {
+
+            copyHaltedTraversersFromMemory(vertex);
+
+            if (!isStartVertex(vertex)) return;
+
+            final Map<Vertex, Pair<Number, Set<Path>>> paths = new HashMap<>();
+            final Path path;
+            final Set<Path> pathSet = new HashSet<>();
+
+            pathSet.add(path = makePath(vertex));
+            paths.put(vertex, Pair.with(0, pathSet));
+
+            vertex.property(VertexProperty.Cardinality.single, PATHS, paths);
+
+            processEdges(vertex, path, 0, messenger);
+
+            voteToHalt = false;
+
+        } else {
+
+            final Map<Vertex, Pair<Number, Set<Path>>> paths =
+                    vertex.<Map<Vertex, Pair<Number, 
Set<Path>>>>property(PATHS).orElseGet(HashMap::new);
+            final Iterator<Triplet<Path, Edge, Number>> iterator = 
messenger.receiveMessages();
+
+            while (iterator.hasNext()) {
+
+                final Triplet<Path, Edge, Number> triplet = iterator.next();
+                final Path sourcePath = triplet.getValue0();
+                final Number distance = triplet.getValue2();
+                final Vertex sourceVertex = sourcePath.get(0);
+
+                Path newPath = null;
+
+                if (paths.containsKey(sourceVertex)) {
+
+                    final Number currentShortestDistance = 
paths.get(sourceVertex).getValue0();
+                    final int cmp = NumberHelper.compare(distance, 
currentShortestDistance);
+
+                    if (cmp <= 0) {
+                        newPath = extendPath(sourcePath, triplet.getValue1(), 
vertex);
+                        if (cmp < 0) {
+                            final Set<Path> pathSet = new HashSet<>();
+                            pathSet.add(newPath);
+                            paths.put(sourceVertex, Pair.with(distance, 
pathSet));
+                        } else {
+                            paths.get(sourceVertex).getValue1().add(newPath);
+                        }
+                    }
+                } else if (!exceedsMaxDistance(distance)) {
+                    final Set<Path> pathSet = new HashSet<>();
+                    pathSet.add(newPath = extendPath(sourcePath, 
triplet.getValue1(), vertex));
+                    paths.put(sourceVertex, Pair.with(distance, pathSet));
+                }
+
+                if (newPath != null) {
+                    vertex.property(VertexProperty.Cardinality.single, PATHS, 
paths);
+                    processEdges(vertex, newPath, distance, messenger);
+                    voteToHalt = false;
+                }
+            }
+        }
+
+        memory.add(VOTE_TO_HALT, voteToHalt);
+    }
+
+    @Override
+    public boolean terminate(final Memory memory) {
+        if (memory.isInitialIteration() && this.haltedTraversersIndex != null) 
{
+            this.haltedTraversersIndex.clear();
+        }
+        final boolean voteToHalt = memory.get(VOTE_TO_HALT);
+        if (voteToHalt) {
+            final int state = memory.get(STATE);
+            if (state == COLLECT_PATHS) {
+                if (this.standalone) return true;
+                memory.set(STATE, UPDATE_HALTED_TRAVERSERS);
+                return false;
+            }
+            if (state == UPDATE_HALTED_TRAVERSERS) return true;
+            else memory.set(STATE, COLLECT_PATHS);
+            return false;
+        } else {
+            memory.set(VOTE_TO_HALT, true);
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+
+        final List<String> options = new ArrayList<>();
+        final Function<String, String> shortName = name -> 
name.substring(name.lastIndexOf(".") + 1);
+
+        if 
(!this.sourceVertexFilterTraversal.equals(DEFAULT_VERTEX_FILTER_TRAVERSAL)) {
+            options.add(shortName.apply(SOURCE_VERTEX_FILTER) + "=" + 
this.sourceVertexFilterTraversal.get());
+        }
+
+        if 
(!this.targetVertexFilterTraversal.equals(DEFAULT_VERTEX_FILTER_TRAVERSAL)) {
+            options.add(shortName.apply(TARGET_VERTEX_FILTER) + "=" + 
this.targetVertexFilterTraversal.get());
+        }
+
+        if (!this.edgeTraversal.equals(DEFAULT_EDGE_TRAVERSAL)) {
+            options.add(shortName.apply(EDGE_TRAVERSAL) + "=" + 
this.edgeTraversal.get());
+        }
+
+        if (!this.distanceTraversal.equals(DEFAULT_DISTANCE_TRAVERSAL)) {
+            options.add(shortName.apply(DISTANCE_TRAVERSAL) + "=" + 
this.distanceTraversal.get());
+        }
+
+        options.add(shortName.apply(INCLUDE_EDGES) + "=" + this.includeEdges);
+
+        return StringFactory.vertexProgramString(this, String.join(", ", 
options));
+    }
+
+    //////////////////////////////
+
+    private void copyHaltedTraversersFromMemory(final Vertex vertex) {
+        final Collection<Traverser.Admin<Vertex>> traversers = 
this.haltedTraversersIndex.get(vertex);
+        if (traversers != null) {
+            final TraverserSet<Vertex> newHaltedTraversers = new 
TraverserSet<>();
+            newHaltedTraversers.addAll(traversers);
+            vertex.property(VertexProperty.Cardinality.single, 
TraversalVertexProgram.HALTED_TRAVERSERS, newHaltedTraversers);
+        }
+    }
+
+
+    private static Path makePath(final Vertex newVertex) {
+        return extendPath(null, newVertex);
+    }
+
+    private static Path extendPath(final Path currentPath, final Element... 
elements) {
+        Path result = ImmutablePath.make();
+        if (currentPath != null) {
+            for (final Object o : currentPath.objects()) {
+                result = result.extend(o, Collections.emptySet());
+            }
+        }
+        for (final Element element : elements) {
+            if (element != null) {
+                result = result.extend(ReferenceFactory.detach(element), 
Collections.emptySet());
+            }
+        }
+        return result;
+    }
+
+    private boolean isStartVertex(final Vertex vertex) {
+        if (this.standalone) {
+            final Traversal.Admin<Vertex, ?> filterTraversal = 
this.sourceVertexFilterTraversal.getPure();
+            
filterTraversal.addStart(filterTraversal.getTraverserGenerator().generate(vertex,
 filterTraversal.getStartStep(), 1));
+            return filterTraversal.hasNext();
+        }
+        return 
vertex.property(TraversalVertexProgram.HALTED_TRAVERSERS).isPresent();
+    }
+
+    private boolean isEndVertex(final Vertex vertex) {
+        final Traversal.Admin<Vertex, ?> filterTraversal = 
this.targetVertexFilterTraversal.getPure();
+        //noinspection unchecked
+        final Step<Vertex, Vertex> startStep = (Step<Vertex, Vertex>) 
filterTraversal.getStartStep();
+        
filterTraversal.addStart(filterTraversal.getTraverserGenerator().generate(vertex,
 startStep, 1));
+        return filterTraversal.hasNext();
+    }
+
+    private void processEdges(final Vertex vertex, final Path currentPath, 
final Number currentDistance,
+                              final Messenger<Triplet<Path, Edge, Number>> 
messenger) {
+
+        final Traversal.Admin<Vertex, Edge> edgeTraversal = 
this.edgeTraversal.getPure();
+        
edgeTraversal.addStart(edgeTraversal.getTraverserGenerator().generate(vertex, 
edgeTraversal.getStartStep(), 1));
+
+        while (edgeTraversal.hasNext()) {
+            final Edge edge = edgeTraversal.next();
+            final Number distance = getDistance(edge);
+
+            Vertex otherV = edge.inVertex();
+            if (otherV.equals(vertex))
+                otherV = edge.outVertex();
+
+            if (!currentPath.objects().contains(otherV)) {
+                messenger.sendMessage(MessageScope.Global.of(otherV),
+                        Triplet.with(currentPath, this.includeEdges ? edge : 
null,
+                                NumberHelper.add(currentDistance, distance)));
+            }
+        }
+    }
+
+    private void updateHaltedTraversers(final Vertex vertex, final Memory 
memory) {
+        if (isStartVertex(vertex)) {
+            final List<Path> paths = memory.get(SHORTEST_PATHS);
+            if 
(vertex.property(TraversalVertexProgram.HALTED_TRAVERSERS).isPresent()) {
+                final TraverserSet<Vertex> haltedTraversers = 
vertex.value(TraversalVertexProgram.HALTED_TRAVERSERS);
+                final TraverserSet<Path> newHaltedTraversers = new 
TraverserSet<>();
+                for (final Traverser.Admin<Vertex> traverser : 
haltedTraversers) {
+                    final Vertex v = traverser.get();
+                    for (final Path path : paths) {
+                        if (path.get(0).equals(v)) {
+                            newHaltedTraversers.add(traverser.split(path, 
this.programStep));
+                        }
+                    }
+                }
+                vertex.property(VertexProperty.Cardinality.single, 
TraversalVertexProgram.HALTED_TRAVERSERS, newHaltedTraversers);
+            }
+        }
+    }
+
+    private Number getDistance(final Edge edge) {
+        if (this.distanceEqualsNumberOfHops) return 1;
+        final Traversal.Admin<Edge, Number> traversal = 
this.distanceTraversal.getPure();
+        traversal.addStart(traversal.getTraverserGenerator().generate(edge, 
traversal.getStartStep(), 1));
+        return traversal.tryNext().orElse(0);
+    }
+
+    private boolean exceedsMaxDistance(final Number distance) {
+        // This method is used to stop the message sending for paths that 
exceed the specified maximum distance. Since
+        // custom distances can be negative, this method should only return 
true if the distance is calculated based on
+        // the number of hops.
+        return this.distanceEqualsNumberOfHops && this.maxDistance != null
+                && NumberHelper.compare(distance, this.maxDistance) > 0;
+    }
+
+    private void collectShortestPaths(final Vertex vertex, final Memory 
memory) {
+
+        final VertexProperty<Map<Vertex, Pair<Number, Set<Path>>>> 
pathProperty = vertex.property(PATHS);
+
+        if (pathProperty.isPresent()) {
+
+            final Map<Vertex, Pair<Number, Set<Path>>> paths = 
pathProperty.value();
+            final List<Path> result = new ArrayList<>();
+
+            for (final Pair<Number, Set<Path>> pair : paths.values()) {
+                for (final Path path : pair.getValue1()) {
+                    if (isEndVertex(vertex)) {
+                        if (this.distanceEqualsNumberOfHops ||
+                                this.maxDistance == null ||
+                                NumberHelper.compare(pair.getValue0(), 
this.maxDistance) <= 0) {
+                            result.add(path);
+                        }
+                    }
+                }
+            }
+
+            pathProperty.remove();
+
+            memory.add(SHORTEST_PATHS, result);
+        }
+    }
+
+    //////////////////////////////
+
+    public static Builder build() {
+        return new Builder();
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    public static final class Builder extends 
AbstractVertexProgramBuilder<Builder> {
+
+
+        private Builder() {
+            super(ShortestPathVertexProgram.class);
+        }
+
+        public Builder source(final Traversal<Vertex, ?> sourceVertexFilter) {
+            if (null == sourceVertexFilter) throw 
Graph.Exceptions.argumentCanNotBeNull("sourceVertexFilter");
+            PureTraversal.storeState(this.configuration, SOURCE_VERTEX_FILTER, 
sourceVertexFilter.asAdmin());
+            return this;
+        }
+
+        public Builder target(final Traversal<Vertex, ?> targetVertexFilter) {
+            if (null == targetVertexFilter) throw 
Graph.Exceptions.argumentCanNotBeNull("targetVertexFilter");
+            PureTraversal.storeState(this.configuration, TARGET_VERTEX_FILTER, 
targetVertexFilter.asAdmin());
+            return this;
+        }
+
+        public Builder edgeDirection(final Direction direction) {
+            if (null == direction) throw 
Graph.Exceptions.argumentCanNotBeNull("direction");
+            return edgeTraversal(__.toE(direction));
+        }
+
+        public Builder edgeTraversal(final Traversal<Vertex, Edge> 
edgeTraversal) {
+            if (null == edgeTraversal) throw 
Graph.Exceptions.argumentCanNotBeNull("edgeTraversal");
+            PureTraversal.storeState(this.configuration, EDGE_TRAVERSAL, 
edgeTraversal.asAdmin());
+            return this;
+        }
+
+        public Builder distanceProperty(final String distance) {
+            //noinspection unchecked
+            return distance != null
+                    ? distanceTraversal(__.values(distance)) // todo: 
(Traversal) new ElementValueTraversal<>(distance)
+                    : distanceTraversal(DEFAULT_DISTANCE_TRAVERSAL.getPure());
+        }
+
+        public Builder distanceTraversal(final Traversal<Edge, Number> 
distanceTraversal) {
+            if (null == distanceTraversal) throw 
Graph.Exceptions.argumentCanNotBeNull("distanceTraversal");
+            PureTraversal.storeState(this.configuration, DISTANCE_TRAVERSAL, 
distanceTraversal.asAdmin());
+            return this;
+        }
+
+        public Builder maxDistance(final Number distance) {
+            if (null != distance)
+                this.configuration.setProperty(MAX_DISTANCE, distance);
+            else
+                this.configuration.clearProperty(MAX_DISTANCE);
+            return this;
+        }
+
+        public Builder includeEdges(final boolean include) {
+            this.configuration.setProperty(INCLUDE_EDGES, include);
+            return this;
+        }
+    }
+
+    ////////////////////////////
+
+    @Override
+    public Features getFeatures() {
+        return new Features() {
+            @Override
+            public boolean requiresGlobalMessageScopes() {
+                return true;
+            }
+
+            @Override
+            public boolean requiresVertexPropertyAddition() {
+                return true;
+            }
+        };
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ShortestPath.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ShortestPath.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ShortestPath.java
new file mode 100644
index 0000000..b0d7815
--- /dev/null
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ShortestPath.java
@@ -0,0 +1,108 @@
+/*
+ * 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.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+
+/**
+ * Configuration options to be passed to the {@link 
GraphTraversal#with(String, Object)} step.
+ */
+public class ShortestPath {
+
+    /**
+     * Configures the traversal to use to filter the target vertices for all 
shortest paths.
+     */
+    public static final String target = 
Graph.Hidden.hide("tinkerpop.shortestPath.target");
+
+    /**
+     * Configures the direction or traversal to use to filter the edges 
traversed during the shortest path search phase.
+     */
+    public static final String edges = 
Graph.Hidden.hide("tinkerpop.shortestPath.edges");
+
+    /**
+     * Configures the edge property or traversal to use for shortest path 
distance calculations.
+     */
+    public static final String distance = 
Graph.Hidden.hide("tinkerpop.shortestPath.distance");
+
+    /**
+     * Configures the maximum distance for all shortest paths. Any path with a 
distance greater than the specified
+     * value will not be returned.
+     */
+    public static final String maxDistance = 
Graph.Hidden.hide("tinkerpop.shortestPath.maxDistance");
+
+    /**
+     * Configures the inclusion of edges in the shortest path computation 
result.
+     */
+    public static final String includeEdges = 
Graph.Hidden.hide("tinkerpop.shortestPath.includeEdges");
+
+    static boolean configure(final ShortestPathVertexProgramStep step, final 
String key, final Object value) {
+
+        if (target.equals(key)) {
+            if (value instanceof Traversal) {
+                step.setTargetVertexFilter((Traversal) value);
+                return true;
+            }
+            else throw new IllegalArgumentException("ShortestPath.target 
requires a Traversal as its argument");
+        }
+        else if (edges.equals(key)) {
+            if (value instanceof Traversal) {
+                step.setEdgeTraversal((Traversal) value);
+                return true;
+            }
+            else if (value instanceof Direction) {
+                step.setEdgeTraversal(__.toE((Direction) value));
+                return true;
+            }
+            else throw new IllegalArgumentException(
+                    "ShortestPath.edges requires a Traversal or a Direction as 
its argument");
+        }
+        else if (distance.equals(key)) {
+            if (value instanceof Traversal) {
+                step.setDistanceTraversal((Traversal) value);
+                return true;
+            }
+            else if (value instanceof String) {
+                // todo: new ElementValueTraversal((String) value)
+                step.setDistanceTraversal(__.values((String) value));
+                return true;
+            }
+            else throw new IllegalArgumentException(
+                    "ShortestPath.distance requires a Traversal or a property 
name as its argument");
+        }
+        else if (maxDistance.equals(key)) {
+            if (value instanceof Number) {
+                step.setMaxDistance((Number) value);
+                return true;
+            }
+            else throw new IllegalArgumentException("ShortestPath.maxDistance 
requires a Number as its argument");
+        }
+        else if (includeEdges.equals(key)) {
+            if (value instanceof Boolean) {
+                step.setIncludeEdges((Boolean) value);
+                return true;
+            }
+            else throw new IllegalArgumentException("ShortestPath.includeEdges 
requires a Boolean as its argument");
+        }
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ShortestPathVertexProgramStep.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ShortestPathVertexProgramStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ShortestPathVertexProgramStep.java
new file mode 100644
index 0000000..692cb6a
--- /dev/null
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ShortestPathVertexProgramStep.java
@@ -0,0 +1,174 @@
+/*
+ * 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.*;
+import 
org.apache.tinkerpop.gremlin.process.computer.search.path.ShortestPathVertexProgram;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram;
+import org.apache.tinkerpop.gremlin.process.traversal.*;
+import org.apache.tinkerpop.gremlin.process.traversal.step.*;
+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.traverser.util.TraverserSet;
+import org.apache.tinkerpop.gremlin.process.traversal.util.PureTraversal;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.Serializer;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+public final class ShortestPathVertexProgramStep extends VertexProgramStep 
implements TraversalParent, Configuring {
+
+    private Parameters parameters = new Parameters();
+    private PureTraversal<Vertex, ?> targetVertexFilter = 
ShortestPathVertexProgram.DEFAULT_VERTEX_FILTER_TRAVERSAL.clone();
+    private PureTraversal<Vertex, Edge> edgeTraversal = 
ShortestPathVertexProgram.DEFAULT_EDGE_TRAVERSAL.clone();
+    private PureTraversal<Edge, Number> distanceTraversal = 
ShortestPathVertexProgram.DEFAULT_DISTANCE_TRAVERSAL.clone();
+    private Number maxDistance;
+    private boolean includeEdges;
+
+    public ShortestPathVertexProgramStep(final Traversal.Admin<?, ?> 
traversal) {
+        super(traversal);
+    }
+
+    void setTargetVertexFilter(final Traversal filterTraversal) {
+        this.targetVertexFilter = new 
PureTraversal<>(this.integrateChild(filterTraversal.asAdmin()));
+    }
+
+    void setEdgeTraversal(final Traversal edgeTraversal) {
+        this.edgeTraversal = new 
PureTraversal<>(this.integrateChild(edgeTraversal.asAdmin()));
+    }
+
+    void setDistanceTraversal(final Traversal distanceTraversal) {
+        this.distanceTraversal = new 
PureTraversal<>(this.integrateChild(distanceTraversal.asAdmin()));
+    }
+
+    void setMaxDistance(final Number maxDistance) {
+        this.maxDistance = maxDistance;
+    }
+
+    void setIncludeEdges(final boolean includeEdges) {
+        this.includeEdges = includeEdges;
+    }
+
+    @Override
+    public void configure(final Object... keyValues) {
+        if (!ShortestPath.configure(this, (String) keyValues[0], 
keyValues[1])) {
+            this.parameters.set(this, keyValues);
+        }
+    }
+
+    @Override
+    public Parameters getParameters() {
+        return parameters;
+    }
+
+    @Override
+    protected Traverser.Admin<ComputerResult> processNextStart() throws 
NoSuchElementException {
+        return super.processNextStart();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<Traversal.Admin<?, ?>> getLocalChildren() {
+        return Arrays.asList(
+                this.targetVertexFilter.get(),
+                this.edgeTraversal.get(),
+                this.distanceTraversal.get());
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.stepString(this, this.targetVertexFilter.get(), 
this.edgeTraversal.get(),
+                this.distanceTraversal.get(), this.maxDistance, 
this.includeEdges, new GraphFilter(this.computer));
+    }
+
+    @Override
+    public ShortestPathVertexProgram generateProgram(final Graph graph, final 
Memory memory) {
+
+        final ShortestPathVertexProgram.Builder builder = 
ShortestPathVertexProgram.build()
+                .target(this.targetVertexFilter.getPure())
+                .edgeTraversal(this.edgeTraversal.getPure())
+                .distanceTraversal(this.distanceTraversal.getPure())
+                .maxDistance(this.maxDistance)
+                .includeEdges(this.includeEdges);
+
+        //noinspection unchecked
+        final PureTraversal pureRootTraversal = new 
PureTraversal<>(this.traversal);
+        Object rootTraversalValue;
+        try {
+            rootTraversalValue = 
Base64.getEncoder().encodeToString(Serializer.serializeObject(pureRootTraversal));
+        } catch (final IOException ignored) {
+            rootTraversalValue = pureRootTraversal;
+        }
+
+        builder.configure(
+                ProgramVertexProgramStep.ROOT_TRAVERSAL, rootTraversalValue,
+                ProgramVertexProgramStep.STEP_ID, this.id);
+
+        if (memory.exists(TraversalVertexProgram.HALTED_TRAVERSERS)) {
+            final TraverserSet<?> haltedTraversers = 
memory.get(TraversalVertexProgram.HALTED_TRAVERSERS);
+            if (!haltedTraversers.isEmpty()) {
+                Object haltedTraversersValue;
+                try {
+                    haltedTraversersValue = 
Base64.getEncoder().encodeToString(Serializer.serializeObject(haltedTraversers));
+                } catch (final IOException ignored) {
+                    haltedTraversersValue = haltedTraversers;
+                }
+                builder.configure(TraversalVertexProgram.HALTED_TRAVERSERS, 
haltedTraversersValue);
+            }
+        }
+
+        return builder.create(graph);
+    }
+
+    @Override
+    public Set<TraverserRequirement> getRequirements() {
+        return TraversalParent.super.getSelfAndChildRequirements();
+    }
+
+    @Override
+    public ShortestPathVertexProgramStep clone() {
+        final ShortestPathVertexProgramStep clone = 
(ShortestPathVertexProgramStep) super.clone();
+        clone.targetVertexFilter = this.targetVertexFilter.clone();
+        clone.edgeTraversal = this.edgeTraversal.clone();
+        clone.distanceTraversal = this.distanceTraversal.clone();
+        return clone;
+    }
+
+    @Override
+    public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) {
+        super.setTraversal(parentTraversal);
+        this.integrateChild(this.targetVertexFilter.get());
+        this.integrateChild(this.edgeTraversal.get());
+        this.integrateChild(this.distanceTraversal.get());
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/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 2df5ecf..200645e 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
@@ -61,6 +61,10 @@ import java.util.Iterator;
         method = "*",
         reason = "https://issues.apache.org/jira/browse/TINKERPOP-1976";)
 @Graph.OptOut(
+        test = 
"org.apache.tinkerpop.gremlin.process.traversal.step.map.ShortestPathTest",
+        method = "*",
+        reason = "hmmmm")
+@Graph.OptOut(
         test = 
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.TranslationStrategyProcessTest",
         method = "*",
         reason = "hmmmm")
@@ -89,6 +93,10 @@ import java.util.Iterator;
         method = "*",
         reason = "RemoteGraph does not support direct Graph.compute() access")
 @Graph.OptOut(
+        test = 
"org.apache.tinkerpop.gremlin.process.computer.search.path.ShortestPathVertexProgramTest",
+        method = "*",
+        reason = "RemoteGraph does not support direct Graph.compute() access")
+@Graph.OptOut(
         test = 
"org.apache.tinkerpop.gremlin.process.computer.bulkloading.BulkLoaderVertexProgramTest",
         method = "*",
         reason = "RemoteGraph does not support direct Graph.compute() access")

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
index 220c995..30435ab 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
@@ -496,15 +496,17 @@ public interface Traversal<S, E> extends Iterator<E>, 
Serializable, Cloneable, A
         public void setGraph(final Graph graph);
 
         public default boolean equals(final Traversal.Admin<S, E> other) {
-            final List<Step> steps = this.getSteps();
-            final List<Step> otherSteps = other.getSteps();
-            if (steps.size() == otherSteps.size()) {
-                for (int i = 0; i < steps.size(); i++) {
-                    if (!steps.get(i).equals(otherSteps.get(i))) {
-                        return false;
+            if (this.getClass().equals(other.getClass())) {
+                final List<Step> steps = this.getSteps();
+                final List<Step> otherSteps = other.getSteps();
+                if (steps.size() == otherSteps.size()) {
+                    for (int i = 0; i < steps.size(); i++) {
+                        if (!steps.get(i).equals(otherSteps.get(i))) {
+                            return false;
+                        }
                     }
+                    return true;
                 }
-                return true;
             }
             return false;
         }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/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 a675ad1..593323c 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
@@ -22,6 +22,7 @@ import 
org.apache.tinkerpop.gremlin.process.computer.VertexProgram;
 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;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ShortestPathVertexProgramStep;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
@@ -2452,6 +2453,24 @@ public interface GraphTraversal<S, E> extends 
Traversal<S, E> {
     }
 
     /**
+     * Executes a Shortest Path algorithm over the graph.
+     *
+     * @return the traversal with the appended {@link 
ShortestPathVertexProgramStep}
+     * @see <a 
href="http://tinkerpop.apache.org/docs/${project.version}/reference/#shortestpath-step";
 target="_blank">Reference Documentation - ShortestPath Step</a>
+     */
+    public default GraphTraversal<S, Path> shortestPath() {
+        if (this.asAdmin().getEndStep() instanceof GraphStep) {
+            // This is very unfortunate, but I couldn't find another way to 
make it work. Without the additional
+            // IdentityStep, TraversalVertexProgram doesn't handle halted 
traversers as expected (it ignores both:
+            // HALTED_TRAVERSER stored in memory and stored as vertex 
properties); instead it just emits all vertices.
+            this.identity();
+        }
+        this.asAdmin().getBytecode().addStep(Symbols.shortestPath);
+        return (GraphTraversal<S, Path>) ((Traversal.Admin) this.asAdmin())
+                .addStep(new ShortestPathVertexProgramStep(this.asAdmin()));
+    }
+
+    /**
      * Executes an arbitrary {@link VertexProgram} over the graph.
      *
      * @return the traversal with the appended {@link ProgramVertexProgramStep}
@@ -2882,6 +2901,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 shortestPath = "shortestPath";
         public static final String program = "program";
 
         public static final String by = "by";

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ColumnTraversal.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ColumnTraversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ColumnTraversal.java
index 5023805..9e2f31c 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ColumnTraversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ColumnTraversal.java
@@ -22,6 +22,8 @@ package org.apache.tinkerpop.gremlin.process.traversal.lambda;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.structure.Column;
 
+import java.util.Objects;
+
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
@@ -57,4 +59,10 @@ public final class ColumnTraversal extends 
AbstractLambdaTraversal {
     public int hashCode() {
         return this.getClass().hashCode() ^ this.column.hashCode();
     }
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof ColumnTraversal
+                && Objects.equals(((ColumnTraversal) other).column, 
this.column);
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
index 91973b9..bcf82d4 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
@@ -18,6 +18,8 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.lambda;
 
+import java.util.Objects;
+
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
@@ -43,5 +45,10 @@ public final class ConstantTraversal<S, E> extends 
AbstractLambdaTraversal<S, E>
     public int hashCode() {
         return this.getClass().hashCode() ^ this.end.hashCode();
     }
-}
 
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof ConstantTraversal
+                && Objects.equals(((ConstantTraversal) other).end, this.end);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ElementValueTraversal.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ElementValueTraversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ElementValueTraversal.java
index 2e9b26c..fbfff62 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ElementValueTraversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ElementValueTraversal.java
@@ -22,6 +22,8 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.Element;
 
+import java.util.Objects;
+
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
@@ -62,4 +64,10 @@ public final class ElementValueTraversal<V> extends 
AbstractLambdaTraversal<Elem
     public int hashCode() {
         return super.hashCode() ^ this.propertyKey.hashCode();
     }
-}
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof ElementValueTraversal
+                && Objects.equals(((ElementValueTraversal) other).propertyKey, 
this.propertyKey);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/IdentityTraversal.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/IdentityTraversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/IdentityTraversal.java
index 98f85c0..1ed5749 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/IdentityTraversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/IdentityTraversal.java
@@ -41,4 +41,9 @@ public final class IdentityTraversal<S> extends 
AbstractLambdaTraversal<S, S> {
     public String toString() {
         return "identity";
     }
-}
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof IdentityTraversal;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/LoopTraversal.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/LoopTraversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/LoopTraversal.java
index ae03c94..68b6b18 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/LoopTraversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/LoopTraversal.java
@@ -55,4 +55,10 @@ public final class LoopTraversal<S> extends 
AbstractLambdaTraversal<S, S> {
     public int hashCode() {
         return this.getClass().hashCode() ^ Long.hashCode(this.maxLoops);
     }
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof LoopTraversal
+                && ((LoopTraversal) other).maxLoops == this.maxLoops;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java
index 4f98a54..d41c402 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TokenTraversal.java
@@ -22,6 +22,8 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.T;
 
+import java.util.Objects;
+
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
@@ -57,4 +59,10 @@ public final class TokenTraversal<S extends Element, E> 
extends AbstractLambdaTr
     public int hashCode() {
         return this.getClass().hashCode() ^ this.t.hashCode();
     }
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof TokenTraversal
+                && Objects.equals(((TokenTraversal) other).t, this.t);
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TrueTraversal.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TrueTraversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TrueTraversal.java
index 84c0db6..71580ce 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TrueTraversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/TrueTraversal.java
@@ -43,4 +43,9 @@ public final class TrueTraversal<S> extends 
AbstractLambdaTraversal<S, S> {
     public TrueTraversal<S> clone() {
         return INSTANCE;
     }
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof TrueTraversal;
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index 2e795a5..1bccd7c 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -129,7 +129,7 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
                     put(List.class, "List");
                     put(Set.class, "Set");
 
-                    // Tinkerpop Graph objects
+                    // TinkerPop Graph objects
                     put(Lambda.class, "Lambda");
                     put(Vertex.class, "Vertex");
                     put(Edge.class, "Edge");

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
index e7dfd93..6d55704 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
@@ -114,10 +114,10 @@ import 
org.apache.tinkerpop.gremlin.util.function.MultiComparator;
 import org.apache.tinkerpop.shaded.kryo.ClassResolver;
 import org.apache.tinkerpop.shaded.kryo.KryoSerializable;
 import org.apache.tinkerpop.shaded.kryo.serializers.JavaSerializer;
-import org.javatuples.Pair;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter;
 import org.apache.commons.collections.map.ReferenceMap;
-import java.util.Stack;
+import org.javatuples.Pair;
+import org.javatuples.Triplet;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -154,6 +154,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
+import java.util.Stack;
 import java.util.TimeZone;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -356,6 +357,7 @@ public enum GryoVersion {
             add(GryoTypeReg.of(MapReduce.NullObject.class, 74));
             add(GryoTypeReg.of(AtomicLong.class, 79));
             add(GryoTypeReg.of(Pair.class, 88, new 
UtilSerializers.PairSerializer()));
+            add(GryoTypeReg.of(Triplet.class, 183, new 
UtilSerializers.TripletSerializer()));                 // ***LAST ID***
             add(GryoTypeReg.of(TraversalExplanation.class, 106, new 
JavaSerializer()));
 
             add(GryoTypeReg.of(Duration.class, 93, new 
JavaTimeSerializers.DurationSerializer()));
@@ -393,8 +395,7 @@ public enum GryoVersion {
             add(GryoTypeReg.of(LP_NL_O_OB_P_S_SE_SL_Traverser.class, 179));
             add(GryoTypeReg.of(LabelledCounter.class, 180));
             add(GryoTypeReg.of(Stack.class, 181));
-            add(GryoTypeReg.of(ReferenceMap.class, 182));                      
  // ***LAST ID***
-
+            add(GryoTypeReg.of(ReferenceMap.class, 182));
 
             // placeholder serializers for classes that don't live here in 
core. this will allow them to be used if
             // present  or ignored if the class isn't available. either way 
the registration numbers are held as
@@ -520,6 +521,7 @@ public enum GryoVersion {
             add(GryoTypeReg.of(MapReduce.NullObject.class, 74));
             add(GryoTypeReg.of(AtomicLong.class, 79));
             add(GryoTypeReg.of(Pair.class, 88, new 
UtilSerializers.PairSerializer()));
+            add(GryoTypeReg.of(Triplet.class, 183, new 
UtilSerializers.TripletSerializer()));                 // ***LAST ID***
             add(GryoTypeReg.of(TraversalExplanation.class, 106, new 
JavaSerializer()));
 
             add(GryoTypeReg.of(Duration.class, 93, new 
JavaTimeSerializers.DurationSerializer()));
@@ -583,7 +585,7 @@ public enum GryoVersion {
             add(GryoTypeReg.of(LP_NL_O_OB_P_S_SE_SL_Traverser.class, 179));
             add(GryoTypeReg.of(LabelledCounter.class, 180));
             add(GryoTypeReg.of(Stack.class, 181));
-            add(GryoTypeReg.of(ReferenceMap.class, 182));                      
  // ***LAST ID***
+            add(GryoTypeReg.of(ReferenceMap.class, 182));
         }};
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java
index 5182e6c..aff6059 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java
@@ -28,6 +28,7 @@ import org.apache.tinkerpop.shaded.kryo.Serializer;
 import org.apache.tinkerpop.shaded.kryo.io.Input;
 import org.apache.tinkerpop.shaded.kryo.io.Output;
 import org.javatuples.Pair;
+import org.javatuples.Triplet;
 
 import java.net.InetAddress;
 import java.net.URI;
@@ -216,6 +217,20 @@ final class UtilSerializers {
         }
     }
 
+    static final class TripletSerializer implements SerializerShim<Triplet> {
+        @Override
+        public <O extends OutputShim> void write(final KryoShim<?, O> kryo, 
final O output, final Triplet triplet) {
+            kryo.writeClassAndObject(output, triplet.getValue0());
+            kryo.writeClassAndObject(output, triplet.getValue1());
+            kryo.writeClassAndObject(output, triplet.getValue2());
+        }
+
+        @Override
+        public <I extends InputShim> Triplet read(final KryoShim<I, ?> kryo, 
final I input, final Class<Triplet> tripletClass) {
+            return Triplet.with(kryo.readClassAndObject(input), 
kryo.readClassAndObject(input), kryo.readClassAndObject(input));
+        }
+    }
+
     static final class EntrySerializer extends Serializer<Map.Entry> {
         @Override
         public void write(final Kryo kryo, final Output output, final 
Map.Entry entry) {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java
index fa999aa..3cd76f2 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.structure.Property;
 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.empty.EmptyGraph;
 
 import java.util.Iterator;
 import java.util.Optional;
@@ -70,7 +71,7 @@ public interface Attachable<V> {
      */
     public static class Method {
         public static <V> Function<Attachable<V>, V> get(final Host 
hostVertexOrGraph) {
-            return (Attachable<V> attachable) -> {
+            return hostVertexOrGraph instanceof EmptyGraph ? Attachable::get : 
(Attachable<V> attachable) -> {
                 final Object base = attachable.get();
                 if (base instanceof Vertex) {
                     final Optional<Vertex> optional = hostVertexOrGraph 
instanceof Graph ?

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/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 3d9a549..d1da36d 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
@@ -43,7 +43,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", "read", "write", "with", "option", 
"iterate", "to", "from", "profile", "pageRank", "peerPressure", "program", 
"none"));
+    private static Set<String> NO_GRAPH = new 
HashSet<>(Arrays.asList("asAdmin", "by", "read", "write", "with", "option", 
"iterate", "to", "from", "profile", "pageRank", "peerPressure", "shortestPath", 
"program", "none"));
     private static Set<String> NO_ANONYMOUS = new 
HashSet<>(Arrays.asList("start", "__"));
     private static Set<String> IGNORES_BYTECODE = new 
HashSet<>(Arrays.asList("asAdmin", "read", "write", "iterate", "mapValues", 
"mapKeys"));
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/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 361b246..aba8a7f 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -1386,6 +1386,15 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Adds the shortestPath step to this <see 
cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, Path> ShortestPath ()
+        {
+            Bytecode.AddStep("shortestPath");
+            return Wrap<S, Path>(this);
+        }
+
+        /// <summary>
         ///     Adds the sideEffect step to this <see 
cref="GraphTraversal{SType, EType}" />.
         /// </summary>
         public GraphTraversal<S, E> SideEffect (IConsumer consumer)
@@ -1722,4 +1731,4 @@ namespace Gremlin.Net.Process.Traversal
         }
 
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/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 4f39fa5..6479515 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
@@ -953,6 +953,16 @@ class GraphTraversal extends Traversal {
   }
   
   /**
+   * Graph traversal shortestPath method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  shortestPath(...args) {
+    this.bytecode.addStep('shortestPath', args);
+    return this;
+  }
+  
+  /**
    * Graph traversal sideEffect method.
    * @param {...Object} args
    * @returns {GraphTraversal}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/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 6d56c5c..cffb5eb 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
@@ -434,6 +434,10 @@ class GraphTraversal(Traversal):
         self.bytecode.add_step("select", *args)
         return self
 
+    def shortestPath(self, *args):
+        self.bytecode.add_step("shortestPath", *args)
+        return self
+
     def sideEffect(self, *args):
         self.bytecode.add_step("sideEffect", *args)
         return self

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java
----------------------------------------------------------------------
diff --git 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java
 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java
index 7ca44ba..6a2b700 100644
--- 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java
+++ 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java
@@ -180,6 +180,10 @@ public abstract class AbstractGremlinTest {
         return convertToVertex(graph, vertexName).id();
     }
 
+    public Vertex convertToVertex(final String vertexName) {
+        return convertToVertex(graph, vertexName);
+    }
+
     public Vertex convertToVertex(final Graph graph, final String vertexName) {
         // all test graphs have "name" as a unique id which makes it easy to 
hardcode this...works for now
         return graph.traversal().V().has("name", vertexName).next();
@@ -249,7 +253,7 @@ public abstract class AbstractGremlinTest {
     public void printTraversalForm(final Traversal traversal) {
         logger.info(String.format("Testing: %s", name.getMethodName()));
         logger.info("   pre-strategy:" + traversal);
-        traversal.hasNext();
+        if (!traversal.asAdmin().isLocked()) 
traversal.asAdmin().applyStrategies();
         logger.info("  post-strategy:" + traversal);
         verifyUniqueStepIds(traversal.asAdmin());
     }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e1ba81b4/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java
----------------------------------------------------------------------
diff --git 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java
 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java
index 2861724..e15fff8 100644
--- 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java
+++ 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java
@@ -119,7 +119,7 @@ public abstract class AbstractGremlinProcessTest extends 
AbstractGremlinTest {
 
     public static <T> void checkResults(final List<T> expectedResults, final 
Traversal<?, T> traversal) {
         final List<T> results = traversal.toList();
-        assertFalse(traversal.hasNext());
+        assertThat(traversal.hasNext(), is(false));
         if (expectedResults.size() != results.size()) {
             logger.error("Expected results: " + expectedResults);
             logger.error("Actual results:   " + results);
@@ -128,20 +128,19 @@ public abstract class AbstractGremlinProcessTest extends 
AbstractGremlinTest {
 
         for (T t : results) {
             if (t instanceof Map) {
-                assertThat("Checking map result existence: " + t, 
expectedResults.stream().filter(e -> e instanceof Map).filter(e -> 
internalCheckMap((Map) e, (Map) t)).findAny().isPresent(), is(true));
+                assertThat("Checking map result existence: " + t, 
expectedResults.stream().anyMatch(e -> e instanceof Map && 
internalCheckMap((Map) e, (Map) t)), is(true));
             } else if (t instanceof List) {
-                assertThat("Checking list result existence: " + t, 
expectedResults.stream().filter(e -> e instanceof List).filter(e -> 
internalCheckList((List) e, (List) t)).findAny().isPresent(), is(true));
+                assertThat("Checking list result existence: " + t, 
expectedResults.stream().anyMatch(e -> e instanceof List && 
internalCheckList((List) e, (List) t)), is(true));
             } else {
                 assertThat("Checking result existence: " + t, 
expectedResults.contains(t), is(true));
             }
         }
         final Map<T, Long> expectedResultsCount = new HashMap<>();
         final Map<T, Long> resultsCount = new HashMap<>();
+        expectedResults.forEach(t -> MapHelper.incr(expectedResultsCount, t, 
1L));
+        results.forEach(t -> MapHelper.incr(resultsCount, t, 1L));
         assertEquals("Checking indexing is equivalent", 
expectedResultsCount.size(), resultsCount.size());
-        expectedResults.forEach(t -> MapHelper.incr(expectedResultsCount, t, 
1l));
-        results.forEach(t -> MapHelper.incr(resultsCount, t, 1l));
         expectedResultsCount.forEach((k, v) -> assertEquals("Checking result 
group counts", v, resultsCount.get(k)));
-        assertThat(traversal.hasNext(), is(false));
     }
 
     public static <T> void checkResults(final Map<T, Long> expectedResults, 
final Traversal<?, T> traversal) {

Reply via email to