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

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

commit 964d6f4b526cacf22946f8746dd1af04c0a7c476
Author: Stephen Mallette <[email protected]>
AuthorDate: Tue Feb 10 17:03:47 2026 +0000

    Fixed issue with Gremlin semantics around comparability
    
    Allowed different size collections to be comparable.
---
 CHANGELOG.asciidoc                                 |   3 +-
 .../gremlin/util/GremlinValueComparator.java       |   3 -
 .../gremlin/process/traversal/CompareTest.java     | 121 +++++++++++++++++++++
 3 files changed, 123 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 22f3c2218f..e82b21c3b5 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 This release also includes changes from <<release-3-7-6, 3.7.6>>.
 
 * Fixed bug in pre-repeat() `emit()/until()` where `emit()` and `until()` 
traversers weren't added to the results.
+* Fixed bug in `GremlinValueComparator` that prevented collections of 
differing sizes from being comparable.
 * Expose serialization functions for alternative transport protocols in 
gremlin-go
 * Improved Gremlint formatting to keep the first argument for a step on the 
same line if line breaks were required to meet max line length.
 * Improved Gremlint formatting to do greedy argument packing when possible so 
that more arguments can appear on a single line.
@@ -35,7 +36,7 @@ This release also includes changes from <<release-3-7-6, 
3.7.6>>.
 
 This release also includes changes from <<release-3-7-5, 3.7.5>>.
 
-* Added a Gremln MCP server.
+* Added a Gremlin MCP server.
 * Added the Air Routes 1.0 dataset to the set of available samples packaged 
with distributions.
 * Added a minimal distribution for `tinkergraph-gremlin` using the `min` 
classifier that doesn't include the sample datasets.
 * Removed `AggregateLocalStep` and `aggregate(Scope, String)`, and renamed 
`AggregateGlobalStep` to `AggregateStep`.
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 2f6e374d24..5d98d6e8ad 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
@@ -370,9 +370,6 @@ public abstract class GremlinValueComparator implements 
Comparator<Object> {
                 return false;
             }
         }
-        if (fi.hasNext() || si.hasNext()) {
-            return false;
-        }
         return true;
     }
 
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CompareTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CompareTest.java
index 38633df8cd..5e35b08926 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CompareTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CompareTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal;
 
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.MutablePath;
 import org.javatuples.Pair;
 import org.junit.Rule;
 import org.junit.Test;
