tinkerpop-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dkupp...@apache.org
Subject tinkerpop git commit: TINKERPOP-2041 Implemented text predicates -- wip
Date Thu, 27 Sep 2018 13:43:21 GMT
Repository: tinkerpop
Updated Branches:
  refs/heads/TINKERPOP-2041 [created] cd2fca834


TINKERPOP-2041 Implemented text predicates -- wip


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

Branch: refs/heads/TINKERPOP-2041
Commit: cd2fca83493e1db6266a8d91a44e486ab9f825ee
Parents: cce3fe6
Author: Daniel Kuppitz <daniel_kuppitz@hotmail.com>
Authored: Wed Sep 26 15:44:35 2018 -0700
Committer: Daniel Kuppitz <daniel_kuppitz@hotmail.com>
Committed: Wed Sep 26 15:44:35 2018 -0700

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 docs/src/reference/the-traversal.asciidoc       |  36 +++---
 docs/src/upgrade/release-3.4.x.asciidoc         |  15 +++
 .../tinkerpop/gremlin/jsr223/CoreImports.java   |   3 +
 .../tinkerpop/gremlin/process/traversal/TP.java | 107 ++++++++++++++++
 .../gremlin/process/traversal/Text.java         | 123 +++++++++++++++++++
 .../structure/io/graphson/GraphSONModule.java   |   5 +
 .../io/graphson/TraversalSerializersV2d0.java   |  35 ++++++
 .../io/graphson/TraversalSerializersV3d0.java   |  35 ++++++
 .../structure/io/gryo/GryoSerializersV1d0.java  |  21 ++++
 .../structure/io/gryo/GryoSerializersV3d0.java  |  21 ++++
 .../gremlin/structure/io/gryo/GryoVersion.java  |   7 +-
 .../gremlin/process/traversal/PTest.java        |  15 +++
 .../GraphSONMapperPartialEmbeddedTypeTest.java  |  14 ++-
 gremlin-dotnet/glv/generate.groovy              |   8 ++
 .../Gremlin.Net.Template.csproj                 |   2 +-
 .../Gremlin.Net.Template.nuspec                 |   2 +-
 .../src/Gremlin.Net/Gremlin.Net.csproj          |   2 +-
 .../gremlin/groovy/jsr223/GroovyTranslator.java |   2 +-
 gremlin-javascript/glv/TraversalSource.template |  38 +++++-
 gremlin-javascript/glv/generate.groovy          |   7 ++
 .../main/javascript/gremlin-javascript/index.js |   1 +
 .../gremlin-javascript/lib/process/traversal.js |  63 +++++++++-
 .../lib/structure/io/type-serializers.js        |  24 +++-
 .../test/cucumber/feature-steps.js              |   1 +
 gremlin-python/glv/TraversalSource.template     |  23 +++-
 gremlin-python/glv/generate.groovy              |   9 +-
 .../gremlin/python/jsr223/PythonTranslator.java |   8 ++
 .../jython/gremlin_python/process/traversal.py  |  63 +++++++++-
 .../gremlin_python/structure/io/graphsonV2d0.py |  13 +-
 .../gremlin_python/structure/io/graphsonV3d0.py |  13 +-
 .../src/main/jython/radish/feature_steps.py     |   3 +-
 gremlin-test/features/filter/Has.feature        |  13 +-
 .../gremlin/process/ProcessStandardSuite.java   |   8 +-
 .../process/traversal/step/filter/HasTest.java  |  18 +++
 35 files changed, 723 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 5e63da0..e6f3cd8 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>>.
 
+* Added text predicates.
 * Rewrote `ConnectiveStrategy` to support an arbitrary number of infix notations in a single traversal.
 * GraphSON `MessageSerializer`s will automatically register the GremlinServerModule to a provided GraphSONMapper.
 * Removed support for `-i` option in Gremlin Server which was previously deprecated.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 6146f9b..0991a98 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -3356,24 +3356,32 @@ interface. Steps that allow for this type of modulation will explicitly state so
 [[a-note-on-predicates]]
 == A Note on Predicates
 
-A `P` is a predicate of the form `Function<Object,Boolean>`. That is, given some object, return true or false. The
-provided predicates are outlined in the table below and are used in various steps such as <<has-step,`has()`>>-step,
-<<where-step,`where()`>>-step, <<is-step,`is()`>>-step, etc.
+A `P` is a predicate of the form `Function<Object,Boolean>`. That is, given some object, return true or false. As of
+the relase of TinkerPop 3.4.0, Gremlin also supports simple text predicates, which only work on `String` values. The `TP`
+text predicates extends the `P` predicates, but are specialized in that they are of the form `Function<String,Boolean>`.
+which are of the form `Function`. The provided predicates are outlined in the table below and are used in various steps
+such as <<has-step,`has()`>>-step, <<where-step,`where()`>>-step, <<is-step,`is()`>>-step, etc.
 
 [width="100%",cols="3,15",options="header"]
 |=========================================================
 | Predicate | Description
-| `eq(object)` | Is the incoming object equal to the provided object?
-| `neq(object)` | Is the incoming object not equal to the provided object?
-| `lt(number)` | Is the incoming number less than the provided number?
-| `lte(number)` | Is the incoming number less than or equal to the provided number?
-| `gt(number)` | Is the incoming number greater than the provided number?
-| `gte(number)` | Is the incoming number greater than or equal to the provided number?
-| `inside(number,number)` | Is the incoming number greater than the first provided number and less than the second?
-| `outside(number,number)` | Is the incoming number less than the first provided number or greater than the second?
-| `between(number,number)` | Is the incoming number greater than or equal to the first provided number and less than the second?
-| `within(objects...)` | Is the incoming object in the array of provided objects?
-| `without(objects...)` | Is the incoming object not in the array of the provided objects?
+| `P.eq(object)` | Is the incoming object equal to the provided object?
+| `P.neq(object)` | Is the incoming object not equal to the provided object?
+| `P.lt(number)` | Is the incoming number less than the provided number?
+| `P.lte(number)` | Is the incoming number less than or equal to the provided number?
+| `P.gt(number)` | Is the incoming number greater than the provided number?
+| `P.gte(number)` | Is the incoming number greater than or equal to the provided number?
+| `P.inside(number,number)` | Is the incoming number greater than the first provided number and less than the second?
+| `P.outside(number,number)` | Is the incoming number less than the first provided number or greater than the second?
+| `P.between(number,number)` | Is the incoming number greater than or equal to the first provided number and less than the second?
+| `P.within(objects...)` | Is the incoming object in the array of provided objects?
+| `P.without(objects...)` | Is the incoming object not in the array of the provided objects?
+| `TP.startsWith(string)` | Does the incoming `String` start with the provided `String`?
+| `TP.endsWith(string)` | Does the incoming `String` end with the provided `String`?
+| `TP.contains(string)` | Does the incoming `String` contain the provided `String`?
+| `TP.startsNotWith(string)` | TODO: find a better name
+| `TP.endsNotWith(string)` | TODO: find a better name
+| `TP.absent(string)` | TODO: find a better name
 |=========================================================
 
 [gremlin-groovy]

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/docs/src/upgrade/release-3.4.x.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.4.x.asciidoc b/docs/src/upgrade/release-3.4.x.asciidoc
index e0484a8..d677a5c 100644
--- a/docs/src/upgrade/release-3.4.x.asciidoc
+++ b/docs/src/upgrade/release-3.4.x.asciidoc
@@ -29,6 +29,21 @@ Please see the link:https://github.com/apache/tinkerpop/blob/3.4.0/CHANGELOG.asc
 
 === Upgrading for Users
 
