You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2019/09/09 13:15:00 UTC

[tinkerpop] 01/01: TINKERPOP-2284 wip

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

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

commit 051ae70860e585df2ee68ba27e5b63e6a9f97817
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Mon Sep 9 09:14:37 2019 -0400

    TINKERPOP-2284 wip
---
 CHANGELOG.asciidoc                                 |   3 +
 .../traversal/dsl/graph/GraphTraversal.java        |  27 ++-
 .../gremlin/process/traversal/dsl/graph/__.java    |   7 +
 .../process/traversal/step/map/ElementMapStep.java | 111 +++++++++++++
 .../org/apache/tinkerpop/gremlin/structure/T.java  |   8 +
 .../traversal/step/map/ElementMapStepTest.java     |  42 +++++
 .../Process/Traversal/GraphTraversal.cs            |  11 ++
 .../src/Gremlin.Net/Process/Traversal/__.cs        |  10 ++
 .../Structure/IO/GraphSON/DirectionDeserializer.cs |  36 ++++
 .../Structure/IO/GraphSON/GraphSONReader.cs        |   1 +
 .../Gherkin/CommonSteps.cs                         |  15 +-
 .../Structure/IO/GraphSON/GraphSONReaderTests.cs   |  12 ++
 .../lib/process/graph-traversal.js                 |  11 ++
 .../lib/structure/io/graph-serializer.js           |   1 +
 .../lib/structure/io/type-serializers.js           |   7 +
 .../test/cucumber/feature-steps.js                 |   8 +-
 .../gremlin_python/process/graph_traversal.py      |  14 ++
 .../gremlin_python/structure/io/graphsonV3d0.py    |  16 +-
 .../src/main/jython/radish/feature_steps.py        |   8 +-
 gremlin-test/features/map/ElementMap.feature       |  62 +++++++
 .../tinkerpop/gremlin/AbstractGremlinTest.java     |   4 +-
 .../gremlin/process/ProcessComputerSuite.java      |   3 +
 .../gremlin/process/ProcessStandardSuite.java      |   3 +
 .../process/traversal/step/map/ElementMapTest.java | 183 +++++++++++++++++++++
 24 files changed, 590 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 3d61b53..8ee342e 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -24,6 +24,9 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 === TinkerPop 3.4.4 (Release Date: NOT OFFICIALLY RELEASED YET)
 
 * Provided support for DSLs by way of remote connections through `AnonymousTraversalSource`.
