Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9ca2f63
Add runtime child-traversal resolution to predicates
xiazcy Jun 12, 2026
c661ea6
Add traversal-accepting has/hasLabel/is/V/E/property/where steps and …
xiazcy Jun 12, 2026
fd6daf4
Add grammar support for nestedTraversal arguments
xiazcy Jun 12, 2026
8341aaa
Add traversal-accepting arguments to the GLVs
xiazcy Jun 12, 2026
83fed48
Add feature conformance tests and documentation for traversal-accepti…
xiazcy Jun 12, 2026
7204dce
Add predicate traversal restriction inside choose().option(). Elimina…
xiazcy Jun 12, 2026
b5690c5
Refactor P class per PR review feedback
Cole-Greer Jun 16, 2026
ff22cd6
Use hasStepOfAssignableClassRecursively in ChildTraversalValidator
Cole-Greer Jun 17, 2026
bb81565
Collapse within/without traversal overloads
Cole-Greer Jun 17, 2026
c84fc23
Interleave traversal overloads with their literal counterparts
Cole-Greer Jun 17, 2026
6131811
Remove handleContainsVarargs and helpers
Cole-Greer Jun 17, 2026
c0220c2
Add TraversalParentTest cases for predicate child traversals
Cole-Greer Jun 17, 2026
cfdf1c8
Add hasKey(Traversal) and hasValue(Traversal) overloads
Cole-Greer Jun 17, 2026
e0d8440
Fix clone() bugs and add clone independence assertions to TraversalPa…
Cole-Greer Jun 18, 2026
9e72624
regen feature tests
Cole-Greer Jun 18, 2026
79b6fca
Fix WherePredicateStep for mixed ConnectiveP with traversal and scope…
Cole-Greer Jun 18, 2026
92d43f1
Rename ChildTraversal* to ReadOnlyChild* for clarity
Cole-Greer Jun 18, 2026
f6f8678
Rename AcceptsChildPredicateTraversal to ReadOnlyTraversalParent
Cole-Greer Jun 18, 2026
a1d54b9
Add HasKey/HasValue/HasLabel traversal overloads to .NET GLV
Cole-Greer Jun 18, 2026
8a8d6e0
Address PR documentation review feedback
Cole-Greer Jun 19, 2026
758d4b2
Add semantics doc entries for traversal-accepting steps
Cole-Greer Jun 19, 2026
cee62e6
Address code review comments: javadoc, error messages, tests
Cole-Greer Jun 22, 2026
688af16
Integrate *Traversal.feature tests into parent step files
Cole-Greer Jun 22, 2026
5dafaff
Expand test coverage for traversal-bearing predicates
Cole-Greer Jun 23, 2026
87d0763
Consolidate PTraversalTest into PTest
Cole-Greer Jun 23, 2026
88d3c14
Address remaining doc and test review comments
Cole-Greer Jun 23, 2026
a823854
Fix test failures in PTest and InlineFilterStrategyTest
Cole-Greer Jun 23, 2026
8466682
Fix feature file parse errors and complete sack() test
Cole-Greer Jun 23, 2026
3cc156f
Add MidVNotSupported tag to V(traversal)/E(traversal) start-step scen…
Cole-Greer Jun 23, 2026
da3721b
Fix within/without multi-traversal semantics to match literal forms
Cole-Greer Jun 23, 2026
2fb9282
Remove redundant asAdmin() from child traversals in predicate tests
Cole-Greer Jun 23, 2026
5d7bec5
Add simulated console output to upgrade doc examples
Cole-Greer Jun 23, 2026
38d9279
Fold has(traversal) examples into the main has() example block
Cole-Greer Jun 24, 2026
8060216
Add richer traversal-argument examples to is() and has() docs
Cole-Greer Jun 24, 2026
8ed3b71
Merge reference cross-links into the See: block (upgrade doc)
Cole-Greer Jun 24, 2026
e5fa8de
Add strategy scaffolding and complete GraphComputer tag audit
Cole-Greer Jun 24, 2026
24165f3
Revert withStrategies scaffolding for ReadOnlyChildVerificationStrategy
Cole-Greer Jun 24, 2026
71279de
Register ReadOnlyChildVerificationStrategy as a user-facing strategy
Cole-Greer Jun 24, 2026
4285557
Update docs to reflect traversal-accepting steps and predicates
Cole-Greer Jun 24, 2026
30fa2d0
Fold ReadOnlyChildVerificationStrategy into StandardVerificationStrategy
Cole-Greer Jun 26, 2026
c726e46
Align new step entries to current gremlin-semantics conventions CTR
spmallette Jun 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Added explicit transaction support to all non-Java GLVs (gremlin-python, gremlin-go, gremlin-javascript, gremlin-dotnet).
* Changed default transaction close behavior from commit to rollback across all GLVs to align with embedded graph defaults.
* Refactored Go driver connection to block until response headers arrive, enabling synchronous error returns and proper transaction ordering.
* Added child traversal support to `has()`, `hasLabel()`, `V()`, `E()`, `property()`, `is()`, `where(P)`, `P.eq/neq/gt/lt/gte/lte/within/without()`, and `TextP` predicates. Child traversals are resolved per-traverser at runtime, enabling dynamic filtering and lookup patterns.
* Added multi-traversal `P.within(trav1, trav2, ...)` and `P.without(trav1, trav2, ...)` which combine results from multiple child traversals for collection membership testing.
* Added `V(traversal)` and `E(traversal)` as start steps with synthetic traverser seeding, consistent with `mergeV(traversal)` behavior.
* Added `ReadOnlyChildVerificationStrategy` that blocks mutating steps (`addV`, `addE`, `drop`, etc.) inside child traversals. All child traversals must be read-only.
* Added `hasLabel(Traversal)` overload with grammar support for dynamic label filtering.
* Added traversal-bearing predicate support to `where(P)` - resolves child traversal and tests against current value when `P.hasTraversal()` is true.
* Added rejection of traversal-bearing predicates in `choose(P)` and `choose().option(P, ...)` - the predicate's child traversal cannot be resolved in the branch-selection context.
* Added runtime Map validation for `property(traversal)` - rejects results that are not a `Map` of property key/value pairs.
* Added mixed traversal/literal detection in `P.within()` and `P.without()` - throws `IllegalArgumentException` with guidance to wrap literals in `__.constant()`.
* Removed `uuid` dependency from `gremlin-javascript` in favor of the built-in `globalThis.crypto.randomUUID()`.
* Added streaming HTTP response support to `gremlin-driver` for incremental result deserialization over GraphBinary.
* Connected HTTP streaming response deserialization to the traversal API in `gremlin-javascript`, enabling `next()` to return the first result without waiting for the full response.
Expand Down
407 changes: 407 additions & 0 deletions docs/src/dev/provider/gremlin-semantics.asciidoc