+==== Added text predicates
+
+Gremlin now supports simple text predicates on top of the existing `P` predicates. Both, the new `TP` text predicates and the old `P` predicates, can be chained using `and()` and `or()`.
+
+[source,groovy]
+----
+gremlin> g.V().has("person","name", contains("o")).valueMap()
+==>[name:[marko],age:[29]]
+==>[name:[josh],age:[32]]
+gremlin> g.V().has("person","name", contains("o").and(gte("j").and(endsWith("ko")))).valueMap()
+==>[name:[marko],age:[29]]
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2041[TINKERPOP-2041]
+
 ==== Changed infix behavior
 
 The infix notation of `and()` and `or()` now supports an arbitrary number of traversals and `ConnectiveStrategy` produces a traversal with proper AND and OR semantics.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
index 2b1e33e..38ef258 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
@@ -67,6 +67,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
 import org.apache.tinkerpop.gremlin.process.traversal.Scope;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.Translator;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
@@ -181,6 +182,7 @@ public final class CoreImports {
         CLASS_IMPORTS.add(TraversalOptionParent.class);
         CLASS_IMPORTS.add(TraversalOptionParent.Pick.class);
         CLASS_IMPORTS.add(P.class);
+        CLASS_IMPORTS.add(TP.class);
         // remote
         CLASS_IMPORTS.add(RemoteConnection.class);
         CLASS_IMPORTS.add(RemoteGraph.class);
@@ -291,6 +293,7 @@ public final class CoreImports {
 
         uniqueMethods(IoCore.class).forEach(METHOD_IMPORTS::add);
         uniqueMethods(P.class).forEach(METHOD_IMPORTS::add);
+        uniqueMethods(TP.class).forEach(METHOD_IMPORTS::add);
         uniqueMethods(__.class).filter(m -> !m.getName().equals("__")).forEach(METHOD_IMPORTS::add);
         uniqueMethods(Computer.class).forEach(METHOD_IMPORTS::add);
         uniqueMethods(TimeUtil.class).forEach(METHOD_IMPORTS::add);

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TP.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TP.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TP.java
new file mode 100644
index 0000000..fc245a4
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TP.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal;
+
+import java.util.function.BiPredicate;
+
+/**
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+public class TP extends P<String> {
+
+    @SuppressWarnings("WeakerAccess")
+    public TP(final BiPredicate<String, String> biPredicate, final String value) {
+        super(biPredicate, value);
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof TP && super.equals(other);
+    }
+
+    @Override
+    public String toString() {
+        return null == this.originalValue ? this.biPredicate.toString() : this.biPredicate.toString() + "(" + this.originalValue + ")";
+    }
+
+    @Override
+    public TP negate() {
+        return new TP(this.biPredicate.negate(), this.originalValue);
+    }
+
+    public TP clone() {
+        return (TP) super.clone();
+    }
+
+    //////////////// statics
+
+    /**
+     * Determines if String does start with the given value.
+     *
+     * @since 3.4.0
+     */
+    public static TP startsWith(final String value) {
+        return new TP(Text.startsWith, value);
+    }
+
+    /**
+     * Determines if String does not start with the given value.
+     *
+     * @since 3.4.0
+     */
+    public static TP startsNotWith(final String value) {
+        return new TP(Text.startsNotWith, value);
+    }
+
+    /**
+     * Determines if String does start with the given value.
+     *
+     * @since 3.4.0
+     */
+    public static TP endsWith(final String value) {
+        return new TP(Text.endsWith, value);
+    }
+
+    /**
+     * Determines if String does not start with the given value.
+     *
+     * @since 3.4.0
+     */
+    public static TP endsNotWith(final String value) {
+        return new TP(Text.endsNotWith, value);
+    }
+
+    /**
+     * Determines if String does contain the given value.
+     *
+     * @since 3.4.0
+     */
+    public static TP contains(final String value) {
+        return new TP(Text.contains, value);
+    }
+
+    /**
+     * Determines if String does not contain the given value.
+     *
+     * @since 3.4.0
+     */
+    public static TP absent(final String value) {
+        return new TP(Text.absent, value);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Text.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Text.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Text.java
new file mode 100644
index 0000000..5169309
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Text.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal;
+
+import java.util.function.BiPredicate;
+
+/**
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+public enum Text implements BiPredicate<String, String> {
+
+    startsWith {
+        @Override
+        public boolean test(final String value, final String prefix) {
+            return value.startsWith(prefix);
+        }
+
+        /**
+         * The negative of {@code startsWith} is {@link #startsNotWith}.
+         */
+        @Override
+        public Text negate() {
+            return startsNotWith;
+        }
+    },
+
+    startsNotWith {
+        @Override
+        public boolean test(final String value, final String prefix) {
+            return !startsWith.test(value, prefix);
+        }
+
+        /**
+         * The negative of {@code startsNotWith} is {@link #startsWith}.
+         */
+        @Override
+        public Text negate() {
+            return startsWith;
+        }
+    },
+
+    endsWith {
+        @Override
+        public boolean test(final String value, final String suffix) {
+            return value.endsWith(suffix);
+        }
+
+        /**
+         * The negative of {@code endsWith} is {@link #endsNotWith}.
+         */
+        @Override
+        public Text negate() {
+            return endsNotWith;
+        }
+    },
+
+    endsNotWith {
+        @Override
+        public boolean test(final String value, final String prefix) {
+            return !endsWith.test(value, prefix);
+        }
+
+        /**
+         * The negative of {@code endsNotWith} is {@link #endsWith}.
+         */
+        @Override
+        public Text negate() {
+            return endsWith;
+        }
+    },
+
+    contains {
+        @Override
+        public boolean test(final String value, final String search) {
+            return value.contains(search);
+        }
+
+        /**
+         * The negative of {@code contains} is {@link #absent}.
+         */
+        @Override
+        public Text negate() {
+            return absent;
+        }
+    },
+
+    absent{
+        @Override
+        public boolean test(final String value, final String search) {
+            return !contains.test(value, search);
+        }
+
+        /**
+         * The negative of {@code absent} is {@link #contains}.
+         */
+        @Override
+        public Text negate() {
+            return contains;
+        }
+    };
+
+    /**
+     * Produce the opposite representation of the current {@code Text} enum.
+     */
+    @Override
+    public abstract Text negate();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index 1bccd7c..74647a1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Path;
 import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
 import org.apache.tinkerpop.gremlin.process.traversal.Scope;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -145,6 +146,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                     put(AndP.class, "P");
                     put(OrP.class, "P");
                     put(P.class, "P");
+                    put(TP.class, "TP");
                     Stream.of(
                             VertexProperty.Cardinality.class,
                             Column.class,
@@ -270,6 +272,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                     TraversalOptionParent.Pick.values(),
                     T.values()).flatMap(Stream::of).forEach(e -> addDeserializer(e.getClass(), new TraversalSerializersV3d0.EnumJacksonDeserializer(e.getDeclaringClass())));
             addDeserializer(P.class, new TraversalSerializersV3d0.PJacksonDeserializer());
+            addDeserializer(TP.class, new TraversalSerializersV3d0.TPJacksonDeserializer());
             addDeserializer(Lambda.class, new TraversalSerializersV3d0.LambdaJacksonDeserializer());
             addDeserializer(Traverser.class, new TraversalSerializersV3d0.TraverserJacksonDeserializer());
             Arrays.asList(
@@ -359,6 +362,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                     put(AndP.class, "P");
                     put(OrP.class, "P");
                     put(P.class, "P");
+                    put(TP.class, "TP");
                     Stream.of(
                             VertexProperty.Cardinality.class,
                             Column.class,
@@ -476,6 +480,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                     TraversalOptionParent.Pick.values(),
                     T.values()).flatMap(Stream::of).forEach(e -> addDeserializer(e.getClass(), new TraversalSerializersV2d0.EnumJacksonDeserializer(e.getDeclaringClass())));
             addDeserializer(P.class, new TraversalSerializersV2d0.PJacksonDeserializer());
+            addDeserializer(TP.class, new TraversalSerializersV2d0.TPJacksonDeserializer());
             addDeserializer(Lambda.class, new TraversalSerializersV2d0.LambdaJacksonDeserializer());
             addDeserializer(Traverser.class, new TraversalSerializersV2d0.TraverserJacksonDeserializer());
             Arrays.asList(

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2d0.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2d0.java
index 2a07723..7ba4ca5 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2d0.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2d0.java
@@ -24,6 +24,7 @@ import org.apache.commons.configuration.MapConfiguration;
 import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -372,6 +373,40 @@ final class TraversalSerializersV2d0 {
         }
     }
 
+    final static class TPJacksonDeserializer extends StdDeserializer<TP> {
+
+        public TPJacksonDeserializer() {
+            super(TP.class);
+        }
+
+        @Override
+        public TP deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
+            String predicate = null;
+            String value = null;
+
+            while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
+                if (jsonParser.getCurrentName().equals(GraphSONTokens.PREDICATE)) {
+                    jsonParser.nextToken();
+                    predicate = jsonParser.getText();
+                } else if (jsonParser.getCurrentName().equals(GraphSONTokens.VALUE)) {
+                    jsonParser.nextToken();
+                    value = deserializationContext.readValue(jsonParser, String.class);
+                }
+            }
+
+            try {
+                return (TP) TP.class.getMethod(predicate, String.class).invoke(null, value);
+            } catch (final Exception e) {
+                throw new IllegalStateException(e.getMessage(), e);
+            }
+        }
+
+        @Override
+        public boolean isCachable() {
+            return true;
+        }
+    }
+
     final static class LambdaJacksonDeserializer extends StdDeserializer<Lambda> {
 
         public LambdaJacksonDeserializer() {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java
index eaa7b0f..ca01ec0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java
@@ -24,6 +24,7 @@ import org.apache.commons.configuration.MapConfiguration;
 import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -370,6 +371,40 @@ final class TraversalSerializersV3d0 {
         }
     }
 
+    final static class TPJacksonDeserializer extends StdDeserializer<TP> {
+
+        public TPJacksonDeserializer() {
+            super(TP.class);
+        }
+
+        @Override
+        public TP deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
+            String predicate = null;
+            String value = null;
+
+            while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
+                if (jsonParser.getCurrentName().equals(GraphSONTokens.PREDICATE)) {
+                    jsonParser.nextToken();
+                    predicate = jsonParser.getText();
+                } else if (jsonParser.getCurrentName().equals(GraphSONTokens.VALUE)) {
+                    jsonParser.nextToken();
+                    value = deserializationContext.readValue(jsonParser, String.class);
+                }
+            }
+
+            try {
+                return (TP) TP.class.getMethod(predicate, String.class).invoke(null, value);
+            } catch (final Exception e) {
+                throw new IllegalStateException(e.getMessage(), e);
+            }
+        }
+
+        @Override
+        public boolean isCachable() {
+            return true;
+        }
+    }
+
     final static class LambdaJacksonDeserializer extends StdDeserializer<Lambda> {
 
         public LambdaJacksonDeserializer() {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
index ca7c241..c7de4ec 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
@@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraver
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
@@ -228,6 +229,26 @@ public final class GryoSerializersV1d0 {
         }
     }
 
+    public final static class TPSerializer implements SerializerShim<TP> {
+        @Override
+        public <O extends OutputShim> void write(final KryoShim<?, O> kryo, final O output, final TP p) {
+            output.writeString(p.getBiPredicate().toString());
+            kryo.writeObject(output, p.getValue());
+        }
+
+        @Override
+        public <I extends InputShim> TP read(final KryoShim<I, ?> kryo, final I input, final Class<TP> clazz) {
+            final String predicate = input.readString();
+            final String value = kryo.readObject(input, String.class);
+
+            try {
+                return (TP) TP.class.getMethod(predicate, String.class).invoke(null, value);
+            } catch (final Exception e) {
+                throw new IllegalStateException(e.getMessage(), e);
+            }
+        }
+    }
+
     public final static class LambdaSerializer implements SerializerShim<Lambda> {
         @Override
         public <O extends OutputShim> void write(final KryoShim<?, O> kryo, final O output, final Lambda lambda) {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
index fee9345..ffda00e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
@@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraver
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
@@ -344,6 +345,26 @@ public final class GryoSerializersV3d0 {
         }
     }
 
+    public final static class TPSerializer implements SerializerShim<TP> {
+        @Override
+        public <O extends OutputShim> void write(final KryoShim<?, O> kryo, final O output, final TP p) {
+            output.writeString(p.getBiPredicate().toString());
+            kryo.writeObject(output, p.getValue());
+        }
+
+        @Override
+        public <I extends InputShim> TP read(final KryoShim<I, ?> kryo, final I input, final Class<TP> clazz) {
+            final String predicate = input.readString();
+            final String value = kryo.readObject(input, String.class);
+
+            try {
+                return (TP) TP.class.getMethod(predicate, String.class).invoke(null, value);
+            } catch (final Exception e) {
+                throw new IllegalStateException(e.getMessage(), e);
+            }
+        }
+    }
+
     public final static class LambdaSerializer implements SerializerShim<Lambda> {
         @Override
         public <O extends OutputShim> void write(final KryoShim<?, O> kryo, final O output, final Lambda lambda) {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
index 7af3766..fb62fee 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
@@ -33,6 +33,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Path;
 import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
 import org.apache.tinkerpop.gremlin.process.traversal.Scope;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.FoldStep;
@@ -244,7 +245,7 @@ public enum GryoVersion {
             add(GryoTypeReg.of(Collections.singleton(null).getClass(), 54));
             add(GryoTypeReg.of(Collections.singletonList(null).getClass(), 24));
             add(GryoTypeReg.of(Collections.singletonMap(null, null).getClass(), 23));
-            add(GryoTypeReg.of(Types.COLLECTIONS_SYNCHRONIZED_MAP, 185, new UtilSerializers.SynchronizedMapSerializer()));  // ***LAST ID***
+            add(GryoTypeReg.of(Types.COLLECTIONS_SYNCHRONIZED_MAP, 185, new UtilSerializers.SynchronizedMapSerializer()));
             add(GryoTypeReg.of(Contains.class, 49));
             add(GryoTypeReg.of(Currency.class, 40));
             add(GryoTypeReg.of(Date.class, 38));
@@ -311,6 +312,7 @@ public enum GryoVersion {
 
             add(GryoTypeReg.of(Bytecode.class, 122, new GryoSerializersV3d0.BytecodeSerializer()));
             add(GryoTypeReg.of(P.class, 124, new GryoSerializersV3d0.PSerializer()));
+            add(GryoTypeReg.of(TP.class, 186, new GryoSerializersV3d0.TPSerializer())); // ***LAST ID***
             add(GryoTypeReg.of(Lambda.class, 125, new GryoSerializersV3d0.LambdaSerializer()));
             add(GryoTypeReg.of(Bytecode.Binding.class, 126, new GryoSerializersV3d0.BindingSerializer()));
             add(GryoTypeReg.of(Order.class, 127));
@@ -436,7 +438,7 @@ public enum GryoVersion {
             add(GryoTypeReg.of(Collections.singleton(null).getClass(), 54));
             add(GryoTypeReg.of(Collections.singletonList(null).getClass(), 24));
             add(GryoTypeReg.of(Collections.singletonMap(null, null).getClass(), 23));
-            add(GryoTypeReg.of(Types.COLLECTIONS_SYNCHRONIZED_MAP, 185, new UtilSerializers.SynchronizedMapSerializer()));  // ***LAST ID***
+            add(GryoTypeReg.of(Types.COLLECTIONS_SYNCHRONIZED_MAP, 185, new UtilSerializers.SynchronizedMapSerializer()));
             add(GryoTypeReg.of(Contains.class, 49));
             add(GryoTypeReg.of(Currency.class, 40));
             add(GryoTypeReg.of(Date.class, 38));
@@ -502,6 +504,7 @@ public enum GryoVersion {
 
             add(GryoTypeReg.of(Bytecode.class, 122, new GryoSerializersV1d0.BytecodeSerializer()));
             add(GryoTypeReg.of(P.class, 124, new GryoSerializersV1d0.PSerializer()));
+            add(GryoTypeReg.of(TP.class, 186, new GryoSerializersV1d0.TPSerializer())); // ***LAST ID***
             add(GryoTypeReg.of(Lambda.class, 125, new GryoSerializersV1d0.LambdaSerializer()));
             add(GryoTypeReg.of(Bytecode.Binding.class, 126, new GryoSerializersV1d0.BindingSerializer()));
             add(GryoTypeReg.of(Order.class, 127));

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
index 6ec33cc..1060b4e 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
@@ -94,6 +94,21 @@ public class PTest {
                     {P.between("m", "n").or(P.eq("daniel")), "marko", true},
                     {P.between("m", "n").or(P.eq("daniel")), "daniel", true},
                     {P.between("m", "n").or(P.eq("daniel")), "stephen", false},
+                    // text predicates
+                    {TP.contains("ark"), "marko", true},
+                    {TP.contains("ark"), "josh", false},
+                    {TP.startsWith("jo"), "marko", false},
+                    {TP.startsWith("jo"), "josh", true},
+                    {TP.endsWith("ter"), "marko", false},
+                    {TP.endsWith("ter"), "peter", true},
+                    {TP.contains("o"), "marko", true},
+                    {TP.contains("o"), "josh", true},
+                    {TP.contains("o").and(P.gte("j")), "marko", true},
+                    {TP.contains("o").and(P.gte("j")), "josh", true},
+                    {TP.contains("o").and(P.gte("j")).and(TP.endsWith("ko")), "marko", true},
+                    {TP.contains("o").and(P.gte("j")).and(TP.endsWith("ko")), "josh", false},
+                    {TP.contains("o").and(P.gte("j").and(TP.endsWith("ko"))), "marko", true},
+                    {TP.contains("o").and(P.gte("j").and(TP.endsWith("ko"))), "josh", false},
             }));
         }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
index 4e86ebd..52a7ee4 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson;
 import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.shaded.jackson.databind.JsonMappingException;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
@@ -327,10 +328,19 @@ public class GraphSONMapperPartialEmbeddedTypeTest extends AbstractGraphSONTest
                 P.without(1,2,3,4),
                 P.without(Arrays.asList(1,2,3,4)),
                 P.eq(1).and(P.eq(2)),
-                P.eq(1).or(P.eq(2)));
+                P.eq(1).or(P.eq(2)),
+                TP.contains("ark"),
+                TP.startsWith("mar"),
+                TP.endsWith("ko"),
+                TP.endsWith("ko").and(P.gte("mar")),
+                P.gte("mar").and(TP.endsWith("ko")));
 
         for (P p : variantsOfP) {
-            assertEquals(p, serializeDeserialize(mapper, p, P.class));
+            if (p instanceof TP) {
+                assertEquals(p, serializeDeserialize(mapper, p, TP.class));
+            } else {
+                assertEquals(p, serializeDeserialize(mapper, p, P.class));
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-dotnet/glv/generate.groovy
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/glv/generate.groovy b/gremlin-dotnet/glv/generate.groovy
index 0c93f3d..19c4955 100644
--- a/gremlin-dotnet/glv/generate.groovy
+++ b/gremlin-dotnet/glv/generate.groovy
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource
 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.P
+import org.apache.tinkerpop.gremlin.process.traversal.TP
 import org.apache.tinkerpop.gremlin.process.traversal.IO
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__
 import org.apache.tinkerpop.gremlin.structure.Direction
@@ -59,6 +60,7 @@ def toCSharpTypeMap = ["Long": "long",
                        "Traversal[]": "ITraversal[]",
                        "Predicate": "IPredicate",
                        "P": "P",
+                       "TP": "TP",
                        "TraversalStrategy": "ITraversalStrategy",
                        "TraversalStrategy[]": "ITraversalStrategy[]",
                        "Function": "IFunction",
@@ -241,6 +243,12 @@ def binding = ["pmethods": P.class.getMethods().
         collect { it.name }.
         unique().
         sort { a, b -> a <=> b },
+               "tpmethods": TP.class.getMethods().
+                       findAll { Modifier.isStatic(it.getModifiers()) }.
+                       findAll { TP.class.isAssignableFrom(it.returnType) }.
+                       collect { it.name }.
+                       unique().
+                       sort { a, b -> a <=> b },
                "sourceStepMethods": GraphTraversalSource.getMethods(). // SOURCE STEPS
                         findAll { GraphTraversalSource.class.equals(it.returnType) }.
                         findAll {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj b/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj
index 3748e83..42ca593 100644
--- a/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj
+++ b/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj
@@ -27,7 +27,7 @@ limitations under the License.
   <ItemGroup>
     <!-- We need both reference elements until this is resolved: https://github.com/dotnet/sdk/issues/1151 -->
     <ProjectReference Include="../Gremlin.Net/Gremlin.Net.csproj" />
-    <PackageReference Include="Gremlin.Net" Version="3.4.0-SNAPSHOT" />
+    <PackageReference Include="Gremlin.Net" Version="3.4.0-dev-1537984785875" />
   </ItemGroup>
 
 </Project>

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.nuspec
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.nuspec b/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.nuspec
index 90cceb6..79ac9d2 100644
--- a/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.nuspec
+++ b/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.nuspec
@@ -3,7 +3,7 @@
     <metadata>
         <id>Gremlin.Net.Template</id>
         <title>Gremlin.Net Template</title>
-        <version>3.4.0-SNAPSHOT</version>
+        <version>3.4.0-dev-1537984785875</version>
         <description>Gremlin.Net template to create a console application with dotnet new.</description>
         <authors>Apache TinkerPop</authors>
         <licenseUrl>https://github.com/apache/tinkerpop/blob/master/LICENSE</licenseUrl>

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj b/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
index 7ffa5ae..afcbe4c 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
+++ b/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
@@ -25,7 +25,7 @@ limitations under the License.
   </PropertyGroup>
 
   <PropertyGroup Label="Package">
-    <Version>3.4.0-SNAPSHOT</Version>
+    <Version>3.4.0-dev-1537984785875</Version>
     <FileVersion>3.4.0.0</FileVersion>
     <AssemblyVersion>3.4.0.0</AssemblyVersion>
     <Title>Gremlin.Net</Title>

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
index 48ba882..8007b00 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
@@ -207,7 +207,7 @@ public final class GroovyTranslator implements Translator.ScriptTranslator {
             }
             current.append(")");
         } else
-            current.append("P.").append(p.getBiPredicate().toString()).append("(").append(convertToString(p.getValue())).append(")");
+            current.append(p.getClass().getSimpleName()).append(".").append(p.getBiPredicate().toString()).append("(").append(convertToString(p.getValue())).append(")");
         return current;
     }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-javascript/glv/TraversalSource.template
----------------------------------------------------------------------
diff --git a/gremlin-javascript/glv/TraversalSource.template b/gremlin-javascript/glv/TraversalSource.template
index ffe0fbc..8eceb07 100644
--- a/gremlin-javascript/glv/TraversalSource.template
+++ b/gremlin-javascript/glv/TraversalSource.template
@@ -162,6 +162,41 @@ function createP(operator, args) {
   return new (Function.prototype.bind.apply(P, args));
 }
 
+class TP {
+  /**
+   * Represents an operation.
+   * @constructor
+   */
+  constructor(operator, value, other) {
+    this.operator = operator;
+    this.value = value;
+    this.other = other;
+  }
+
+  /**
+   * Returns the string representation of the instance.
+   * @returns {string}
+   */
+  toString() {
+    if (this.other === undefined) {
+      return this.operator + '(' + this.value + ')';
+    }
+    return this.operator + '(' + this.value + ', ' + this.other + ')';
+  }
+
+<% tpmethods.each{ method -> %>
+  /** @param {...Object} args */
+  static <%= toJs.call(method) %>(...args) {
+    return createTP('<%= method %>', args);
+  }
+<% } %>
+}
+
+function createTP(operator, args) {
+  args.unshift(null, operator);
+  return new (Function.prototype.bind.apply(TP, args));
+}
+
 class Traverser {
   constructor(object, bulk) {
     this.object = object;
@@ -199,6 +234,7 @@ class EnumValue {
 module.exports = {
   EnumValue,
   P,
+  TP,
   IO,
   Traversal,
   TraversalSideEffects,
@@ -208,4 +244,4 @@ enums.each{ enumClass ->
         enumClass.getEnumConstants().sort { a, b -> a.name() <=> b.name() }.collect { toJs.call(it.name()) }.join(' ') + "')"
 }
 %>
-};
\ No newline at end of file
+};

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-javascript/glv/generate.groovy
----------------------------------------------------------------------
diff --git a/gremlin-javascript/glv/generate.groovy b/gremlin-javascript/glv/generate.groovy
index aab55d4..243c607 100644
--- a/gremlin-javascript/glv/generate.groovy
+++ b/gremlin-javascript/glv/generate.groovy
@@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource
 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.P
+import org.apache.tinkerpop.gremlin.process.traversal.TP
 import org.apache.tinkerpop.gremlin.process.traversal.IO
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__
 import java.lang.reflect.Modifier
@@ -69,6 +70,12 @@ def binding = ["enums": CoreImports.getClassImports()
                        collect { it.name }.
                        unique().
                        sort { a, b -> a <=> b },
+               "tpmethods": TP.class.getMethods().
+                       findAll { Modifier.isStatic(it.getModifiers()) }.
+                       findAll { TP.class.isAssignableFrom(it.returnType) }.
+                       collect { it.name }.
+                       unique().
+                       sort { a, b -> a <=> b },
                "sourceStepMethods": GraphTraversalSource.getMethods(). // SOURCE STEPS
                        findAll { GraphTraversalSource.class.equals(it.returnType) }.
                        findAll {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js
----------------------------------------------------------------------
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js
index c2e810d..b15ecbc 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js
@@ -49,6 +49,7 @@ module.exports = {
     Bytecode: Bytecode,
     EnumValue: t.EnumValue,
     P: t.P,
+    TP: t.TP,
     Traversal: t.Traversal,
     TraversalSideEffects: t.TraversalSideEffects,
     TraversalStrategies: strategiesModule.TraversalStrategies,

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal.js
----------------------------------------------------------------------
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 2b9ba26..50a7a3e 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
@@ -310,6 +310,66 @@ function createP(operator, args) {
   return new (Function.prototype.bind.apply(P, args));
 }
 
+class TP {
+  /**
+   * Represents an operation.
+   * @constructor
+   */
+  constructor(operator, value, other) {
+    this.operator = operator;
+    this.value = value;
+    this.other = other;
+  }
+
+  /**
+   * Returns the string representation of the instance.
+   * @returns {string}
+   */
+  toString() {
+    if (this.other === undefined) {
+      return this.operator + '(' + this.value + ')';
+    }
+    return this.operator + '(' + this.value + ', ' + this.other + ')';
+  }
+
+
+  /** @param {...Object} args */
+  static absent(...args) {
+    return createTP('absent', args);
+  }
+
+  /** @param {...Object} args */
+  static contains(...args) {
+    return createTP('contains', args);
+  }
+
+  /** @param {...Object} args */
+  static endsNotWith(...args) {
+    return createTP('endsNotWith', args);
+  }
+
+  /** @param {...Object} args */
+  static endsWith(...args) {
+    return createTP('endsWith', args);
+  }
+
+  /** @param {...Object} args */
+  static startsNotWith(...args) {
+    return createTP('startsNotWith', args);
+  }
+
+  /** @param {...Object} args */
+  static startsWith(...args) {
+    return createTP('startsWith', args);
+  }
+
+}
+
+function createTP(operator, args) {
+  args.unshift(null, operator);
+  return new (Function.prototype.bind.apply(TP, args));
+}
+
 class Traverser {
   constructor(object, bulk) {
     this.object = object;
@@ -347,6 +407,7 @@ class EnumValue {
 module.exports = {
   EnumValue,
   P,
+  TP,
   IO,
   Traversal,
   TraversalSideEffects,
@@ -363,4 +424,4 @@ module.exports = {
   pop: toEnum('Pop', 'all first last mixed'),
   scope: toEnum('Scope', 'global local'),
   t: toEnum('T', 'id key label value')
-};
\ No newline at end of file
+};

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
----------------------------------------------------------------------
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 0e17cc8..9e76186 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
@@ -150,6 +150,28 @@ class PSerializer extends TypeSerializer {
   }
 }
 
+class TPSerializer extends TypeSerializer {
+  /** @param {P} item */
+  serialize(item) {
+    const result = {};
+    result[typeKey] = 'g:TP';
+    const resultValue = result[valueKey] = {
+      'predicate': item.operator
+    };
+    if (item.other === undefined || item.other === null) {
+      resultValue['value'] = this.writer.adaptObject(item.value);
+    }
+    else {
+      resultValue['value'] = [ this.writer.adaptObject(item.value), this.writer.adaptObject(item.other) ];
+    }
+    return result;
+  }
+
+  canBeUsedFor(value) {
+    return (value instanceof t.TP);
+  }
+}
+
 class LambdaSerializer extends TypeSerializer {
   /** @param {Function} item */
   serialize(item) {
@@ -391,4 +413,4 @@ module.exports = {
   valueKey,
   VertexPropertySerializer,
   VertexSerializer
-};
\ No newline at end of file
+};

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
----------------------------------------------------------------------
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 a0ddddb..abef2a2 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
@@ -218,6 +218,7 @@ function getSandbox(g, parameters) {
     },
     Order: traversalModule.order,
     P: traversalModule.P,
+    TP: traversalModule.TP,
     IO: traversalModule.IO,
     Pick: traversalModule.pick,
     Pop: traversalModule.pop,

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-python/glv/TraversalSource.template
----------------------------------------------------------------------
diff --git a/gremlin-python/glv/TraversalSource.template b/gremlin-python/glv/TraversalSource.template
index 713681b..639c5f8 100644
--- a/gremlin-python/glv/TraversalSource.template
+++ b/gremlin-python/glv/TraversalSource.template
@@ -152,6 +152,27 @@ def <%= method %>(*args):
 statics.add_static('<%= method %>',<%= method %>)
 <% } %>
 
+class TP(object):
+    def __init__(self, operator, value, other=None):
+        self.operator = operator
+        self.value = value
+        self.other = other
+<% tpmethods.each { method -> %>
+    @staticmethod
+    def <%= method %>(*args):
+        return TP("<%= toJava.call(method) %>", *args)
+<% } %>
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.operator == other.operator and self.value == other.value and self.other == other.other
+
+    def __repr__(self):
+        return self.operator + "(" + str(self.value) + ")" if self.other is None else self.operator + "(" + str(self.value) + "," + str(self.other) + ")"
+<% tpmethods.findAll{!it.equals("clone")}.each { method -> %>
+def <%= method %>(*args):
+    return TP.<%= method %>(*args)
+statics.add_static('<%= method %>',<%= method %>)
+<% } %>
+
 <% tokens.each { k,v -> %>
 '''
 <%= k %>
@@ -324,4 +345,4 @@ class Binding(object):
         return hash(self.key) + hash(self.value)
 
     def __repr__(self):
-        return "binding[" + self.key + "=" + str(self.value) + "]"
\ No newline at end of file
+        return "binding[" + self.key + "=" + str(self.value) + "]"

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-python/glv/generate.groovy
----------------------------------------------------------------------
diff --git a/gremlin-python/glv/generate.groovy b/gremlin-python/glv/generate.groovy
index c7ad241..8c3d647 100644
--- a/gremlin-python/glv/generate.groovy
+++ b/gremlin-python/glv/generate.groovy
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource
 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.P
+import org.apache.tinkerpop.gremlin.process.traversal.TP
 import org.apache.tinkerpop.gremlin.process.traversal.IO
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__
 import java.lang.reflect.Modifier
@@ -63,6 +64,12 @@ def binding = ["enums": CoreImports.getClassImports()
                        collect { toPython(it.name) }.
                        unique().
                        sort { a, b -> a <=> b },
+               "tpmethods": TP.class.getMethods().
+                       findAll { Modifier.isStatic(it.getModifiers()) }.
+                       findAll { TP.class.isAssignableFrom(it.returnType) }.
+                       collect { toPython(it.name) }.
+                       unique().
+                       sort { a, b -> a <=> b },
                "sourceStepMethods": GraphTraversalSource.getMethods(). // SOURCE STEPS
                        findAll { GraphTraversalSource.class.equals(it.returnType) }.
                        findAll {
@@ -102,4 +109,4 @@ traversalFile.newWriter().withWriter{ it << traversalTemplate }
 
 def graphTraversalTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/GraphTraversalSource.template")).make(binding)
 def graphTraversalFile = new File("${projectBaseDir}/src/main/jython/gremlin_python/process/graph_traversal.py")
-graphTraversalFile.newWriter().withWriter{ it << graphTraversalTemplate }
\ No newline at end of file
+graphTraversalFile.newWriter().withWriter{ it << graphTraversalTemplate }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonTranslator.java
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonTranslator.java b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonTranslator.java
index 0bc324e..38108eb 100644
--- a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonTranslator.java
+++ b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonTranslator.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.Translator;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
@@ -195,6 +196,8 @@ public class PythonTranslator implements Translator.ScriptTranslator {
             return "Pick." + resolveSymbol(object.toString());
         else if (object instanceof Enum)
             return convertStatic(((Enum) object).getDeclaringClass().getSimpleName() + ".") + resolveSymbol(object.toString());
+        else if (object instanceof TP)
+            return convertTPToString((TP) object, new StringBuilder()).toString();
         else if (object instanceof P)
             return convertPToString((P) object, new StringBuilder()).toString();
         else if (object instanceof Element) {
@@ -237,6 +240,11 @@ public class PythonTranslator implements Translator.ScriptTranslator {
         return current;
     }
 
+    private StringBuilder convertTPToString(final TP p, final StringBuilder current) {
+        current.append(convertStatic("TP.")).append(p.getBiPredicate().toString()).append("(").append(convertToString(p.getValue())).append(")");
+        return current;
+    }
+
     protected String convertLambdaToString(final Lambda lambda) {
         final String lambdaString = lambda.getLambdaScript().trim();
         return lambdaString.startsWith("lambda") ? lambdaString : "lambda " + lambdaString;

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
----------------------------------------------------------------------
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 094bd6d..4892bfc 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
@@ -322,6 +322,67 @@ def without(*args):
 statics.add_static('without',without)
 
 
+class TP(object):
+    def __init__(self, operator, value, other=None):
+        self.operator = operator
+        self.value = value
+        self.other = other
+
+    @staticmethod
+    def absent(*args):
+        return TP("absent", *args)
+
+    @staticmethod
+    def contains(*args):
+        return TP("contains", *args)
+
+    @staticmethod
+    def endsNotWith(*args):
+        return TP("endsNotWith", *args)
+
+    @staticmethod
+    def endsWith(*args):
+        return TP("endsWith", *args)
+
+    @staticmethod
+    def startsNotWith(*args):
+        return TP("startsNotWith", *args)
+
+    @staticmethod
+    def startsWith(*args):
+        return TP("startsWith", *args)
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.operator == other.operator and self.value == other.value and self.other == other.other
+
+    def __repr__(self):
+        return self.operator + "(" + str(self.value) + ")" if self.other is None else self.operator + "(" + str(self.value) + "," + str(self.other) + ")"
+
+def absent(*args):
+    return TP.absent(*args)
+statics.add_static('absent',absent)
+
+def contains(*args):
+    return TP.contains(*args)
+statics.add_static('contains',contains)
+
+def endsNotWith(*args):
+    return TP.endsNotWith(*args)
+statics.add_static('endsNotWith',endsNotWith)
+
+def endsWith(*args):
+    return TP.endsWith(*args)
+statics.add_static('endsWith',endsWith)
+
+def startsNotWith(*args):
+    return TP.startsNotWith(*args)
+statics.add_static('startsNotWith',startsNotWith)
+
+def startsWith(*args):
+    return TP.startsWith(*args)
+statics.add_static('startsWith',startsWith)
+
+
 
 '''
 IO
@@ -556,4 +617,4 @@ class Binding(object):
         return hash(self.key) + hash(self.value)
 
     def __repr__(self):
-        return "binding[" + self.key + "=" + str(self.value) + "]"
\ No newline at end of file
+        return "binding[" + self.key + "=" + str(self.value) + "]"

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py
index d53a080..2cb0b5b 100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py
@@ -27,7 +27,7 @@ from aenum import Enum
 
 from gremlin_python import statics
 from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, TypeType
-from gremlin_python.process.traversal import Binding, Bytecode, P, Traversal, Traverser, TraversalStrategy
+from gremlin_python.process.traversal import Binding, Bytecode, P, TP, Traversal, Traverser, TraversalStrategy
 from gremlin_python.structure.graph import Edge, Property, Vertex, VertexProperty, Path
 
 # When we fall back to a superclass's serializer, we iterate over this map.
@@ -278,6 +278,17 @@ class PSerializer(_GraphSONTypeIO):
         return GraphSONUtil.typedValue("P", out)
 
 
+class TPSerializer(_GraphSONTypeIO):
+    python_type = TP
+
+    @classmethod
+    def dictify(cls, p, writer):
+        out = {"predicate": p.operator,
+               "value": [writer.toDict(p.value), writer.toDict(p.other)] if p.other is not None else
+               writer.toDict(p.value)}
+        return GraphSONUtil.typedValue("TP", out)
+
+
 class BindingSerializer(_GraphSONTypeIO):
     python_type = Binding
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
----------------------------------------------------------------------
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 5539448..266e23d 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
@@ -27,7 +27,7 @@ from aenum import Enum
 
 from gremlin_python import statics
 from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, TypeType, DictType, ListType, SetType
-from gremlin_python.process.traversal import Binding, Bytecode, P, Traversal, Traverser, TraversalStrategy, T
+from gremlin_python.process.traversal import Binding, Bytecode, P, TP, Traversal, Traverser, TraversalStrategy, T
 from gremlin_python.structure.graph import Edge, Property, Vertex, VertexProperty, Path
 
 # When we fall back to a superclass's serializer, we iterate over this map.
@@ -284,6 +284,17 @@ class PSerializer(_GraphSONTypeIO):
         return GraphSONUtil.typedValue("P", out)
 
 
+class TPSerializer(_GraphSONTypeIO):
+    python_type = TP
+
+    @classmethod
+    def dictify(cls, p, writer):
+        out = {"predicate": p.operator,
+               "value": [writer.toDict(p.value), writer.toDict(p.other)] if p.other is not None else
+               writer.toDict(p.value)}
+        return GraphSONUtil.typedValue("TP", out)
+
+
 class BindingSerializer(_GraphSONTypeIO):
     python_type = Binding
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-python/src/main/jython/radish/feature_steps.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/radish/feature_steps.py b/gremlin-python/src/main/jython/radish/feature_steps.py
index aff73dc..151d6d5 100644
--- a/gremlin-python/src/main/jython/radish/feature_steps.py
+++ b/gremlin-python/src/main/jython/radish/feature_steps.py
@@ -21,7 +21,7 @@ import json
 import re
 from gremlin_python.structure.graph import Graph, Path
 from gremlin_python.process.graph_traversal import __
-from gremlin_python.process.traversal import Barrier, Cardinality, P, Pop, Scope, Column, Order, Direction, T, Pick, Operator, IO
+from gremlin_python.process.traversal import Barrier, Cardinality, P, TP, Pop, Scope, Column, Order, Direction, T, Pick, Operator, IO
 from radish import given, when, then
 from hamcrest import *
 
@@ -256,6 +256,7 @@ def _make_traversal(g, traversal_string, params):
          "Direction": Direction,
          "Order": Order,
          "P": P,
+         "TP": TP,
          "IO": IO,
          "Pick": Pick,
          "Pop": Pop,

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-test/features/filter/Has.feature
----------------------------------------------------------------------
diff --git a/gremlin-test/features/filter/Has.feature b/gremlin-test/features/filter/Has.feature
index ddf9984..01e6bd6 100644
--- a/gremlin-test/features/filter/Has.feature
+++ b/gremlin-test/features/filter/Has.feature
@@ -557,4 +557,15 @@ Feature: Step - has()
     When iterated to list
     Then the result should be unordered
       | result |
-      | d[6].l |
\ No newline at end of file
+      | d[6].l |
+
+  Scenario: g_V_hasXname_containsXarkXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("name", TP.contains("ark"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
----------------------------------------------------------------------
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..aa31319 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
@@ -109,7 +109,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
      */
     private static final Class<?>[] allTests = new Class<?>[]{
             // branch
-            BranchTest.Traversals.class,
+            /*BranchTest.Traversals.class,
             ChooseTest.Traversals.class,
             OptionalTest.Traversals.class,
             LocalTest.Traversals.class,
@@ -122,8 +122,8 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             CyclicPathTest.Traversals.class,
             DedupTest.Traversals.class,
             DropTest.Traversals.class,
-            FilterTest.Traversals.class,
-            HasTest.Traversals.class,
+            FilterTest.Traversals.class,*/
+            HasTest.Traversals.class/*,
             IsTest.Traversals.class,
             OrTest.Traversals.class,
             RangeTest.Traversals.class,
@@ -191,7 +191,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             SubgraphStrategyProcessTest.class,
 
             // optimizations
-            IncidentToAdjacentStrategyProcessTest.class
+            IncidentToAdjacentStrategyProcessTest.class*/
     };
 
     /**

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/cd2fca83/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasTest.java
index 3bdb24a..dd3567c 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasTest.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.LoadGraphWith;
 import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
 import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.TP;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -125,6 +126,8 @@ public abstract class HasTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, Long> get_g_V_hasXage_withoutX27_29X_count();
 
+    public abstract Traversal<Vertex, Vertex> get_g_V_hasXname_containsXarkXX();
+
     @Test
     @LoadGraphWith(MODERN)
     public void g_V_outXcreatedX_hasXname__mapXlengthX_isXgtX3XXX_name() {
@@ -547,6 +550,16 @@ public abstract class HasTest extends AbstractGremlinProcessTest {
         assertEquals(2L, traversal.next().longValue());
     }
 
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_hasXname_containsXarkXX() {
+        final Traversal<Vertex, Vertex> traversal = get_g_V_hasXname_containsXarkXX();
+        printTraversalForm(traversal);
+        assertTrue(traversal.hasNext());
+        assertTrue(traversal.next().value("name").equals("marko"));
+        assertFalse(traversal.hasNext());
+    }
+
     public static class Traversals extends HasTest {
         @Override
         public Traversal<Edge, Edge> get_g_EX11X_outV_outE_hasXid_10X(final Object e11Id, final Object e10Id) {
@@ -727,5 +740,10 @@ public abstract class HasTest extends AbstractGremlinProcessTest {
         public Traversal<Vertex, Long> get_g_V_hasXage_withoutX27_29X_count() {
             return g.V().has("age", P.without(27, 29)).count();
         }
+
+        @Override
+        public Traversal<Vertex, Vertex> get_g_V_hasXname_containsXarkXX() {
+            return g.V().has("name", TP.contains("ark"));
+        }
     }
 }
\ No newline at end of file


Mime
View raw message