You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by dk...@apache.org on 2018/12/04 22:00:44 UTC

[tinkerpop] 01/01: TINKERPOP-1849 Provide a way to fold() with an index

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

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

commit e12a9b0d3935606f6eafce90bbb2e5d1e90892eb
Author: Daniel Kuppitz <da...@hotmail.com>
AuthorDate: Tue Dec 4 15:00:15 2018 -0700

    TINKERPOP-1849 Provide a way to fold() with an index
---
 docs/src/reference/the-traversal.asciidoc          |  36 ++++++
 .../traversal/dsl/graph/GraphTraversal.java        |  23 ++++
 .../gremlin/process/traversal/dsl/graph/__.java    |   6 +
 .../process/traversal/step/map/IndexStep.java      | 118 +++++++++++++++++++
 .../process/traversal/step/util/WithOptions.java   |  19 +++
 .../process/traversal/step/map/IndexStepTest.java  |  49 ++++++++
 .../Process/Traversal/GraphTraversal.cs            |   9 ++
 .../Gremlin.Net/Process/Traversal/WithOptions.cs   |   9 ++
 .../src/Gremlin.Net/Process/Traversal/__.cs        |   8 ++
 .../lib/process/graph-traversal.js                 |  11 ++
 .../gremlin-javascript/lib/process/traversal.js    |   5 +-
 .../gremlin_python/process/graph_traversal.py      |  12 ++
 .../jython/gremlin_python/process/traversal.py     |   6 +
 gremlin-test/features/map/Index.feature            |  65 +++++++++++
 .../gremlin/process/ProcessStandardSuite.java      |   4 +
 .../process/traversal/step/map/IndexTest.java      | 129 +++++++++++++++++++++
 .../tinkergraph/structure/TinkerGraphPlayTest.java |  32 +----
 17 files changed, 514 insertions(+), 27 deletions(-)

diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 1e43e9f..1f2cc24 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -1084,6 +1084,42 @@ g.V().identity()
 
 link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#identity--++[`identity()`]
 