Large diffs are not rendered by default.

57 changes: 0 additions & 57 deletions docs/src/recipes/anti-patterns.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -266,60 +266,3 @@ g.V().choose(label).
<1> Note, that tokens use a `fold()` reducer by default.
<2> `by("name")` doesn't use a token, but falls into the same category as the String `"name"` is translated into an optimized traversal.

[has-traversal]
== has() and Traversal Arguments

There is an understandable assumption that the `has(String,Traversal)` overload indicates that the value returned by
the `Traversal` argument will be used as the comparative value for the specified property key. There are often similar
assumptions that values of `P` can take a `Traversal` argument to achieve a similar end as in
`has(String, eq(Traversal))`. Unfortunately, neither of these work as assumed.

Starting with the latter issue of `P` and `Traversal` it should be noted that while `P` values take `Object` and thus
a `Traversal` it does not mean the `Traversal` will be resolved to a result that will be comparable. `P` will rather
do a compare on the raw `Traversal` object which of course will always return `false` (unless for some odd reason you
happen to store that `Traversal` object in your graph):

[gremlin-groovy,modern]
----
g.V().has('name', eq(constant('josh')))
eq(constant('josh'))
----

As for the former issue with `has(String,Traversal)`, this requires a bit more explanation. The `Traversal` object is
meant to be treated as a `Predicate`, meaning that if it returns a value the `has()` will allow the traverser to pass:

