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

xiazcy pushed a commit to branch type-predicate-poc
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit f9f18479fe9830b2ea0d86cd5c11eb4d22f4137d
Author: xiazcy <[email protected]>
AuthorDate: Fri Jun 6 12:25:51 2025 -0700

    asbool() & type predicate proposal drafts
---
 .../src/dev/future/proposal-asbool-step-7.asciidoc |  75 +++++++++++++++
 .../dev/future/proposal-type-predicate-8.asciidoc  | 105 +++++++++++++++++++++
 .../gremlin/process/traversal/Compare.java         |  40 ++++++++
 .../tinkerpop/gremlin/process/traversal/P.java     |  21 +++++
 .../gremlin/util/GremlinValueComparator.java       |  11 +++
 .../tinkerpop/gremlin/process/traversal/PTest.java |   3 +
 6 files changed, 255 insertions(+)

diff --git a/docs/src/dev/future/proposal-asbool-step-7.asciidoc 
b/docs/src/dev/future/proposal-asbool-step-7.asciidoc
new file mode 100644
index 0000000000..901fddd141
--- /dev/null
+++ b/docs/src/dev/future/proposal-asbool-step-7.asciidoc
@@ -0,0 +1,75 @@
+////
+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.
+////
+
+image::apache-tinkerpop-logo.png[width=500,link="https://tinkerpop.apache.org";]
+
+*x.y.z - Proposal 7*
+
+== asBool() Step
+
+=== Motivation
+
+Given the additions of the `asString()` and `asDate()` steps in the 3.7 line, 
this proposal seeks to bridge another gap in language functionality, which is 
boolean parsing.
+
+=== Definition
+
+The `asBool()` step will convert the incoming traverser into a Boolean value.
+
+Note that `null` value will be considered `false`.
+
+The incoming traverser can be of type:
+
+*Number* - All non-zero values are considered `true`, zero, `NaN` and `null` 
values are considered `false`, for example:
+[cols=",",options="header",]
+|===
+|Numerical Value |Boolean Value
+|1 |true
+|0 |false
+|3.14 |true
+|-1 |true
+|-0.0 |false
+|0.0 |false
+|NaN |false
+|null| false
+|===
+
+*String* - Strings will not be convertable into bool, except boolean strings:
+[cols=",",options="header",]
+|===
+|Sting Value |Boolean Value
+|"true" |true
+|"false" |false
+|"True" |true
+|"False" |false
+|"TRUE" |true
+|"FALSE" |false
+|"trUE" |true
+|"faLSe" |false
+|"null"|Parsing Exception
+|"1" |Parsing Exception
+|"hello" |Parsing Exception
+|===
+
+*All other types* - Throws parsing exception.
+
+=== Alternative semantics
+
+Depending on how we want `asBool()` to be used, a potential alternative is to 
make `asBool()` an eval function and follow the `Groovy Truth` definitions.
+
+* For strings, all non-empty strings are considered `true`, and empty strings 
are considered `false`. Note in this case, `"false"` will be considered `true`, 
which could be confusing.
+
+* For collection, all non-empty collections are considered `true`, and empty 
collections are considered `false`.
\ No newline at end of file
diff --git a/docs/src/dev/future/proposal-type-predicate-8.asciidoc 
b/docs/src/dev/future/proposal-type-predicate-8.asciidoc
new file mode 100644
index 0000000000..b9eb0771f3
--- /dev/null
+++ b/docs/src/dev/future/proposal-type-predicate-8.asciidoc
@@ -0,0 +1,105 @@
+////
+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.
+////
+
+image::apache-tinkerpop-logo.png[width=500,link="https://tinkerpop.apache.org";]
+
+*x.y.z - Proposal 7*
+
+== Type Predicate
+
+=== Motivation
+
+Following up on 
link:https://issues.apache.org/jira/browse/TINKERPOP-2234[TINKERPOP-2234], 
filtering of traversers based on types would be a very convenient addition to 
data manipulation in Gremlin. For example, when we
+perform union of traversals and only want vertices or edges, currently there 
is no simple way to assert the type and pass it through.
+
+=== Definitions
+
+For defining what types to assert, we'll be using a set of tokens. A proposed 
set of type tokens are listed below based on GraphBinary.
+
+* The `token` could be a defined set of `Type` enums, e.g. `Type.String`, 
`Type.Number`, `Type.Date`, `Type.Vertex`, `Type.Edge`, `Type.Property`.
+** Note that the `GremlinValueComparator` class contains a set of `Type` 
enums, which we could extract and promote to use.
+* In addition, could also recognize the `Number` type tokens used by the 
`asNumber()` step, i.e. `N.Int`, `N.Double`.
+
+The range of tokens are:
+[cols=",",options="header",]
+|===
+|Token |Gremlin Type Reference
+|N.Int |GraphBinary 4.0, GType
+|N.Long |GraphBinary 4.0, GType
+|N.Double |GraphBinary 4.0, GType
+|N.Float |GraphBinary 4.0, GType
+|N.BigDecimal |GraphBinary 4.0, GType
+|N.BigInteger |GraphBinary 4.0, GType
+|N.Byte |GraphBinary 4.0, GType
+|N.Short |GraphBinary 4.0, GType
+|Type.Number | Token for Java Number type
+|Type.String |GraphBinary 4.0, GType
+|Type.DateTime |GraphBinary 4.0, GType
+|Type.List |GraphBinary 4.0, GType
+|Type.Set |GraphBinary 4.0, GType
+|Type.Map |GraphBinary 4.0, GType
+|Type.UUID |GraphBinary, 4.0 GType
+|Type.Edge |GraphBinary 4.0
+|Type.Path |GraphBinary 4.0
+|Type.Property |GraphBinary 4.0
+|Type.Graph |GraphBinary 4.0
+|Type.Vertex |GraphBinary 4.0, GType
+|Type.VP |GraphBinary 4.0(VertexProperty)
+|Type.Direction |GraphBinary 4.0
+|Type.T |GraphBinary 4.0
+|Type.Binary |GraphBinary 4.0
+|Type.Boolean |GraphBinary 4.0
+|Type.Tree |GraphBinary 4.0
+|Type.Char |GraphBinary 4.0
+|Type.Duration |GraphBinary 4.0
+|Type.Marker |GraphBinary 4.0
+|Type.PDT(type)* |GraphBinary 4.0(CompositePDT & PrimitivePDT)
+|Type.Null |GraphBinary 4.0(Unspecified Null Object), GValue(UNKNOWN)
+|===
+
+*Providers are to register their custom types which can be string arguments to 
the token, for the Grammar to recognize.
+
+==== Addition of `P.of(token)`
+
+Given `P` is widely used by filter steps and has existing evaluation 
structure, we would also introduce a new predicate condition, which should be 
quite straight forward.
+
+* `P.of(token)`
+* `P.nof(token)` - negation "not of type"
+
+A special convenience for embedded use is to take Java class as overload for 
syntactic sugar.
+
+* `P.of(class)`
+* `P.nof(class)` - negation "not of type"
+
+[source]
+----
+// embedded - recognizes Java classes
+gremlin> g.V().is(P.of(Vertex)).fold()
+==> [v[1],v[2],[v3],[v4],[v5],[v6]]
+gremlin> g.inject(1.0,2,3,'hello',false).is(P.of(Number)).fold()
+==> [1.0,2,3]
+
+// remote - uses the token
+gremlin> g.V().is(P.of(Type.Vertex)).fold()
+==> [v[1],v[2],[v3],[v4],[v5],[v6]]
+gremlin> g.inject(1.0,2,3,'hello',false).is(P.of(Type.number)).fold()
+==> [1.0,2,3]
+
+gremlin> g.inject(1.0,2,3,'hello',false).is(P.nof(Type.number)).fold()
+==> ['hello',false]
+----
+
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java
index 09387614f8..1f1be62777 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java
@@ -51,6 +51,26 @@ public enum Compare implements PBiPredicate<Object, Object> {
         }
     },
 
