This is an automated email from the ASF dual-hosted git repository.
Cole-Greer pushed a commit to branch TINKERPOP-3256
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/TINKERPOP-3256 by this push:
new ac7fa00a5c Fold TinkerGQL variable-length path docs into existing
reference
ac7fa00a5c is described below
commit ac7fa00a5ce25480c9aa7b4862a96cf64e02f35c
Author: Cole Greer <[email protected]>
AuthorDate: Tue Oct 27 19:06:19 2026 -0700
Fold TinkerGQL variable-length path docs into existing reference
Replace the standalone variable-length path subsection with rows in the
existing Supported Syntax table, a short note, and one modern-graph
example. Move the CHANGELOG entry from the released 4.0.0-beta.3 section
to the unreleased 4.0.0 section and restore the beta.3 section.
Assisted-by: Kiro:claude-opus-5
---
CHANGELOG.asciidoc | 4 +-
docs/src/reference/the-traversal.asciidoc | 95 ++++++-------------------------
2 files changed, 19 insertions(+), 80 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index a49c6d9c36..6570cb7295 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -28,6 +28,7 @@
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Fixed `gremlin-go` to report a malformed or truncated GraphBinary response
as a deserialization error rather than a bare decoder message.
* Made `TinkerGraph` an interface and renamed the in-memory implementation to
`TinkerMemoryGraph`; `TinkerGraph.open()` and `gremlin.graph=...TinkerGraph`
behave as before. *(breaking)*
* Renamed `TinkerTransactionGraph` to `TinkerStorageGraph`. *(breaking)*
+* Added variable-length path patterns to TinkerGQL using quantified
relationships such as `(a)-[:knows]->{1,3}(b)`.
[[release-4-0-0-beta-3]]
=== TinkerPop 4.0.0-beta.3 (July 20, 2026)
@@ -125,8 +126,7 @@
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Deprecated the traversal-based `match(Traversal[])` in favor of the new
string-based API.
* Added the `gql-gremlin` module containing the TinkerGQL engine, a
`MATCH`-pattern executor implementing a deliberate minimal subset of ISO GQL,
usable by any graph provider.
* Added `Graph.countVerticesByLabel(String)`,
`Graph.countEdgesByLabel(String)`, and the `Graph.Index` nested interface as
performance-hint extension points for the TinkerGQL engine.
-* Added GQL support to TinkerGraph with `gql-gremlin`.
-* Added variable-length path patterns to TinkerGQL via the
quantified-relationship form `(a)-[r:label]->{m,n}(b)` (also `{m}`, `{m,}`, and
`+`), across directed, reverse, and undirected edges.
+* Added GQL support to TinkerGraph with `gql-gremlin`,
[[release-4-0-0-beta-2]]
=== TinkerPop 4.0.0-beta.2 (April 1, 2026)
diff --git a/docs/src/reference/the-traversal.asciidoc
b/docs/src/reference/the-traversal.asciidoc
index ff76be1b6a..f24f0571b3 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -3269,6 +3269,10 @@ The following `MATCH` patterns are supported:
|`-[:Label]-` |Labeled undirected edge
|`-[e:Label]-` |Named and labeled undirected edge
|`-[e:Label {key: 'value'}]-` |Named, labeled undirected edge with an inline
property filter
+|`-[e:Label]->{m,n}` |Quantified edge that matches a walk of `m` to `n`
repeated edges
+|`-[e:Label]->{m}` |Quantified edge that matches a walk of exactly `m`
repeated edges
+|`-[e:Label]->{m,}` |Quantified edge that matches a walk of `m` or more
repeated edges
+|`-[e:Label]->+` |Quantified edge that matches a walk of one or more repeated
edges, equivalent to `{1,}`
|=========================================================
Multiple comma-separated path patterns are supported in a single `MATCH`
clause. Patterns that share a variable
@@ -3276,6 +3280,10 @@ name are joined on that variable, which must resolve to
the same element across
clause — `match()` always returns the full binding `Map` and any projection is
performed with Gremlin steps like
`select()`.
+A quantifier may follow any directed, reverse, or undirected edge, as in
`<-[e:Label]-{m,n}` or
+`-[e:Label]-{m,n}`, and the quantified walk matches once at every length in
its range. A walk never repeats an
+edge, and a named edge variable binds to the ordered list of edges in the walk.
+
The following examples use the modern toy graph to demonstrate more complex
usage patterns.
*Find all software and the person who created it, traversing the edge in
reverse*
@@ -3312,6 +3320,14 @@ g.match("MATCH (a:person)-[e:knows]-(b:person)").
by("name").by("weight").by("name")
----
+*Find people who created the same software as marko, using a quantified
undirected edge to walk two `created` edges*
+
+[gremlin-groovy,modern]
+----
+g.match("MATCH (a:person {name: 'marko'})-[:created]-{2}(b:person)").
+ select("a", "b").by("name")
+----
+
The `match()` step becomes arguably preferable when the query involves a
multi-way join, where several paths
must all connect through shared variables, because the pattern expresses the
shape of the subgraph directly
rather than requiring the reader to trace variable bindings through a chain of
steps. For example, "find people
@@ -3450,83 +3466,6 @@ performs an index-backed seed lookup rather than a full
scan. Whether `Graph.Ind
structure depends on the graph system; see <<tinkerpop-providers-tinkergql,the
provider documentation>> and
your specific graph system's documentation for details.
-[[tinkergql-variable-length]]
-===== Variable-Length Path Patterns
-
-A relationship pattern can be quantified so that it matches a variable-length
walk of repeated edges of the same
-shape rather than a single hop. The quantifier is written immediately after
the closing arrow of the
-relationship, in the abbreviated form `(a)-[r:LABEL]->{q}(b)`. Four quantifier
forms are supported, each with a
-lower bound of at least one:
-
-[width="100%",cols="2,4",options="header"]
-|=========================================================
-|Quantifier |Meaning
-|`{m,n}` |Between `m` and `n` repetitions, inclusive
-|`{m}` |Exactly `m` repetitions
-|`{m,}` |At least `m` repetitions, unbounded above
-|`+` |One or more repetitions, equivalent to `{1,}`
-|=========================================================
-
-Quantification is available on all three edge directions. The examples below
use the modern toy graph, where the
-`knows` relationship forms only single-hop paths, so a bounded quantifier
there resolves to the same immediate
-neighbours as an unquantified edge:
-
-[gremlin-groovy,modern]
-----
-g.match("MATCH (a:person)-[:knows]->{1,2}(b:person)").
- select("a", "b").by("name")
-----
-
-The reverse form `<-[r:LABEL]-{q}` walks incoming edges, and the undirected
form `-[r:LABEL]-{q}` walks edges in
-either direction:
-
-[gremlin-groovy,modern]
-----
-g.match("MATCH (b:person)<-[:knows]-{1}(a:person)").
- select("a", "b").by("name")
-----
-
-*Semantics*
-
-A quantified relationship expands from its already-bound endpoint outward, one
hop at a time, up to the maximum
-repetition count. Three properties define the result set. First, expansion
follows *trail* semantics: within a
-single walk no edge is traversed more than once, which guarantees termination
even in the presence of cycles.
-Vertices, by contrast, may repeat when reached through distinct edges. Second,
expansion *emits at each depth*
-within the quantifier range. A walk that reaches a valid target at every depth
from the lower bound to the upper
-bound produces one result row per depth, not only at the final depth. Third,
the edge variable `r` binds as an
-ordered `List<Edge>`, a *group variable* holding every edge traversed on that
walk, in walk order. A result
-row's list has one element for a depth-one match, two for a depth-two match,
and so on.
-
-The following illustrates these semantics over a linear chain
`a-[route]->b-[route]->c-[route]->d`, which the
-modern graph does not contain. A `{1,3}` quantifier anchored at `a` emits `b`
at depth one, `c` at depth two, and
-`d` at depth three, with `r` accumulating the traversed edges at each depth:
-
-[source,text]
-----
-MATCH (a:airport {code: 'AAA'})-[r:route]->{1,3}(x:airport)
-
-// three result rows, one per depth:
-// x = BBB, r = [AAA->BBB]
-// x = CCC, r = [AAA->BBB, BBB->CCC]
-// x = DDD, r = [AAA->BBB, BBB->CCC, CCC->DDD]
-----
-
-A lower bound greater than one suppresses the shallower depths. Over the same
chain, `{2,}` emits only `c` and `d`,
-while `+` (equivalent to `{1,}`) emits `b`, `c`, and `d`.
-
-*Limitations*
-
-Quantified relationships in the current release carry three restrictions:
-
-. *Forward expansion only.* A quantified edge expands outward from one bound
endpoint to reach the other. A
-quantified edge whose endpoints are both already bound elsewhere in the
pattern, one that closes a
-pattern cycle, is not yet supported and raises an error at plan time. Only
unquantified edges may close a cycle.
-. *No zero-length forms.* Every quantifier must have a lower bound of at least
one. The zero-length forms `*`,
-`{0,n}`, and `{,n}` are not yet supported and are rejected when the query is
parsed or planned.
-. *Per-walk edge uniqueness only.* The trail (no-repeated-edge) guarantee
applies within a single quantified
-walk. Edge uniqueness across an entire multi-pattern match is not enforced, so
an edge traversed inside a
-quantified walk may also appear in another part of the same match.
-
===== Unsupported Features
TinkerGQL is a minimal subset of ISO GQL. The following ISO GQL `MATCH`
features are not yet implemented:
@@ -3537,7 +3476,7 @@ TinkerGQL is a minimal subset of ISO GQL. The following
ISO GQL `MATCH` features
|`WHERE` clause |Post-match filtering is not supported inside the `MATCH`
string. Use Gremlin's `where()` step after `match()` instead.
|`RETURN` clause |Projection inside the `MATCH` string is not supported. Use
`select()` after `match()` to project named variables.
|Inequality / range predicates |Only equality tests (`{key: value}`) are
supported. Expressions like `n.age > 30` are not part of the grammar.
-|Zero-length and cyclic quantified paths |Quantified path patterns with a
lower bound of at least one (`{m,n}`, `{m}`, `{m,}`, `+`) are supported (see
<<tinkergql-variable-length>>). Zero-length forms (`*`, `{0,n}`, `{,n}`) and a
quantified edge that closes a pattern cycle are not yet supported.
+|Some quantified paths |Zero-length quantifiers (`*`, `{0,n}`, `{,n}`) and
quantified edges that close a pattern cycle are not supported. Edge uniqueness
is enforced within each quantified walk rather than across the whole pattern.
|Multiple labels per element |`(n:Person:Employee)` — more than one label per
node pattern — is not supported.
|`OPTIONAL MATCH` |Optional pattern matching (outer joins) is not supported.
|Aggregation functions |`COUNT`, `SUM`, `AVG`, and similar functions are not
part of the grammar.