+* Added `elementMap()` step.
+* Allowed for embedded map assertions in GLV tests.
+* Added `Direction` deserialization support in GLVs.
 
 [[release-3-4-3]]
 === TinkerPop 3.4.3 (Release Date: August 5, 2019)
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 ce445c4..5a932c3 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
@@ -80,6 +80,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountLocalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.DedupLocalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeOtherVertexStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeVertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.ElementMapStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.FoldStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupCountStep;
@@ -553,7 +554,26 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
-     * Map the {@link Element} to a {@link Map} of the property values key'd according to their {@link Property#key}.
+     * Map the {@link Element} to a {@code Map} of the property values key'd according to their {@link Property#key}.
+     * If no property keys are provided, then all property values are retrieved. For vertices, the {@code Map} will
+     * be returned with the assumption of single property values along with {@link T#id} and {@link T#label}. Prefer
+     * {@link #valueMap(String...)} if multi-property processing is required. For  edges, keys will include additional
+     * related edge structure of {@link Direction#IN} and {@link Direction#OUT} which themselves are {@code Map}
+     * instances of the particular {@link Vertex} represented by {@link T#id} and {@link T#label}.
+     *
+     * @param propertyKeys the properties to retrieve
+     * @param <E2>         the value type of the returned properties
+     * @return the traversal with an appended {@link ElementMapStep}.
+     * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#elementmap-step" target="_blank">Reference Documentation - ElementMap Step</a>
+     * @since 3.4.4
+     */
+    public default <E2> GraphTraversal<S, Map<Object, E2>> elementMap(final String... propertyKeys) {
+        this.asAdmin().getBytecode().addStep(Symbols.elementMap, propertyKeys);
+        return this.asAdmin().addStep(new ElementMapStep<>(this.asAdmin(), propertyKeys));
+    }
+
+    /**
+     * Map the {@link Element} to a {@code Map} of the property values key'd according to their {@link Property#key}.
      * If no property keys are provided, then all property values are retrieved.
      *
      * @param propertyKeys the properties to retrieve
@@ -568,7 +588,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
     }
 
     /**
-     * Map the {@link Element} to a {@link Map} of the property values key'd according to their {@link Property#key}.
+     * Map the {@link Element} to a {@code Map} of the property values key'd according to their {@link Property#key}.
      * If no property keys are provided, then all property values are retrieved.
      *
      * @param includeTokens whether to include {@link T} tokens in the emitted map.
@@ -578,7 +598,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
      * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#valuemap-step" target="_blank">Reference Documentation - ValueMap Step</a>
      * @since 3.0.0-incubating
      * @deprecated As of release 3.4.0, deprecated in favor of {@link GraphTraversal#valueMap(String...)} in conjunction with
-     *             {@link GraphTraversal#with(String, Object)}.
+     *             {@link GraphTraversal#with(String, Object)} or simple prefer {@link #elementMap(String...)}.
      */
     @Deprecated
     public default <E2> GraphTraversal<S, Map<Object, E2>> valueMap(final boolean includeTokens, final String... propertyKeys) {
@@ -2946,6 +2966,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         public static final String values = "values";
         public static final String propertyMap = "propertyMap";
         public static final String valueMap = "valueMap";
+        public static final String elementMap = "elementMap";
         public static final String select = "select";
         public static final String key = "key";
         public static final String value = "value";
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 ab98930..aab83f4 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
@@ -254,6 +254,13 @@ public class __ {
     }
 
     /**
+     * @see GraphTraversal#elementMap(String...)
+     */
+    public static <A extends Element, B> GraphTraversal<A, Map<Object, B>> elementMap(final String... propertyKeys) {
+        return __.<A>start().elementMap(propertyKeys);
+    }
+
+    /**
      * @see GraphTraversal#valueMap(String...)
      */
     public static <A extends Element, B> GraphTraversal<A, Map<Object, B>> valueMap(final String... propertyKeys) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapStep.java
new file mode 100644
index 0000000..c2a2c76
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapStep.java
@@ -0,0 +1,111 @@
+/*
+ * 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.TraversalParent;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+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.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.StringFactory;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Converts a {@link Element} to a {@code Map}.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ElementMapStep<K,E> extends MapStep<Element, Map<K, E>> implements TraversalParent {
+
+    protected final String[] propertyKeys;
+
+    public ElementMapStep(final Traversal.Admin traversal, final String... propertyKeys) {
+        super(traversal);
+        this.propertyKeys = propertyKeys;
+    }
+
+    @Override
+    protected Map<K, E> map(final Traverser.Admin<Element> traverser) {
+        final Map<Object, Object> map = new LinkedHashMap<>();
+        final Element element = traverser.get();
+        map.put(T.id, element.id());
+        if (element instanceof VertexProperty) {
+            map.put(T.key, ((VertexProperty<?>) element).key());
+            map.put(T.value, ((VertexProperty<?>) element).value());
+        } else {
+            map.put(T.label, element.label());
+        }
+
+        if (element instanceof Edge) {
+            final Edge e = (Edge) element;
+            map.put(Direction.IN, getVertexStructure(e.inVertex()));
+            map.put(Direction.OUT, getVertexStructure(e.outVertex()));
+        }
+
+        final Iterator<? extends Property> properties = element.properties(this.propertyKeys);
+        while (properties.hasNext()) {
+            final Property<?> property = properties.next();
+            map.put(property.key(), property.value());
+        }
+
+        return (Map) map;
+    }
+
+    protected Map<Object, Object> getVertexStructure(final Vertex v) {
+        final Map<Object, Object> m = new LinkedHashMap<>();
+        m.put(T.id, v.id());
+        m.put(T.label, v.label());
+        return m;
+    }
+
+    public String[] getPropertyKeys() {
+        return propertyKeys;
+    }
+
+    public String toString() {
+        return StringFactory.stepString(this, Arrays.asList(this.propertyKeys));
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        for (final String propertyKey : this.propertyKeys) {
+            result ^= propertyKey.hashCode();
+        }
+        return result;
+    }
+
+    @Override
+    public Set<TraverserRequirement> getRequirements() {
+        return this.getSelfAndChildRequirements(TraverserRequirement.OBJECT);
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/T.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/T.java
index a836840..2d67d6b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/T.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/T.java
@@ -30,6 +30,8 @@ import java.util.function.Function;
 public enum T implements Function<Element, Object> {
     /**
      * Label (representing Element.label())
+     *
+     * @since 3.0.0-incubating
      */
     label {
         @Override
@@ -44,6 +46,8 @@ public enum T implements Function<Element, Object> {
     },
     /**
      * Id (representing Element.id())
+     *
+     * @since 3.0.0-incubating
      */
     id {
         @Override
@@ -58,6 +62,8 @@ public enum T implements Function<Element, Object> {
     },
     /**
      * Key (representing Property.key())
+     *
+     * @since 3.0.0-incubating
      */
     key {
         @Override
@@ -72,6 +78,8 @@ public enum T implements Function<Element, Object> {
     },
     /**
      * Value (representing Property.value())
+     *
+     * @since 3.0.0-incubating
      */
     value {
         @Override
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapStepTest.java
new file mode 100644
index 0000000..3bffbe0
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapStepTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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 java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ElementMapStepTest extends StepTest {
+
+    @Override
+    protected List<Traversal> getTraversals() {
+        return Arrays.asList(
+                __.elementMap(),
+                __.elementMap("name"),
+                __.elementMap("age"),
+                __.elementMap("name", "age")
+        );
+    }
+}
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index f22ee8c..f65276b 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -487,6 +487,17 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Adds the elementMap step to this <see cref="GraphTraversal{SType, EType}" />.
+        /// </summary>
+        public GraphTraversal<S, IDictionary<object, E2>> ElementMap<E2> (params string[] propertyKeys)
+        {
+            var args = new List<object>(0 + propertyKeys.Length) {};
+            args.AddRange(propertyKeys);
+            Bytecode.AddStep("elementMap", args.ToArray());
+            return Wrap<S, IDictionary<object, E2>>(this);
+        }
+
+        /// <summary>
         ///     Adds the emit step to this <see cref="GraphTraversal{SType, EType}" />.
         /// </summary>
         public GraphTraversal<S, E> Emit ()
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
index af31d1b..9bd0b14 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs
@@ -331,6 +331,16 @@ namespace Gremlin.Net.Process.Traversal
         }
 
         /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the elementMap step to that traversal.
+        /// </summary>
+        public static GraphTraversal<object, IDictionary<object, E2>> ElementMap<E2>(params string[] propertyKeys)
+        {
+            return propertyKeys.Length == 0
+                ? new GraphTraversal<object, IDictionary<object, E2>>().ElementMap<E2>()
+                : new GraphTraversal<object, IDictionary<object, E2>>().ElementMap<E2>(propertyKeys);            
+        }
+
+        /// <summary>
         ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the emit step to that traversal.
         /// </summary>
         public static GraphTraversal<object, object> Emit()
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/DirectionDeserializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/DirectionDeserializer.cs
new file mode 100644
index 0000000..4027171
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/DirectionDeserializer.cs
@@ -0,0 +1,36 @@
+#region License
+
+/*
+ * 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.
+ */
+
+#endregion
+
+using Gremlin.Net.Process.Traversal;
+using Newtonsoft.Json.Linq;
+
+namespace Gremlin.Net.Structure.IO.GraphSON
+{
+    internal class DirectionDeserializer : IGraphSONDeserializer
+    {
+        public dynamic Objectify(JToken graphsonObject, GraphSONReader reader)
+        {
+            return Direction.GetByValue(graphsonObject.ToString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs
index 5ced99a..63641f0 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs
@@ -44,6 +44,7 @@ namespace Gremlin.Net.Structure.IO.GraphSON
                 {"g:Int64", new Int64Converter()},
                 {"g:Float", new FloatConverter()},
                 {"g:Double", new DoubleConverter()},
+                {"g:Direction", new DirectionDeserializer()},
                 {"g:UUID", new UuidDeserializer()},
                 {"g:Date", new DateDeserializer()},
                 {"g:Timestamp", new DateDeserializer()},
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
index 96851d2..1c6588b 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
@@ -51,6 +51,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
             new Dictionary<string, Func<string, string, object>>
             {
                 {@"d\[([\d.]+)\]\.([ilfdm])", ToNumber},
+                {@"D\[(.+)\]", ToDirection},
                 {@"v\[(.+)\]", ToVertex},
                 {@"v\[(.+)\]\.id", (x, graphName) => ToVertex(x, graphName).Id},
                 {@"v\[(.+)\]\.sid", (x, graphName) => ToVertex(x, graphName).Id.ToString()},
@@ -90,7 +91,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
         [Given("using the parameter (\\w+) defined as \"(.*)\"")]
         public void UsingParameter(string name, string value)
         {
-            var parsedValue = ParseValue(value, _graphName);
+            var parsedValue = ParseValue(value.Replace("\\\"", "\""), _graphName);
             _parameters.Add(name, parsedValue);
         }
 
@@ -244,6 +245,16 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
             return T.GetByValue(enumName);
         }
 
+        private static object ToDirection(string enumName, string graphName)
+        {
+            return Direction.GetByValue(enumName);
+        }
+
+//        private static string toNormalCase(string name)
+//        {
+//            return name.Substring(0, 1) + name.Substring(1).ToLower();
+//        }
+
         private static object ToNumber(string stringNumber, string graphName)
         {
             return NumericParsers[stringNumber[stringNumber.Length - 1]](
@@ -306,8 +317,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
 
         private static object ParseValue(string stringValue, string graphName)
         {
-            // Parser issue: quotes are not normalized
-            stringValue = stringValue.Replace("\\\"", "\"");
             Func<string, string, object> parser = null;
             string extractedValue = null;
             foreach (var kv in Parsers)
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
index 7685b28..3a49143 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
@@ -280,6 +280,18 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON
             Assert.Equal(T.Label, readT);
         }
 
+        [Theory, MemberData(nameof(Versions))]
+        public void ShouldDeserializeDirection(int version)
+        {
+            var serializedValue = "{\"@type\":\"g:Direction\",\"@value\":\"OUT\"}";
+            var reader = CreateStandardGraphSONReader(version);
+
+            var jObject = JObject.Parse(serializedValue);
+            var deserializedValue = reader.ToObject(jObject);
+
+            Assert.Equal(Direction.Out, deserializedValue);
+        }
+
         [Fact]
         public void ShouldDeserializePathFromGraphSON2()
         {
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 157b0af..0fe34d6 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
@@ -443,6 +443,16 @@ class GraphTraversal extends Traversal {
   }
   
   /**
+   * Graph traversal elementMap method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  elementMap(...args) {
+    this.bytecode.addStep('elementMap', args);
+    return this;
+  }
+  
+  /**
    * Graph traversal emit method.
    * @param {...Object} args
    * @returns {GraphTraversal}
@@ -1264,6 +1274,7 @@ const statics = {
   cyclicPath: (...args) => callOnEmptyTraversal('cyclicPath', args),
   dedup: (...args) => callOnEmptyTraversal('dedup', args),
   drop: (...args) => callOnEmptyTraversal('drop', args),
+  elementMap: (...args) => callOnEmptyTraversal('elementMap', args),
   emit: (...args) => callOnEmptyTraversal('emit', args),
   filter: (...args) => callOnEmptyTraversal('filter', args),
   flatMap: (...args) => callOnEmptyTraversal('flatMap', args),
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/graph-serializer.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/graph-serializer.js
index ff14320..7943852 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/graph-serializer.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/graph-serializer.js
@@ -163,6 +163,7 @@ const deserializers = {
   'g:Float':  typeSerializers.NumberSerializer,
   'g:Double': typeSerializers.NumberSerializer,
   'g:Date': typeSerializers.DateSerializer,
+  'g:Direction': typeSerializers.DirectionSerializer,
   'g:Vertex': typeSerializers.VertexSerializer,
   'g:Edge': typeSerializers.EdgeSerializer,
   'g:VertexProperty': typeSerializers.VertexPropertySerializer,
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
index cfd67c9..c581712 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
@@ -363,6 +363,12 @@ class TSerializer extends TypeSerializer {
   }
 }
 
+class DirectionSerializer extends TypeSerializer {
+    deserialize(obj) {
+        return t.direction[obj[valueKey]];
+    }
+}
+
 class ArraySerializer extends TypeSerializer {
   constructor(typeKey) {
     super();
@@ -458,6 +464,7 @@ module.exports = {
   BulkSetSerializer,
   BytecodeSerializer,
   DateSerializer,
+  DirectionSerializer,
   EdgeSerializer,
   EnumSerializer,
   LambdaSerializer,
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
index 02e7b3f..fed551d 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
@@ -34,6 +34,7 @@ const traversal = require('../../lib/process/anonymous-traversal').traversal;
 const Path = graphModule.Path;
 const __ = graphTraversalModule.statics;
 const t = traversalModule.t;
+const direction = traversalModule.direction
 
 // Determines whether the feature maps (m[]), are deserialized as objects (true) or maps (false).
 // Use false for GraphSON3.
@@ -52,7 +53,8 @@ const parsers = [
   [ 's\\[(.*)\\]', toArray ],
   [ 'm\\[(.+)\\]', toMap ],
   [ 'c\\[(.+)\\]', toLambda ],
-  [ 't\\[(.+)\\]', toT ]
+  [ 't\\[(.+)\\]', toT ],
+  [ 'D\\[(.+)\\]', toDirection ]
 ].map(x => [ new RegExp('^' + x[0] + '$'), x[1] ]);
 
 const ignoreReason = {
@@ -276,6 +278,10 @@ function toT(value) {
   return t[value];
 }
 
+function toDirection(value) {
+    return direction[value.toLowerCase()];
+}
+
 function toArray(stringList) {
   if (stringList === '') {
     return new Array(0);
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 5dde030..4be02f8 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
@@ -229,6 +229,10 @@ class GraphTraversal(Traversal):
         self.bytecode.add_step("drop", *args)
         return self
 
+    def elementMap(self, *args):
+        self.bytecode.add_step("elementMap", *args)
+        return self
+
     def emit(self, *args):
         self.bytecode.add_step("emit", *args)
         return self
@@ -664,6 +668,10 @@ class __(object):
         return cls.graph_traversal(None, None, Bytecode()).drop(*args)
 
     @classmethod
+    def elementMap(cls, *args):
+        return cls.graph_traversal(None, None, Bytecode()).elementMap(*args)
+
+    @classmethod
     def emit(cls, *args):
         return cls.graph_traversal(None, None, Bytecode()).emit(*args)
 
@@ -1046,6 +1054,10 @@ def drop(*args):
     return __.drop(*args)
 
 
+def elementMap(*args):
+    return __.elementMap(*args)
+
+
 def emit(*args):
     return __.emit(*args)
 
@@ -1388,6 +1400,8 @@ statics.add_static('dedup', dedup)
 
 statics.add_static('drop', drop)
 
+statics.add_static('elementMap', elementMap)
+
 statics.add_static('emit', emit)
 
 statics.add_static('filter_', filter_)
diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
index 8c3cd9f..bccb982 100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
@@ -34,7 +34,7 @@ from isodate import parse_duration, duration_isoformat
 
 from gremlin_python import statics
 from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, TypeType, DictType, ListType, SetType, SingleByte, ByteBufferType, SingleChar
-from gremlin_python.process.traversal import Binding, Bytecode, P, TextP, Traversal, Traverser, TraversalStrategy, T
+from gremlin_python.process.traversal import Binding, Bytecode, Direction, P, TextP, Traversal, Traverser, TraversalStrategy, T
 from gremlin_python.structure.graph import Edge, Property, Vertex, VertexProperty, Path
 
 log = logging.getLogger(__name__)
@@ -711,6 +711,20 @@ class TDeserializer(_GraphSONTypeIO):
     def objectify(cls, d, reader):
         return T[d]
 
+
+class DirectionIO(_GraphSONTypeIO):
+    graphson_type = "g:Direction"
+    graphson_base_type = "Direction"
+    python_type = Direction
+
+    @classmethod
+    def dictify(cls, d, writer):
+        return GraphSONUtil.typedValue(cls.graphson_base_type, d.name, "g")
+
+    @classmethod
+    def objectify(cls, d, reader):
+        return Direction[d]
+
 class TraversalMetricsDeserializer(_GraphSONTypeIO):
     graphson_type = "g:TraversalMetrics"
 
diff --git a/gremlin-python/src/main/jython/radish/feature_steps.py b/gremlin-python/src/main/jython/radish/feature_steps.py
index ed6c15d..8db6b7b 100644
--- a/gremlin-python/src/main/jython/radish/feature_steps.py
+++ b/gremlin-python/src/main/jython/radish/feature_steps.py
@@ -80,7 +80,7 @@ def add_parameter(step, param_name, param):
     if not hasattr(step.context, "traversal_params"):
         step.context.traversal_params = {}
 
-    step.context.traversal_params[param_name] = _convert(param, step.context)
+    step.context.traversal_params[param_name] = _convert(param.replace('\\"', '"'), step.context)
 
 
 @given("the traversal of")
@@ -172,14 +172,16 @@ def _convert(val, ctx):
     elif isinstance(val, str) and re.match("^e\[.*\]$", val):           # parse edge
         return __find_cached_element(ctx, graph_name, val[2:-1], "e")
     elif isinstance(val, str) and re.match("^m\[.*\]$", val):           # parse json as a map
-        return _convert(json.loads(val[2:-1].replace('\\"', '"')), ctx)
+        return _convert(json.loads(val[2:-1]), ctx)
     elif isinstance(val, str) and re.match("^p\[.*\]$", val):           # parse path
         path_objects = list(map((lambda x: _convert(x, ctx)), val[2:-1].split(",")))
         return Path([set([])], path_objects)
     elif isinstance(val, str) and re.match("^c\[.*\]$", val):           # parse lambda/closure
         return lambda: (val[2:-1], "gremlin-groovy")
-    elif isinstance(val, str) and re.match("^t\[.*\]$", val):         # parse instance of T enum
+    elif isinstance(val, str) and re.match("^t\[.*\]$", val):           # parse instance of T enum
         return T[val[2:-1]]
+    elif isinstance(val, str) and re.match("^D\[.*\]$", val):           # parse instance of Direction enum
+        return Direction[val[2:-1]]
     else:
         return val
 
diff --git a/gremlin-test/features/map/ElementMap.feature b/gremlin-test/features/map/ElementMap.feature
new file mode 100644
index 0000000..95de688
--- /dev/null
+++ b/gremlin-test/features/map/ElementMap.feature
@@ -0,0 +1,62 @@
+# 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 - elementMap()
+
+  Scenario: g_V_elementMap
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().elementMap()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"t[id]": "v[marko].id", "t[label]": "person", "name": "marko", "age": 29}] |
+      | m[{"t[id]": "v[josh].id", "t[label]": "person", "name": "josh", "age": 32}] |
+      | m[{"t[id]": "v[peter].id", "t[label]": "person", "name": "peter", "age": 35}] |
+      | m[{"t[id]": "v[vadas].id", "t[label]": "person", "name": "vadas", "age": 27}] |
+      | m[{"t[id]": "v[lop].id", "t[label]": "software", "name": "lop", "lang": "java"}] |
+      | m[{"t[id]": "v[ripple].id", "t[label]": "software", "name": "ripple", "lang": "java"}] |
+
+  Scenario: g_V_elementMapXname_ageX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().elementMap("name", "age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"t[id]": "v[marko].id", "t[label]": "person", "name": "marko", "age": 29}] |
+      | m[{"t[id]": "v[josh].id", "t[label]": "person", "name": "josh", "age": 32}] |
+      | m[{"t[id]": "v[peter].id", "t[label]": "person", "name": "peter", "age": 35}] |
+      | m[{"t[id]": "v[vadas].id", "t[label]": "person", "name": "vadas", "age": 27}] |
+      | m[{"t[id]": "v[lop].id", "t[label]": "software", "name": "lop"}] |
+      | m[{"t[id]": "v[ripple].id", "t[label]": "software", "name": "ripple"}] |
+
+  Scenario: g_EX11X_elementMap
+    Given the modern graph
+    And using the parameter e11Id defined as "e[josh-created->lop].id"
+    And the traversal of
+    """
+      g.E(e11Id).elementMap()
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | m[{"t[id]": "e[josh-created->lop].id", "t[label]": "created", "weight": "d[0.4].d", "D[OUT]": "m[{\\"t[id]\\": \\"v[josh].id\\", \\"t[label]\\": \\"person\\"}]", "D[IN]": "m[{\\"t[id]\\": \\"v[lop].id\\", \\"t[label]\\": \\"software\\"}]"}] |
\ No newline at end of file
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 650dd38..f069a12 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
@@ -159,8 +159,8 @@ public abstract class AbstractGremlinTest {
                 logger.warn("The {} is not of type ManagedGraphProvider and therefore graph instances may leak between test cases.", graphProvider.getClass());
 
             if (shouldTestIteratorLeak) {
-                long wait = 300;
-                long[] tries = new long[] { 1, 3, 5, 7, 9, 18, 27, 36, 72, 144, 256, 512 };
+                final long wait = 300;
+                final long[] tries = new long[] { 1, 3, 5, 7, 9, 18, 27, 36, 72, 144, 256, 512 };
                 long openItrCount = StoreIteratorCounter.INSTANCE.getOpenIteratorCount();
                 for (int ix = 0; ix < tries.length && openItrCount > 0; ix++) {
                     Thread.sleep(wait * tries[ix]);
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
index 0951b86..3ca5b76 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
@@ -54,6 +54,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.CoalesceTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConnectedComponentTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConstantTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.ElementMapTest;
 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;
@@ -150,6 +151,7 @@ public class ProcessComputerSuite extends AbstractGremlinSuite {
             ConnectedComponentTest.Traversals.class,
             ConstantTest.Traversals.class,
             CountTest.Traversals.class,
+            ElementMapTest.Traversals.class,
             FlatMapTest.Traversals.class,
             FoldTest.Traversals.class,
             GraphTest.Traversals.class,
@@ -249,6 +251,7 @@ public class ProcessComputerSuite extends AbstractGremlinSuite {
             ConstantTest.class,
             CountTest.class,
             FlatMapTest.class,
+            ElementMapTest.class,
             FoldTest.class,
             MatchTest.class,
             MathTest.class,
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 6bd6d5b..f127ca0 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
@@ -48,6 +48,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.CoalesceTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConstantTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.ElementMapTest;
 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;
@@ -140,6 +141,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             CoalesceTest.Traversals.class,
             ConstantTest.Traversals.class,
             CountTest.Traversals.class,
+            ElementMapTest.Traversals.class,
             FlatMapTest.Traversals.class,
             FoldTest.Traversals.class,
             GraphTest.Traversals.class,
@@ -232,6 +234,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             CoalesceTest.class,
             ConstantTest.class,
             CountTest.class,
+            ElementMapTest.class,
             FlatMapTest.class,
             FoldTest.class,
             LoopsTest.class,
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapTest.java
new file mode 100644
index 0000000..d64cbd9
--- /dev/null
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.CREW;
+import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
+import static org.apache.tinkerpop.gremlin.structure.T.id;
+import static org.apache.tinkerpop.gremlin.structure.T.label;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@RunWith(GremlinProcessRunner.class)
+public abstract class ElementMapTest extends AbstractGremlinProcessTest {
+
+    public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_elementMap();
+
+    public abstract Traversal<Vertex, Map<Object, Object>> get_g_V_elementMapXname_ageX();
+
+    public abstract Traversal<Edge, Map<Object, Object>> get_g_EX11X_elementMap(final Object e11Id);
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_elementMap() {
+        final Traversal<Vertex, Map<Object, Object>> traversal = get_g_V_elementMap();
+        printTraversalForm(traversal);
+        int counter = 0;
+        while (traversal.hasNext()) {
+            counter++;
+            final Map<Object, Object> values = traversal.next();
+            final String name = (String) values.get("name");
+            assertEquals(4, values.size());
+            if (name.equals("marko")) {
+                assertEquals(29, values.get("age"));
+                assertEquals("person", values.get(label));
+            } else if (name.equals("josh")) {
+                assertEquals(32, values.get("age"));
+                assertEquals("person", values.get(label));
+            } else if (name.equals("peter")) {
+                assertEquals(35, values.get("age"));
+                assertEquals("person", values.get(label));
+            } else if (name.equals("vadas")) {
+                assertEquals(27, values.get("age"));
+                assertEquals("person", values.get(label));
+            } else if (name.equals("lop")) {
+                assertEquals("java", values.get("lang"));
+                assertEquals("software", values.get(label));
+            } else if (name.equals("ripple")) {
+                assertEquals("java", values.get("lang"));
+                assertEquals("software", values.get(label));
+            } else {
+                throw new IllegalStateException("It is not possible to reach here: " + values);
+            }
+
+            assertThat(values.containsKey(T.id), is(true));
+        }
+        assertEquals(6, counter);
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_elementMapXname_ageX() {
+        final Traversal<Vertex, Map<Object, Object>> traversal = get_g_V_elementMapXname_ageX();
+        printTraversalForm(traversal);
+        int counter = 0;
+        while (traversal.hasNext()) {
+            counter++;
+            final Map<Object, Object> values = traversal.next();
+            final String name = (String) values.get("name");
+            if (name.equals("marko")) {
+                assertEquals(29, values.get("age"));
+                assertEquals("person", values.get(label));
+                assertEquals(4, values.size());
+            } else if (name.equals("josh")) {
+                assertEquals(32, values.get("age"));
+                assertEquals("person", values.get(label));
+                assertEquals(4, values.size());
+            } else if (name.equals("peter")) {
+                assertEquals(35, values.get("age"));
+                assertEquals("person", values.get(label));
+                assertEquals(4, values.size());
+            } else if (name.equals("vadas")) {
+                assertEquals(27, values.get("age"));
+                assertEquals("person", values.get(label));
+                assertEquals(4, values.size());
+            } else if (name.equals("lop")) {
+                assertNull(values.get("lang"));
+                assertEquals("software", values.get(label));
+                assertEquals(3, values.size());
+            } else if (name.equals("ripple")) {
+                assertNull(values.get("lang"));
+                assertEquals("software", values.get(label));
+                assertEquals(3, values.size());
+            } else {
+                throw new IllegalStateException("It is not possible to reach here: " + values);
+            }
+
+            assertThat(values.containsKey(T.id), is(true));
+        }
+        assertEquals(6, counter);
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_EX11X_elementMap() {
+        final Object edgeId = convertToEdgeId("josh", "created", "lop");
+        final Traversal<Edge, Map<Object,Object>> traversal = get_g_EX11X_elementMap(edgeId);
+        printTraversalForm(traversal);
+
+        final Map<Object,Object> m = traversal.next();
+        assertEquals(5, m.size());
+        assertEquals(0.4d, m.get("weight"));
+        assertEquals("created", m.get(label));
+        assertEquals(edgeId, m.get(T.id));
+
+        final Vertex vJosh = convertToVertex("josh");
+        assertEquals(vJosh.id(), ((Map<Object,Object>) m.get(Direction.OUT)).get(T.id));
+        assertEquals(vJosh.label(), ((Map<Object,Object>) m.get(Direction.OUT)).get(T.label));
+
+        final Vertex vLop = convertToVertex("lop");
+        assertEquals(vLop.id(), ((Map<Object,Object>) m.get(Direction.IN)).get(T.id));
+        assertEquals(vLop.label(), ((Map<Object,Object>) m.get(Direction.IN)).get(T.label));
+
+        assertThat(traversal.hasNext(), is(false));
+    }
+
+    public static class Traversals extends ElementMapTest {
+        @Override
+        public Traversal<Vertex, Map<Object, Object>> get_g_V_elementMap() {
+            return g.V().elementMap();
+        }
+
+        @Override
+        public Traversal<Vertex, Map<Object, Object>> get_g_V_elementMapXname_ageX() {
+            return g.V().elementMap("name", "age");
+        }
+
+        @Override
+        public Traversal<Edge, Map<Object, Object>> get_g_EX11X_elementMap(final Object e11Id) {
+            return g.E(e11Id).elementMap();
+        }
+    }
+}