+    /**
+     * Evaluates if the first object is equal to the second per Gremlin 
Comparison semantics.
+     *
+     * @since 3.8.0
+     */
+    of {
+        @Override
+        public boolean test(final Object first, final Object second) {
+            return GremlinValueComparator.COMPARABILITY.of(first, second);
+        }
+
+        /**
+         * The negative of {@code eq} is {@link #neq}.
+         */
+        @Override
+        public Compare negate() {
+            return nof;
+        }
+    },
+
     /**
      * Evaluates if the first object is not equal to the second per Gremlin 
Comparison semantics.
      *
@@ -71,6 +91,26 @@ public enum Compare implements PBiPredicate<Object, Object> {
         }
     },
 
+    /**
+     * Evaluates if the first object is not equal to the second per Gremlin 
Comparison semantics.
+     *
+     * @since 3.8.0
+     */
+    nof {
+        @Override
+        public boolean test(final Object first, final Object second) {
+            return !of.test(first, second);
+        }
+
+        /**
+         * The negative of {@code neq} is {@link #eq}
+         */
+        @Override
+        public Compare negate() {
+            return of;
+        }
+    },
+
     /**
      * Evaluates if the first object is greater than the second per Gremlin 
Comparison semantics.
      *
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
index 917a13891d..edd18f5265 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal;
 
 import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.util.GremlinValueComparator;
 
 import java.io.Serializable;
 import java.util.Arrays;
@@ -145,6 +146,26 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
         return new P(Compare.neq, value);
     }
 
+    /**
+     * Determines if values are of a type.
+     *
+     * @since 3.8.0-incubating
+     */
+    // restricting parameter type to Class for PoC
+    public static <V> P<V> of(final Class<?> value) {
+        return new P(Compare.of, value);
+    }
+
+    /**
+     * Determines if values are not of a type.
+     *
+     * @since 3.8.0-incubating
+     */
+    // restricting parameter type to Class for PoC
+    public static <V> P<V> nof(final Class<?> value) {
+        return new P(Compare.nof, value);
+    }
+
     /**
      * Determines if a value is less than another.
      *
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinValueComparator.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinValueComparator.java
index f99c191b30..5593a11401 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinValueComparator.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinValueComparator.java
@@ -80,6 +80,10 @@ public abstract class GremlinValueComparator implements 
Comparator<Object> {
         public boolean equals(final Object f, final Object s) {
             return compare(f, s) == 0;
         }
+
+        public boolean of(final Object f, final Object s) {
+            return ((Class<?>)s).isAssignableFrom(f.getClass());
+        }
     };
 
     /**
@@ -160,6 +164,11 @@ public abstract class GremlinValueComparator implements 
Comparator<Object> {
             }
         }
 
+        @Override
+        public boolean of(final Object f, final Object s) {
+            return ((Class<?>)s).isAssignableFrom(f.getClass());
+        }
+
         private boolean containersOfDifferentSize(final Object f, final Object 
s) {
             if (f instanceof Collection && s instanceof Collection)
                 if (((Collection) f).size() != (((Collection) s).size()))
@@ -377,4 +386,6 @@ public abstract class GremlinValueComparator implements 
Comparator<Object> {
      */
     public abstract boolean equals(final Object f, final Object s);
 
+    public abstract boolean of(final Object f, final Object s);
+
 }
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 15855e0108..c6689f76d2 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
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.util.GremlinValueComparator;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -55,6 +56,8 @@ public class PTest {
         @Parameterized.Parameters(name = "{0}.test({1}) = {2}")
         public static Iterable<Object[]> data() {
             return new ArrayList<>(Arrays.asList(new Object[][]{
+                    {P.of(Number.class), 1, true},
+
                     {P.eq(0), 0, true},
                     {P.eq(0), -0, true},
                     {P.eq(0), +0, true},

Reply via email to