+[[index-step]]
+=== Index Step
+
+The `index()`-step (*map*) indexes each element in the current collection. If the current traverser's value is not a collection, then it's treated as a single-item collection. There two indexers
+available, which can be chosen using the `with()` modulator. The list indexer (default) creates a list for each collection item, with the first item being the original element and the second element
+being the index. The map indexer created a linked hash map in which the index represents the key and the original item is used as the value.
+
+[gremlin.groovy,modern]
+----
+g.V().hasLabel("software").values("name").fold().
+  order(Scope.local).
+  index().
+  unfold().
+  order().
+    by(__.tail(Scope.local, 1))  <1>
+g.V().hasLabel("software").values("name").fold().
+  order(Scope.local).
+  index().
+    with(WithOptions.indexer, WithOptions.list).
+  unfold().
+  order().
+    by(__.tail(Scope.local, 1))  <2>
+g.V().hasLabel("person").values("name").fold().
+  order(Scope.local).
+  index().
+    with(WithOptions.indexer, WithOptions.map)
+----
+
+<1> Index all software names in their alphabetical order.
+<2> Same as statement 1, but with an explicitely specified list indexer.
+<3> Index all person names in their alphabetical order and store the result in an ordered map.
+
+*Additional References*
+
+link:++http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#index--++[`index()`]
+
 [[inject-step]]
 === Inject Step
 
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 1ef0be7..372710d 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
@@ -85,6 +85,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupCountStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.IdStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.IndexStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.LabelStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaCollectingBarrierStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.LambdaFlatMapStep;
@@ -2561,6 +2562,27 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
+     * Indexes all items of the current collection. The indexing format can be configured using the {@link GraphTraversal#with(String, Object)}
+     * and {@link org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions#indexer}.
+     *
+     * Indexed as list: ["a","b","c"] => [["a",0],["b",1],["c",2]]
+     * Indexed as map:  ["a","b","c"] => {0:"a",1:"b",2:"c"}
+     *
+     * If the current object is not a collection, this step will map the object to a single item collection/map:
+     *
+     * Indexed as list: "a" => ["a",0]
+     * Indexed as map:  "a" => {0:"a"}
+     *
+     * @return the traversal with an appended {@link IndexStep}
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#index-step" target="_blank">Reference Documentation - Index Step</a>
+     * @since 3.4.0
+     */
+    public default <E2> GraphTraversal<S, E2> index() {
+        this.asAdmin().getBytecode().addStep(Symbols.index);
+        return this.asAdmin().addStep(new IndexStep<>(this.asAdmin()));
+    }
+
+    /**
      * Turns the lazy traversal pipeline into a bulk-synchronous pipeline which basically iterates that traversal to
      * the size of the barrier. In this case, it iterates the entire thing as the default barrier size is set to
      * {@code Integer.MAX_VALUE}.
@@ -2934,6 +2956,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         public static final String aggregate = "aggregate";
         public static final String subgraph = "subgraph";
         public static final String barrier = "barrier";
+        public static final String index = "index";
         public static final String local = "local";
         public static final String emit = "emit";
         public static final String repeat = "repeat";
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
index c0f9cad..f991e80 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
@@ -1125,4 +1125,10 @@ public class __ {
         return __.<A>start().barrier(barrierConsumer);
     }
 
+    /**
+     * @see GraphTraversal#index()
+     */
+    public static <A, B> GraphTraversal<A, B> index() {
+        return __.<A>start().index();
+    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexStep.java
new file mode 100644
index 0000000..b903c15
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexStep.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+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 IndexStep<S, E> extends MapStep<S, E> implements TraversalParent, Configuring {
+
+    private final static IllegalArgumentException INVALID_CONFIGURATION_EXCEPTION =
+            new IllegalArgumentException("WithOptions.indexer requires a single Integer argument (possible " + "" +
+                    "values are: WithOptions.[list|map])");
+
+    private final Parameters parameters = new Parameters();
+
+    private Function<Iterator, Object> indexer;
+
+    public IndexStep(final Traversal.Admin traversal) {
+        super(traversal);
+        this.configure(WithOptions.indexer, WithOptions.list);
+    }
+
+    @Override
+    protected E map(final Traverser.Admin<S> traverser) {
+        //noinspection unchecked
+        return (E) indexer.apply(IteratorUtils.asIterator(traverser.get()));
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() ^ this.indexer.hashCode();
+    }
+
+    private static List<List<Object>> indexedList(final Iterator iterator) {
+        final List<List<Object>> list = new ArrayList<>();
+        int i = 0;
+        while (iterator.hasNext()) {
+            list.add(Arrays.asList(iterator.next(), i++));
+        }
+        return Collections.unmodifiableList(list);
+    }
+
+    private static Map<Integer, Object> indexedMap(final Iterator iterator) {
+        final Map<Integer, Object> map = new LinkedHashMap<>();
+        int i = 0;
+        while (iterator.hasNext()) {
+            map.put(i++, iterator.next());
+        }
+        return Collections.unmodifiableMap(map);
+    }
+
+    @Override
+    public Set<TraverserRequirement> getRequirements() {
+        return Collections.singleton(TraverserRequirement.OBJECT);
+    }
+
+    @Override
+    public void configure(final Object... keyValues) {
+        final Integer indexer;
+        if (keyValues[0].equals(WithOptions.indexer)) {
+            if (keyValues.length == 2 && keyValues[1] instanceof Integer) {
+                indexer = (Integer) keyValues[1];
+            } else {
+                throw INVALID_CONFIGURATION_EXCEPTION;
+            }
+            if (indexer == WithOptions.list) {
+                this.indexer = IndexStep::indexedList;
+            } else if (indexer == WithOptions.map) {
+                this.indexer = IndexStep::indexedMap;
+            } else {
+                throw INVALID_CONFIGURATION_EXCEPTION;
+            }
+        } else {
+            this.parameters.set(this, keyValues);
+        }
+    }
+
+    @Override
+    public Parameters getParameters() {
+        return parameters;
+    }
+}
+
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/WithOptions.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/WithOptions.java
index 3faa95d..e8e695f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/WithOptions.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/WithOptions.java
@@ -67,4 +67,23 @@ public class WithOptions {
      * Include all tokens.
      */
     public static int all = ids | labels | keys | values;
+
+    //
+    // IndexStep
+    //
+
+    /**
+     * Configures the indexer to be used in {@link org.apache.tinkerpop.gremlin.process.traversal.step.map.IndexStep}.
+     */
+    public static final String indexer = Graph.Hidden.hide("tinkerpop.index.indexer");
+
+    /**
+     * Index items using 2-item lists.
+     */
+    public static int list = 0;
+
+    /**
+     * Index items using a {@link java.util.LinkedHashMap}.
+     */
+    public static int map = 1;
 }
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexStepTest.java
new file mode 100644
index 0000000..a3b4071
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexStepTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+public class IndexStepTest extends StepTest {
+
+    @Override
+    protected List<Traversal> getTraversals() {
+        return Arrays.asList(
+                __.index(),
+                __.index().with(WithOptions.indexer, WithOptions.map)
+        );
+    }
+
+    @Test
+    public void testDefault() {
+        assertEquals(__.index(), __.index().with(WithOptions.indexer, WithOptions.list));
+    }
+}
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index 5437c39..e1cfd64 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -841,6 +841,15 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Adds the index step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, E2> Index<E2> ()
+        {
+            Bytecode.AddStep("index");
+            return Wrap<S, E2>(this);
+        }
+
+        /// <summary>
         ///     Adds the inject step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
         public GraphTraversal<S, E> Inject (params E[] injections)
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/WithOptions.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/WithOptions.cs
index 8b79089..c20c59a 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/WithOptions.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/WithOptions.cs
@@ -59,6 +59,15 @@ namespace Gremlin.Net.Process.Traversal.Step.Util
 
         public static readonly int All = 15;
 
+
+        public static readonly string Indexer = "~tinkerpop.index.indexer";
+
+
+        public static readonly int List = 0;
+
+
+        public static readonly int Map = 1;
+
     }
 
 #pragma warning restore 1591
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
index 3681ce6..daea514 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
@@ -623,6 +623,14 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the index step to that traversal.
+        /// </summary>
+        public static GraphTraversal<object, E2> Index<E2>()
+        {
+            return new GraphTraversal<object, E2>().Index<E2>();            
+        }
+
+        /// <summary>
         ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the inject step to that traversal.
         /// </summary>
         public static GraphTraversal<object, object> Inject(params object[] injections)
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 bcb37fa..ec554e6 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
@@ -603,6 +603,16 @@ class GraphTraversal extends Traversal {
   }
   
   /**
+   * Graph traversal index method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  index(...args) {
+    this.bytecode.addStep('index', args);
+    return this;
+  }
+  
+  /**
    * Graph traversal inject method.
    * @param {...Object} args
    * @returns {GraphTraversal}
@@ -1251,6 +1261,7 @@ const statics = {
   in_: (...args) => callOnEmptyTraversal('in_', args),
   inE: (...args) => callOnEmptyTraversal('inE', args),
   inV: (...args) => callOnEmptyTraversal('inV', args),
+  index: (...args) => callOnEmptyTraversal('index', args),
   inject: (...args) => callOnEmptyTraversal('inject', args),
   is: (...args) => callOnEmptyTraversal('is', args),
   key: (...args) => callOnEmptyTraversal('key', args),
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js
index 4517689..2173ade 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js
@@ -396,7 +396,10 @@ const withOptions = {
   labels: 2,
   keys: 4,
   values: 8,
-  all: 15
+  all: 15,
+  indexer: "~tinkerpop.index.indexer",
+  list: 0,
+  map: 1
 };
 
 function toEnum(typeName, keys) {
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 7d7a4c9..b71c9e1 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
@@ -295,6 +295,10 @@ class GraphTraversal(Traversal):
         self.bytecode.add_step("in", *args)
         return self
 
+    def index(self, *args):
+        self.bytecode.add_step("index", *args)
+        return self
+
     def inject(self, *args):
         self.bytecode.add_step("inject", *args)
         return self
@@ -695,6 +699,10 @@ class __(object):
         return cls.graph_traversal(None, None, Bytecode()).in_(*args)
 
     @classmethod
+    def index(cls, *args):
+        return cls.graph_traversal(None, None, Bytecode()).index(*args)
+
+    @classmethod
     def inject(cls, *args):
         return cls.graph_traversal(None, None, Bytecode()).inject(*args)
 
@@ -1048,6 +1056,10 @@ def in_(*args):
     return __.in_(*args)
 statics.add_static('in_', in_)
 
+def index(*args):
+    return __.index(*args)
+statics.add_static('index', index)
+
 def inject(*args):
     return __.inject(*args)
 statics.add_static('inject', inject)
diff --git a/gremlin-python/src/main/jython/gremlin_python/process/traversal.py b/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
index e92d0cb..0825fb9 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
@@ -637,3 +637,9 @@ class WithOptions(object):
 
     all = 15
 
+    indexer = "~tinkerpop.index.indexer"
+
+    list = 0
+
+    map = 1
+
diff --git a/gremlin-test/features/map/Index.feature b/gremlin-test/features/map/Index.feature
new file mode 100644
index 0000000..04c190b
--- /dev/null
+++ b/gremlin-test/features/map/Index.feature
@@ -0,0 +1,65 @@
+# 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.
+
+Feature: Step - index()
+
+  Scenario: g_V_hasLabelXsoftwareX_index_unfold
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().hasLabel("software").index().unfold()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | l[v[lop],d[0].i] |
+      | l[v[ripple],d[0].i] |
+
+  Scenario: g_V_hasLabelXsoftwareX_order_byXnameX_index_withXmapX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().hasLabel("software").order().by("name").index().with(WithOptions.indexer, WithOptions.map)
+      """
+    When iterated to list
+    Then the result should be ordered
+      | result |
+      | m[{"d[0].i": "v[lop]"}] |
+      | m[{"d[0].i": "v[ripple]"}] |
+
+  Scenario: g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().hasLabel("software").values("name").fold().order(Scope.local).index().unfold().order().by(__.tail(Scope.local, 1))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | l[lop,d[0].i] |
+      | l[ripple,d[1].i] |
+
+  Scenario: g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().hasLabel("person").values("name").fold().order(Scope.local).index().with(WithOptions.indexer, WithOptions.map)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"d[0].i": "josh", "d[1].i": "marko", "d[2].i": "peter", "d[3].i": "vadas"}] |
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
index f3eb669..3ca0644 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
@@ -51,6 +51,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.FlatMapTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.FoldTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.IndexTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.LoopsTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MapTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchTest;
@@ -141,7 +142,9 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             FlatMapTest.Traversals.class,
             FoldTest.Traversals.class,
             GraphTest.Traversals.class,
+            IndexTest.Traversals.class,
             LoopsTest.Traversals.class,
+            IndexTest.Traversals.class,
             MapTest.Traversals.class,
             MatchTest.CountMatchTraversals.class,
             MatchTest.GreedyMatchTraversals.class,
@@ -231,6 +234,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             FlatMapTest.class,
             FoldTest.class,
             LoopsTest.class,
+            IndexTest.class,
             MapTest.class,
             MatchTest.class,
             MathTest.class,
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexTest.java
new file mode 100644
index 0000000..36ef074
--- /dev/null
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IndexTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.AbstractGremlinProcessTest;
+import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner;
+import org.apache.tinkerpop.gremlin.process.traversal.Scope;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
+import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.tail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+@RunWith(GremlinProcessRunner.class)
+public abstract class IndexTest extends AbstractGremlinProcessTest {
+
+    public abstract Traversal<Vertex, List<Object>> get_g_V_hasLabelXsoftwareX_index_unfold();
+
+    public abstract Traversal<Vertex, Map<Integer, Vertex>> get_g_V_hasLabelXsoftwareX_order_byXnameX_index_withXmapX();
+
+    public abstract Traversal<Vertex, List<Object>> get_g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX();
+
+    public abstract Traversal<Vertex, Map<Integer, String>> get_g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX();
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_hasLabelXsoftwareX_index() {
+        final Traversal<Vertex, List<Object>> traversal = get_g_V_hasLabelXsoftwareX_index_unfold();
+        printTraversalForm(traversal);
+        checkResults(Arrays.asList(
+                Arrays.asList(convertToVertex("lop"), 0),
+                Arrays.asList(convertToVertex("ripple"), 0)
+        ), traversal);
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_hasLabelXsoftwareX_order_byXnameX_index_withXmapX() {
+        final Traversal<Vertex, Map<Integer, Vertex>> traversal = get_g_V_hasLabelXsoftwareX_order_byXnameX_index_withXmapX();
+        printTraversalForm(traversal);
+        final Map<Integer, Vertex> map1 = new LinkedHashMap<>();
+        final Map<Integer, Vertex> map2 = new LinkedHashMap<>();
+        map1.put(0, convertToVertex("lop"));
+        map2.put(0, convertToVertex("ripple"));
+        checkOrderedResults(Arrays.asList(map1, map2), traversal);
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX() {
+        final Traversal<Vertex, List<Object>> traversal = get_g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX();
+        printTraversalForm(traversal);
+        checkOrderedResults(Arrays.asList(
+                Arrays.asList("lop", 0),
+                Arrays.asList("ripple", 1)), traversal);
+        assertFalse(traversal.hasNext());
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX() {
+        final Traversal<Vertex, Map<Integer, String>> traversal = get_g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX();
+        printTraversalForm(traversal);
+        final Map<Integer, String> expected = new LinkedHashMap<>();
+        expected.put(0, "josh");
+        expected.put(1, "marko");
+        expected.put(2, "peter");
+        expected.put(3, "vadas");
+        assertEquals(expected, traversal.next());
+        assertFalse(traversal.hasNext());
+    }
+
+    public static class Traversals extends IndexTest {
+
+        @Override
+        public Traversal<Vertex, List<Object>> get_g_V_hasLabelXsoftwareX_index_unfold() {
+            return g.V().hasLabel("software").index().unfold();
+        }
+
+        @Override
+        public Traversal<Vertex, Map<Integer, Vertex>> get_g_V_hasLabelXsoftwareX_order_byXnameX_index_withXmapX() {
+            return g.V().hasLabel("software").order().by("name")
+                    .<Map<Integer, Vertex>> index().with(WithOptions.indexer, WithOptions.map);
+        }
+
+        @Override
+        public Traversal<Vertex, List<Object>> get_g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX() {
+            return g.V().hasLabel("software").values("name").fold().order(Scope.local).index()
+                    .<List<Object>> unfold().order().by(tail(Scope.local, 1));
+        }
+
+        @Override
+        public Traversal<Vertex, Map<Integer, String>> get_g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX() {
+            return g.V().hasLabel("person").values("name").fold().order(Scope.local)
+                    .<Map<Integer, String>> index().with(WithOptions.indexer, WithOptions.map);
+        }
+    }
+}
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
index a2019ae..c453a38 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
@@ -23,6 +23,7 @@ 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.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy;
 import org.apache.tinkerpop.gremlin.structure.*;
@@ -118,33 +119,12 @@ public class TinkerGraphPlayTest {
     @Test
     @Ignore
     public void testPlayDK() throws Exception {
-        TinkerGraph graph = TinkerFactory.createTheCrew();
-        GraphTraversalSource g = graph.traversal().withStrategies(
-                SubgraphStrategy.build().vertexProperties(hasNot("endTime")).create());
-
-        System.out.println("\n--- valueMap().by(unfold()) ---");
-        g.V().valueMap().forEachRemaining(System.out::println);
-
-        System.out.println("\n--- valueMap().by(unfold()) ---");
-        g.V().valueMap().by(unfold()).forEachRemaining(System.out::println);
-
-        System.out.println("\n--- valueMap().by(unfold()).with(tokens) ---");
-        g.V().valueMap().by(unfold()).with(tokens).forEachRemaining(System.out::println);
-
-        System.out.println("\n--- valueMap().by(unfold()).with(tokens, false) ---");
-        g.V().valueMap().by(unfold()).with(tokens, false).forEachRemaining(System.out::println);
-
-        System.out.println("\n--- valueMap().by(unfold()).with(tokens, ids) ---");
-        g.V().valueMap().by(unfold()).with(tokens, ids).forEachRemaining(System.out::println);
-
-        System.out.println("\n--- valueMap().by(unfold()).with(tokens, labels) ---");
-        g.V().valueMap().by(unfold()).with(tokens, labels).forEachRemaining(System.out::println);
-
-        System.out.println("\n--- valueMap().by(unfold()).with(tokens, all) ---");
-        g.V().valueMap().by(unfold()).with(tokens, all).forEachRemaining(System.out::println);
+        GraphTraversalSource g = TinkerFactory.createModern().traversal();
 
-        System.out.println("\n--- valueMap().by(unfold()).with(tokens, ids | labels) ---");
-        g.V().valueMap().by(unfold()).with(tokens, ids | labels).forEachRemaining(System.out::println);
+        g.V().index().forEachRemaining(System.out::println);
+        g.V().fold().index().forEachRemaining(System.out::println);
+        g.V().index().with(WithOptions.indexer, WithOptions.list).forEachRemaining(System.out::println);
+        g.V().fold().index().with(WithOptions.indexer, WithOptions.map).forEachRemaining(System.out::println);
     }
 
     @Test