[gremlin-groovy,modern]
----
g.V().has('name', constant('josh')) <1>
g.V().has('name', constant('josh').is('xyz')) <2>
----

<1> `constant()` always returns a value so all vertices pass through the `has()`
<2> By adding `is()` this `Traversal` will no longer return a value so no vertices pass through the `has()`

These examples are a bit contrived for sake of demonstration, but the common pattern folks attempt appears as follows:

[gremlin-groovy,modern]
----
g.withSideEffect('x',['name': 'josh']).V().has('name', select('x').select('name'))
----

The above example represents a commonly seen mistake where we try to dynamically inject the value "josh" from a
`Map` stored in a side-effect named "x". As we can see, since `select('x').select('name')` returns a value the `has()`
succeeds for every single vertex which is unexpected. The correct way to do this dynamic injection is with `where()`
as in the following example:

[gremlin-groovy,modern]
----
g.withSideEffect('x',['name': 'josh']).V().as('a').where('a',eq('x')).by('name')
----

As a final note on this topic, it's worth noting how `has(String,Traversal)` can be used. Note that the traverser that
starts the `Traversal` argument is the `Property` value being compared. Therefore, if we wanted to find all the
vertices that had the "name" of "josh" we would do:

[gremlin-groovy,modern]
----
g.V().has('name', is('josh'))
----

35 changes: 16 additions & 19 deletions docs/src/recipes/traversal-induced-values.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,30 @@ g.V().has('name','marko').out('knows').has('age', gt(29)).values('name')
In plain language, the above Gremlin asks, "What are the names of the people who Marko knows who are over the age of
29?". In this case, "29" is known as a constant to the traversal. Of course, if the question is changed slightly to
instead ask, "What are the names of the people who Marko knows who are older than he is?", the hardcoding of "29" will
no longer suffice. There are multiple ways Gremlin would allow this second question to be answered. The first is
obvious to any programmer - use a variable:
no longer suffice. Rather than computing Marko's age in a separate query and passing it in as a variable, the value
can be _induced_ from the traversal itself by supplying a child `Traversal` to the predicate. The child traversal is
resolved for each traverser and its first result is used as the comparison value, so Marko's age is queried once and
then applied to filter the people he knows:

[gremlin-groovy,modern]
----
vMarko = g.V().has('name','marko').next()
g.V(vMarko).out('knows').has('age', gt(marko.value('age'))).values('name')
----

The downside to this approach is that it takes two separate traversals to answer the question. Ideally, there should
be a single traversal, that can query "marko" once, determine his `age` and then use that for the value supplied to
filter the people he knows. In this way the _value_ for the `age` in the `has()`-filter is _induced_ from the `Traversal`
itself.

[gremlin-groovy,modern]
----
g.V().has('name','marko').as('marko'). <1>
out('knows').as('friend'). <2>
where('friend', gt('marko')).by('age'). <3>
values('name') <4>
g.V().has('name','marko').as('marko'). <1>
out('knows'). <2>
has('age', gt(__.select('marko').values('age'))). <3>
values('name') <4>
----

<1> Find the "marko" `Vertex` and label it as "marko".
<2> Traverse out on the "knows" edges to the adjacent `Vertex` and label it as "friend".
<3> Continue to traverser only if Marko's current friend is older than him.
<2> Traverse out on the "knows" edges to Marko's friends.
<3> Keep a friend only if their `age` is greater than Marko's. The child traversal `__.select('marko').values('age')`
resolves Marko's age from the labelled "marko" vertex and `gt()` compares each friend's `age` against it.
<4> Get the name of Marko's older friend.

The child traversal supplied to a predicate must be read-only; mutating steps such as `addV()` or `property()` are
rejected. See the link:https://tinkerpop.apache.org/docs/x.y.z/reference/#a-note-on-predicates[A Note on Predicates]
and link:https://tinkerpop.apache.org/docs/x.y.z/reference/#has-step[has()] sections of the reference documentation
for more detail on traversal-bearing predicates.

As another example of how traversal induced values can be used, consider a scenario where there was a graph that
contained people, their friendship relationships, and the movies that they liked.

Expand Down
Loading
Loading