You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2026/07/20 12:17:21 UTC

(tinkerpop) branch master updated (1e0cb6f267 -> 2e15a7adbc)

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

spmallette pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


    from 1e0cb6f267 Behavioral test fixes and error-handling consistency across GLVs (#3519)
     new a75784e163 Fixed toString on Tree in Gremlin Console CTR
     new 2e15a7adbc Rewrote upgrade docs on Tree changes CTR

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 docs/src/upgrade/release-4.x.x.asciidoc            | 61 +++++++++++-----------
 .../gremlin/groovy/loaders/SugarLoader.groovy      |  7 +++
 .../gremlin/groovy/TreeSubscriptTest.groovy        |  9 ++++
 3 files changed, 46 insertions(+), 31 deletions(-)


(tinkerpop) 01/02: Fixed toString on Tree in Gremlin Console CTR

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a75784e1635a881cbcb1fcb319c50f4184f4de88
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Mon Jul 20 08:16:28 2026 -0400

    Fixed toString on Tree in Gremlin Console CTR
---
 .../apache/tinkerpop/gremlin/groovy/loaders/SugarLoader.groovy   | 7 +++++++
 .../org/apache/tinkerpop/gremlin/groovy/TreeSubscriptTest.groovy | 9 +++++++++
 2 files changed, 16 insertions(+)

diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/loaders/SugarLoader.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/loaders/SugarLoader.groovy
index 5f33f27723..7eb732d728 100644
--- a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/loaders/SugarLoader.groovy
+++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/loaders/SugarLoader.groovy
@@ -99,6 +99,13 @@ class SugarLoader {
         public static final Object getAt(final Tree tree, final Object key) {
             return tree.childAt(key);
         }
+
+        // Mixing this category into Tree.metaClass shadows Tree's own toString() with the
+        // category delegate's Object.toString(), which renders as "...$TreeCategory@<hash>".
+        // Reflectively invoke the real Tree.toString() so the console shows the nested nodes.
+        public String toString() {
+            return Tree.class.getMethod("toString").invoke(this.metaClass.owner);
+        }
     }
 
     public static class ElementCategory {
diff --git a/gremlin-groovy/src/test/groovy/org/apache/tinkerpop/gremlin/groovy/TreeSubscriptTest.groovy b/gremlin-groovy/src/test/groovy/org/apache/tinkerpop/gremlin/groovy/TreeSubscriptTest.groovy
index b2d068e2c4..ac2a489b73 100644
--- a/gremlin-groovy/src/test/groovy/org/apache/tinkerpop/gremlin/groovy/TreeSubscriptTest.groovy
+++ b/gremlin-groovy/src/test/groovy/org/apache/tinkerpop/gremlin/groovy/TreeSubscriptTest.groovy
@@ -53,6 +53,15 @@ class TreeSubscriptTest {
         assertTrue(tree['marko']['josh'].rootNodes().contains('lop'))
     }
 
+    @Test
+    public void shouldRenderNestedToStringViaSugar() {
+        def g = TinkerFactory.createModern().traversal()
+        Tree tree = g.V(1).out().out().tree().by('name').next()
+
+        // The mixin must not shadow Tree's toString() with the category delegate's identity string.
+        assertEquals('{marko={josh={ripple={}, lop={}}}}', tree.toString())
+    }
+
     @Test
     public void shouldThrowOnMissingSubscriptKey() {
         def g = TinkerFactory.createModern().traversal()


(tinkerpop) 02/02: Rewrote upgrade docs on Tree changes CTR

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 2e15a7adbc6ae847c3079e03297cae9bc51fbc7c
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Mon Jul 20 08:16:51 2026 -0400

    Rewrote upgrade docs on Tree changes CTR
---
 docs/src/upgrade/release-4.x.x.asciidoc | 61 ++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 31 deletions(-)

diff --git a/docs/src/upgrade/release-4.x.x.asciidoc b/docs/src/upgrade/release-4.x.x.asciidoc
index 0c2d71dc6d..2f41fab8ba 100644
--- a/docs/src/upgrade/release-4.x.x.asciidoc
+++ b/docs/src/upgrade/release-4.x.x.asciidoc
@@ -840,38 +840,37 @@ supports both forms via the `PDT("name",[map])` and `PDT("name","value")` litera
 See: link:https://tinkerpop.apache.org/docs/4.0.0-beta.3/dev/provider/#provider-defined-types[Provider Defined Types],
 link:https://tinkerpop.apache.org/docs/4.0.0-beta.3/dev/provider/#primitive-provider-defined-types[Primitive Provider Defined Types]
 
+==== Improving Tree
 
-==== Tree No Longer Extends HashMap
-
-`Tree` no longer extends `HashMap`. It is now a `final` class that holds a `Map` internally and exposes a
-tree-shaped API instead of `Map` methods. This is a breaking change for code that treated a `tree()` result as a
-`Map`.
-
-Replacements for the common `Map`-based access patterns:
-
-[options="header"]
-|=======================
-|3.x (`Tree` as `Map`) |4.x (`Tree` API)
-|`tree.get(key)` |`tree.childAt(key)` (throws if absent) or `tree.findSubtree(key)` (recursive, returns `Optional`)
-|`tree.containsKey(key)` |`tree.hasChild(key)`
-|`tree.keySet()` |`tree.rootNodes()`
-|`tree.size()` |`tree.rootNodes().size()` for root entries, or `tree.nodeCount()` for total nodes
-|`getObjectsAtDepth(d)` |`getNodesAtDepth(d)` (now 0-based: depth 0 returns the roots)
-|`getLeafObjects()` |`getLeafNodes()`
-|=======================
-
-A few Gremlin patterns that worked only because `Tree` was a `Map` (for example `select(keys)` and `unfold()`
-applied to a `Tree`) no longer compose. Process the `Tree` result client-side after `next()`, or reshape the
-upstream traversal.
-
-`count(local)` on a `Tree` has changed semantics. Previously, because `Tree` was a `Map`, it returned the
-number of root entries; it now returns the total number of nodes in the tree (`Tree.nodeCount()`), consistent
-with how `count(local)` counts the contents of other local objects (for example the objects in a `Path`). Use
-`rootNodes().size()` on the materialized `Tree` if the root-entry count is required.
-
-`isLeaf()` no longer throws on an empty tree (it returns `true`), and a `Tree` keeps the long-standing limitation
-that sibling branches resolving to the same value collapse into one node; use `path()` or `subgraph()` when full
-path structure must be preserved.
+The `Tree` object in Java, returned from the `tree()` step was originally built as an extension to `HashMap`. There is
+a certain convenience to that approach, but at times the API did not always feel tree-like. When working with a tree
+structure, developers tend to be used to accessing a "child" or a "node" and with TinkerPop's `Tree` it was necessary
+to shift into thinking in `Map` semantics to navigate. In 4.0, `Tree` no longer extends from `HashMap` and exposes an
+API that is consistent with how developers are used to working with this type of data structure.
+
+The following shows an example of `Tree` access from versions prior to 4.0:
+
+[source,java]
+----
+Tree tree = g.V().out().out().tree().by("name").next();
+
+// 3.x
+Tree<String> marko = tree.get("marko");                     // {josh={ripple={}, lop={}}}
+Tree<String> josh  = tree.get("marko").get("josh");         // {ripple={}, lop={}}
+
+// 4.x
+Tree<String> marko = tree.childAt("marko");                 // {josh={ripple={}, lop={}}}
+Tree<String> josh  = tree.childAt("marko").childAt("josh"); // {ripple={}, lop={}}
+
+// 3.x
+List<String> depth  = tree.getObjectsAtDepth(3);            // [ripple, lop] (1-based depth)
+List<String> leaves = tree.getLeafObjects();                // [ripple, lop]
+
+// 4.x
+List<String> depth  = tree.getNodesAtDepth(2);              // [ripple, lop] (0-based depth)
+List<String> leaves = tree.getLeafNodes();                  // [ripple, lop]
+Optional<Tree<String>> found = tree.findSubtree("josh");    // Optional[{ripple={}, lop={}}]
+----
 
 See: link:https://lists.apache.org/thread/o0nqh6kmrkdht531655p351ldjll045d[[DISCUSS] Make Tree no longer extend HashMap]