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

spmallette pushed a commit to branch polecat/nux/ti-sgi@mntfr6jh
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit d9e450aae5efc1ccaa3e402181957bd03b4bbf75
Author: nux <[email protected]>
AuthorDate: Fri Apr 10 17:50:52 2026 -0400

    fix: replace Comparable cast in Traverser.compareTo() with 
GremlinValueComparator (TINKERPOP-3197)
    
    Fixes ClassCastException when order() or order(local) is called on Map.Entry
    objects (e.g. from unfold() on a groupCount result). Traverser.compareTo()
    previously cast the traverser's object to Comparable, which fails for types
    like HashMap$Node that don't implement Comparable.
    
    Replaced with GremlinValueComparator.ORDERABILITY.compare() which handles 
all
    Gremlin types including Map.Entry. Added Gherkin scenarios and a unit test 
to
    cover the fixed behavior. (ti-sgi)
---
 CHANGELOG.asciidoc                                 |  2 ++
 .../gremlin/process/traversal/Traverser.java       | 11 +++++-----
 .../traversal/step/map/OrderGlobalStepTest.java    | 14 ++++++++++++
 .../gremlin/test/features/map/Order.feature        | 25 +++++++++++++++++++++-
 4 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 032033ad5e..4ee4453a98 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,8 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 [[release-4-0-0]]
 === TinkerPop 4.0.0 (Release Date: NOT OFFICIALLY RELEASED YET)
 
+* Fixed `ClassCastException` thrown when calling `order()` or `order(local)` 
on `Map.Entry` objects produced by `unfold()` on grouped traversals. 
`Traverser.compareTo()` now uses `GremlinValueComparator.ORDERABILITY` to 
support all Gremlin types rather than requiring `Comparable` (TINKERPOP-3197).
+
 [[release-4-0-0-beta-2]]
 === TinkerPop 4.0.0-beta.2 (April 1, 2026)
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java
index 4c78d55f59..88595f887c 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal;
 
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.LoopsStep;
 import org.apache.tinkerpop.gremlin.structure.util.Attachable;
+import org.apache.tinkerpop.gremlin.util.GremlinValueComparator;
 
 import java.io.Serializable;
 import java.util.Collections;
@@ -132,20 +133,20 @@ public interface Traverser<T> extends Serializable, 
Comparable<Traverser<T>>, Cl
     }
 
     /**
-     * If the underlying object of the traverser is comparable, compare it 
with the other traverser.
+     * Compares this traverser with another traverser using {@link 
GremlinValueComparator#ORDERABILITY},
+     * which supports all Gremlin types including {@link java.util.Map.Entry}.
      *
-     * @param other the other traverser that presumably has a comparable 
internal object
+     * @param other the other traverser to compare against
      * @return the comparison of the two objects of the traversers
-     * @throws ClassCastException if the object of the traverser is not 
comparable
      */
     @Override
-    public default int compareTo(final Traverser<T> other) throws 
ClassCastException {
+    public default int compareTo(final Traverser<T> other) {
         final Object thisObj = this.get();
         final Object otherObj = other.get();
         if (thisObj == otherObj) return 0;
         if (null == thisObj) return -1;
         if (null == otherObj) return 1;
-        return ((Comparable) thisObj).compareTo(otherObj);
+        return GremlinValueComparator.ORDERABILITY.compare(thisObj, otherObj);
     }
 
     /**
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStepTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStepTest.java
index 9400e43d14..64958bdd34 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStepTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStepTest.java
@@ -26,9 +26,12 @@ import org.junit.Test;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.outE;
+import static org.junit.Assert.assertEquals;
 
 /**
  * @author Daniel Kuppitz (http://gremlin.guru)
@@ -64,4 +67,15 @@ public class OrderGlobalStepTest extends StepTest {
             __.inject(list).unfold().order().by(__.identity(), 
Order.shuffle).by().iterate();
         }
     }
+
+    @Test
+    public void shouldOrderMapEntriesWithoutBy() {
+        final Map<String, Long> map = new LinkedHashMap<>();
+        map.put("software", 2L);
+        map.put("person", 4L);
+        final List<Map.Entry> results = 
__.inject(map).unfold().order().toList();
+        assertEquals(2, results.size());
+        assertEquals("person", results.get(0).getKey());
+        assertEquals("software", results.get(1).getKey());
+    }
 }
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Order.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Order.feature
index c49c6fca14..eee8d1ef9a 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Order.feature
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Order.feature
@@ -548,4 +548,27 @@ Feature: Step - order()
     When iterated to list
     Then the result should be unordered
       | result |
-      | d[29].i |
\ No newline at end of file
+      | d[29].i |
+
+  Scenario: g_V_groupCount_byXlabelX_unfold_order
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().groupCount().by(T.label).unfold().order()
+      """
+    When iterated to list
+    Then the result should be ordered
+      | result |
+      | m[{"person":"d[4].l"}] |
+      | m[{"software":"d[2].l"}] |
+
+  Scenario: g_V_groupCount_byXlabelX_orderXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().groupCount().by(T.label).order(Scope.local)
+      """
+    When iterated to list
+    Then the result should be ordered
+      | result |
+      | m[{"person":"d[4].l","software":"d[2].l"}] |
\ No newline at end of file

Reply via email to