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/05 15:07:31 UTC

[tinkerpop] branch TINKERPOP-1849 updated (fae3229 -> 4ed58da)

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

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


 discard fae3229  TINKERPOP-1849 Provide a way to fold() with an index
     new 4ed58da  TINKERPOP-1849 Provide a way to fold() with an index

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

 * -- * -- B -- O -- O -- O   (fae3229)
            \
             N -- N -- N   refs/heads/TINKERPOP-1849 (4ed58da)

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

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

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


Summary of changes:
 .../java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java  | 1 -
 1 file changed, 1 deletion(-)


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

Posted by dk...@apache.org.
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 4ed58dab235aa962e13ad24cdb29bb24fc058b4f
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
---
 CHANGELOG.asciidoc                                 |   1 +
 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      |   3 +
 .../process/traversal/step/map/IndexTest.java      | 129 +++++++++++++++++++++
 .../tinkergraph/structure/TinkerGraphPlayTest.java |  32 +----
 18 files changed, 514 insertions(+), 27 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index a300af3..d9e849b 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 `IndexStep` which allows to transform local collections into indexed collections or maps.
 * Made `valueMap()` aware of `by` and `with` modulators and deprecated `valueMap(boolean)` overloads.
 * Use `Compare.eq` in `Contains` predicates to ensure the same filter behavior for numeric values.
 * Added `OptionsStrategy` to allow traversals to take arbitrary traversal-wide configurations.
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 1e43e9f..3f55a33 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)  <3>
+----
+
+<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..cb181cd 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;
@@ -142,6 +143,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             FoldTest.Traversals.class,
             GraphTest.Traversals.class,
             LoopsTest.Traversals.class,
+            IndexTest.Traversals.class,
             MapTest.Traversals.class,
             MatchTest.CountMatchTraversals.class,
             MatchTest.GreedyMatchTraversals.class,
@@ -231,6 +233,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