@@ -29,6 +30,7 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import static org.apache.tinkerpop.gremlin.util.CollectionUtil.asList;
@@ -249,6 +251,117 @@ public class CompareTest {
                 {Compare.gt, asMap(1.0, "foo", 2.0, "bar"), asMap(2, "bar", 1, 
"foo"), false},
                 {Compare.gte, asMap(1.0, "foo", 2.0, "bar"), asMap(2, "bar", 
1, "foo"), true},
 
+                // Empty collections
+                {Compare.eq,  asList(), asList(), true},
+                {Compare.neq, asList(), asList(), false},
+                {Compare.lt,  asList(), asList(), false},
+                {Compare.lte, asList(), asList(), true},
+                {Compare.gt,  asList(), asList(), false},
+                {Compare.gte, asList(), asList(), true},
+
+                {Compare.lt,  asList(), asList(1), true},
+                {Compare.gt,  asList(1), asList(), true},
+
+                {Compare.eq,  asSet(), asSet(), true},
+                {Compare.lt,  asSet(), asSet(1), true},
+                {Compare.gt,  asSet(1), asSet(), true},
+
+                {Compare.eq,  asMap(), asMap(), true},
+                {Compare.lt,  asMap(), asMap(1, 1), true},
+                {Compare.gt,  asMap(1, 1), asMap(), true},
+
+                // Different sizes
+                {Compare.lt,  asList(1, 2, 3), asList(1, 2, 3, 4), true},
+                {Compare.gt,  asList(1, 2, 3, 4), asList(1, 2, 3), true},
+                {Compare.lt,  asList(1, 2, 4), asList(1, 2, 3, 4), false},
+                {Compare.gt,  asList(1, 2, 4), asList(1, 2, 3, 4), true},
+
+                {Compare.lt,  asSet(1, 2, 3), asSet(1, 2, 3, 4), true},
+                {Compare.gt,  asSet(1, 2, 3, 4), asSet(1, 2, 3), true},
+                {Compare.lt,  asSet(1, 2, 4), asSet(1, 2, 3, 4), false},
+                {Compare.gt,  asSet(1, 2, 4), asSet(1, 2, 3, 4), true},
+
+                {Compare.lt,  asMap(1, 1, 2, 2, 3, 3), asMap(1, 1, 2, 2, 3, 3, 
4, 4), true},
+                {Compare.gt,  asMap(1, 1, 2, 2, 3, 3, 4, 4), asMap(1, 1, 2, 2, 
3, 3), true},
+
+                // Type promotion within collections
+                {Compare.eq,  asList(1, 2), asList(1.0, 2.0), true},
+                {Compare.lt,  asList(1, 2), asList(1.0, 3.0), true},
+                {Compare.eq,  asSet(1, 2), asSet(2.0, 1.0), true},
+                {Compare.eq,  asMap(1, 1.0), asMap(1L, 1.0d), true},
+
+                // Lexicographical comparison with mixed types (but pairwise 
comparable)
+                {Compare.lt,  asList(1, "a"), asList(1, "b"), true},
+                {Compare.lt,  asList(1, "a"), asList(2, "a"), true},
+                {Compare.gt,  asList(1, "a"), asList(0, "a"), true},
+
+                // Sets and Maps with mixed types (Orderability used for 
sorting)
+                {Compare.lt,  asSet(1, "a"), asSet(1, "b"), true},
+                {Compare.eq,  asSet(1, "a"), asSet("a", 1), true},
+                {Compare.gt,  asMap(1, "a", "z", 2), asMap(1, "a", "y", 2), 
true},
+
+                // Incomparable elements (cross-type) in collections
+                {Compare.eq,  asList(1, "a"), asList(1, 1), false},
+                {Compare.neq, asList(1, "a"), asList(1, 1), true},
+                {Compare.lt,  asList(1, "a"), asList(1, 1), false},
+                {Compare.gt,  asList(1, "a"), asList(1, 1), false},
+
+                {Compare.eq,  asSet(1, "a"), asSet(1, 2.0), false},
+                {Compare.lt,  asSet(1, "a"), asSet(1, 2.0), false},
+
+                // Nested collections
+                {Compare.lt,  asList(asList(1, 2)), asList(asList(1, 3)), 
true},
+                {Compare.lt,  asList(asList(1, 2)), asList(asList(1, 2, 3)), 
true},
+                {Compare.gt,  asList(asList(1, 2)), asList(asList(1, 1)), 
true},
+
+                // Nulls in collections
+                {Compare.eq,  asList(1, null), asList(1, null), true},
+                {Compare.eq,  asList(1, null), asList(1, 2), false},
+                {Compare.lt,  asList(1, null), asList(1, 2), false},
+                {Compare.gt,  asList(1, null), asList(1, 2), false},
+
+                // Paths
+                {Compare.eq,  p(), p(), true},
+                {Compare.neq, p(), p(), false},
+                {Compare.lt,  p(), p(), false},
+                {Compare.lte, p(), p(), true},
+                {Compare.gt,  p(), p(), false},
+                {Compare.gte, p(), p(), true},
+
+                {Compare.lt,  p(), p(1), true},
+                {Compare.gt,  p(1), p(), true},
+
+                // Lexicographical comparison (pairwise)
+                {Compare.lt,  p(1, "a"), p(1, "b"), true},
+                {Compare.lt,  p(1, "a"), p(2, "a"), true},
+                {Compare.gt,  p(1, "a"), p(0, "a"), true},
+
+                // Different sizes
+                {Compare.lt,  p(1, 2, 3), p(1, 2, 3, 4), true},
+                {Compare.gt,  p(1, 2, 3, 4), p(1, 2, 3), true},
+
+                // Incomparable elements (cross-type) in paths
+                {Compare.eq,  p(1, "a"), p(1, 1), false},
+                {Compare.neq, p(1, "a"), p(1, 1), true},
+                {Compare.lt,  p(1, "a"), p(1, 1), false},
+                {Compare.gt,  p(1, "a"), p(1, 1), false},
+
+                // Nested collections in paths
+                {Compare.lt,  p(asList(1, 2)), p(asList(1, 3)), true},
+                {Compare.lt,  p(asList(1, 2)), p(asList(1, 2, 3)), true},
+                {Compare.gt,  p(asList(1, 2)), p(asList(1, 1)), true},
+
+                // Nulls in paths
+                {Compare.eq,  p(1, null), p(1, null), true},
+                {Compare.eq,  p(1, null), p(1, 2), false},
+                {Compare.lt,  p(1, null), p(1, 2), false},
+                {Compare.gt,  p(1, null), p(1, 2), false},
+
+                // Cross-type Path vs List (must be false/error per semantics)
+                {Compare.eq,  p(1, 2), asList(1, 2), false},
+                {Compare.neq, p(1, 2), asList(1, 2), true},
+                {Compare.lt,  p(1, 2), asList(1, 2), false},
+                {Compare.gt,  p(1, 2), asList(1, 2), false},
         }));
         // Compare Numbers of mixed types.
         final List<Object> one = Arrays.asList(1, 1l, 1d, 1f, BigDecimal.ONE, 
BigInteger.ONE);
@@ -433,4 +546,12 @@ public class CompareTest {
 
     static class D extends B {
     }
+
+    private static Path p(final Object... objects) {
+        final Path path = MutablePath.make();
+        for (final Object obj : objects) {
+            path.extend(obj, Collections.emptySet());
+        }
+        return path;
+    }
 }

Reply via email to