This is an automated email from the ASF dual-hosted git repository. colegreer pushed a commit to branch 3.8.0-GameDay-Snapshot in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 612c4c464ba1bd699abf82d4c58ab9b5a94d89b5 Author: Cole-Greer <[email protected]> AuthorDate: Mon Oct 20 23:16:07 2025 -0700 add docs for local() changes --- docs/src/dev/provider/gremlin-semantics.asciidoc | 34 ++++++++++++++++++++++++ docs/src/reference/the-traversal.asciidoc | 23 +++------------- docs/src/upgrade/release-3.8.x.asciidoc | 28 +++++++++++++++++++ 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc index e36fa984c2..eeb331e61b 100644 --- a/docs/src/dev/provider/gremlin-semantics.asciidoc +++ b/docs/src/dev/provider/gremlin-semantics.asciidoc @@ -1589,6 +1589,40 @@ See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/j link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthLocalStep.java[source (local)], link:https://tinkerpop.apache.org/docs/x.y.z/reference/#length-step[reference] +[[local-step]] +=== local() + +*Description:* Executes the provided traversal in an object-local manner. + +*Syntax:* `local(Traversal localTraversal)` + +[width="100%",options="header"] +|========================================================= +|Start Step |Mid Step |Modulated |Domain |Range +|N |Y |N |`any` |`any` +|========================================================= + +*Arguments:* + +* `localTraversal` - The traversal that processes each single-object traverser individually. + +*Modulation:* + +None + +*Considerations:* + +The `local()` step enforces object-local execution. As a branching step with local children, it implements strict lazy +evaluation by passing a single traverser at a time to the local traversal (bulk of exactly one, if bulking is supported) +and resetting the traversal to clean state between executions. + +Under lazy evaluation, each traverser must complete its full journey through all steps in the local traversal before +the next traverser begins processing. This creates a sequential, pull-based execution model where each step processes +one traverser completely before requesting the next. + +See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LocalStep.java[source], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#local-step[reference] + [[intersect-step]] === intersect() diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index 1bbb336560..f136bdba4e 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -2557,30 +2557,15 @@ in an object-local traversal. As such, the `order().by()` and the `limit()` refe stream as a whole. Local Step is quite similar in functionality to <<general-steps,Flat Map Step>> where it can often be confused. -`local()` propagates the traverser through the internal traversal as is without splitting/cloning it. Thus, its -a “global traversal” with local processing. Its use is subtle and primarily finds application in compilation -optimizations (i.e. when writing `TraversalStrategy` implementations. As another example consider: +The primary distinction between these steps is that while `local()` preserves the path history of traversers as they +pass through its child traversal, `flatMap()` does not. As another example consider: [gremlin-groovy,modern] ---- -g.V().both().barrier().flatMap(groupCount().by("name")) -g.V().both().barrier().local(groupCount().by("name")) +g.V().local(outE().inV()).path() +g.V().flatMap(outE().inV()).path() ---- -Use of `local()` is often a mistake. This is especially true when its argument contains a reducing step. For example, -let's say the requirement was to count the number of properties per `Vertex` in: - -[gremlin-groovy,modern] ----- -g.V().both().local(properties('name','age').count()) <1> -g.V().both().map(properties('name','age').count()) <2> ----- - -<1> The output here seems impossible because no single vertex in the "modern" graph can have more than two properties -given the "name" and "age" filters, but because the counting is happening object-local the counting is occurring unique -to each object rather than each global traverser. -<2> Replacing `local()` with `map()` returns the result desired by the requirement. - WARNING: The anonymous traversal of `local()` processes the current object "locally." In OLAP, where the atomic unit of computing is the vertex and its local "star graph," it is important that the anonymous traversal does not leave the confines of the vertex's star graph. In other words, it can not traverse to an adjacent vertex's properties or edges. diff --git a/docs/src/upgrade/release-3.8.x.asciidoc b/docs/src/upgrade/release-3.8.x.asciidoc index 9392434f50..8508b949f7 100644 --- a/docs/src/upgrade/release-3.8.x.asciidoc +++ b/docs/src/upgrade/release-3.8.x.asciidoc @@ -554,6 +554,34 @@ compatibility. See: link:https://issues.apache.org/jira/browse/TINKERPOP-3161[TINKERPOP-3161] +==== Split bulked traversers for `local()` + +Prior to 3.8.0, the `local()` exhibited "traverser-local" semantics, where the local traversal would independently to +each individual Traverser. This often led to confusion, especially in the presence of reducing barrier steps as the +presence of bulked traversers would lead to multiple objects being processed at once. `local()` has been updated to +automatically split any bulked traversers and thus now exhibits true "object-local" semantics. + +[source,groovy] +---- +// 3.7.4 +gremlin> g.V().out().barrier().local(count()) +==>3 +==>1 +==>1 +==>1 + +// 3.8.0 +gremlin> g.V().out().barrier().local(count()) +==>1 +==>1 +==>1 +==>1 +==>1 +==>1 +---- + +See: link:https://issues.apache.org/jira/browse/TINKERPOP-3196[TINKERPOP-3196] + ==== Removal of P.getOriginalValue() `P.getOriginalValue()` has been removed as it was not offering much value and was often confused with `P.getValue()`.
