blob: c7a6f94dd5b4b503b5f657734171e8e4114bb1ca [file]
////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
////
[[tiny-gremlin]]
= Tiny Gremlin
image:tiny-gremlin.png[]
== Introduction
*Tiny Gremlin* is a defined subset of the Gremlin traversal language intended for local, in-process graph traversal
against small, detached `Graph` instances. A common use case would be to take the result of a `subgraph()` call and then
locally traverse it with Tiny Gremlin. It specifies which steps must be supported, the precise semantics of each step
within the local execution model, and any deviations from the full Gremlin standard that implementors must be aware of.
=== Motivation
The primary use case for Tiny Gremlin is post-processing of subgraphs received from a remote graph server without
requiring an additional network round-trip. A user retrieves a subgraph, holds it in memory as a `Graph` object, and
then navigates and filters it locally using familiar Gremlin syntax. A secondary use case is constructing small graphs
entirely in process for lightweight analysis or testing, using `addV()`, `addE()`, and `property()`.
Because the graph is small and entirely in memory, features that are expensive or architecture-dependent in a full
implementation, such as distributed OLAP steps, aggregation barriers, and strategy-based optimizations, are
deliberately excluded. The result is a small, self-contained, predictable execution model.
=== Supported Steps
Tiny Gremlin supports exactly the 39 steps listed below, grouped by category. No other step may be used in a Tiny
Gremlin traversal; doing so results in an error before any graph data is accessed.
[width="100%",options="header"]
|===
|Category |Steps
|Source |`V()`, `E()`
|Navigation |`out()`, `in()`, `both()`, `outE()`, `inE()`, `bothE()`, `outV()`, `inV()`, `otherV()`
|Filter |`has()`, `hasId()`, `hasLabel()`, `hasNot()`, `is()`
|Value Extraction |`id()`, `label()`, `values()`, `valueMap()`, `elementMap()`, `value()`, `key()`
|Path |`path()`
|Range |`limit()`, `range()`, `skip()`, `tail()`
|Ordering |`order()`
|Branch |`repeat()`, `loops()`
|Mutation |`addV()`, `addE()`, `property()`
|Modulators |`from()`, `to()`, `times()`, `until()`, `emit()`
|===
=== Terminal Methods
Tiny Gremlin must support the standard `toList()`, `next()`, and `iterate()` terminal methods, but should offer
language-specific terminals designed to help with stream processing ergonomics. For example, in Javascript, TinkerPop
offers:
* `forEach(fn)` — calls `fn` for each result element; returns `Promise<void>`.
* `reduce(fn, seed)` — folds all results into a single value using `fn` and an initial `seed`; returns `Promise<T>`.
Since Tiny Gremlin lacks steps needed for data transformation and filtering, these added steps will allow users to
express post-processing functions on the result set.
== Step Semantics
The following sub-sections describe each supported step, its relationship to the full Gremlin Semantics definition,
and any limitations or behavioral differences that apply specifically to Tiny Gremlin.
=== addE()
*Gremlin Semantics reference:* <<adde-step,addE()>>
Creates a new edge in the local `Graph` between vertices specified by `from()` and `to()` modulators.
*Tiny Gremlin limitations:*
* Only the `addE(String edgeLabel)` form is supported. The `addE(Traversal)` label-traversal form is not supported
because the traversal would need to reference prior labeled elements via `as()` and `select()`, neither of which
is in the Tiny Gremlin step set.
* `from()` and `to()` must use the anonymous traversal form (e.g. `from(__.V(id))`). The step-label form
(`from("stepLabel")`) is not supported because `as()` is not in the Tiny Gremlin step set. The GLV convenience
sugar `from_(vertexObject)` does not work because the GLV converts the `Vertex` to its raw ID before
serialization, producing `from(id)` which is not a valid grammar form and results in a parse error.
* The vertex referenced by `from()` or `to()` must exist in the local `Graph` at execution time. If the ID is not
found, an error is raised. No vertex is created implicitly.
=== addV()
*Gremlin Semantics reference:* <<addv-step,addV()>>
Creates a new vertex in the local `Graph`.
*Tiny Gremlin limitations:*
* No cardinality-aware property form (`property(Cardinality, key, value)`) is supported; only the simple
`property(key, value)` form applies to vertices created by `addV()`.
* Changes are local only; the remote graph is not modified.
=== both()
*Gremlin Semantics reference:* <<both-step,both()>>
Maps a vertex to its adjacent vertices, traversing both incoming and outgoing edges.
=== bothE()
*Gremlin Semantics reference:* <<bothe-step,bothE()>>
Maps a vertex to its incident edges, both incoming and outgoing.
=== E()
*Gremlin Semantics reference:* <<e-step,E()>>
Produces edges from the local graph. When called with no arguments, all edges are produced. When called with one or
more IDs, only matching edges are produced. A single list argument is also accepted.
=== elementMap()
*Gremlin Semantics reference:* <<elementmap-step,elementMap()>>
Maps an element to a `Map` containing its `T.id`, `T.label`, and property key-value pairs. For edges,
`Direction.OUT` and `Direction.IN` keys are included as stub maps containing `T.id` and `T.label` of the adjacent
vertices.
*Tiny Gremlin limitations:*
* `by()` modulation is not supported.
=== has()
*Gremlin Semantics reference:* <<has-step,has()>>
Filters elements by property existence, equality, or predicate.
Supported forms:
* `has(key)` — element has the property.
* `has(key, value)` — element has the property with the given value.
* `has(key, P)` — element has the property satisfying the predicate.
* `has(label, key, value)` — element has the given label and the property with the given value.
* `has(label, key, P)` — element has the given label and the property satisfying the predicate.
Supported `P` predicates: `eq`, `neq`, `lt`, `lte`, `gt`, `gte`, `between`, `inside`, `outside`, `within`,
`without`, `not`. Supported `TextP` predicates: `containing`, `notContaining`, `startingWith`, `notStartingWith`,
`endingWith`, `notEndingWith`, `regex`, `notRegex`.
The ordering predicates (`lt`, `lte`, `gt`, `gte`, `between`, `inside`, `outside`) follow the
<<gremlin-semantics-concepts,Gremlin Comparability semantics>>: cross-type comparisons and comparisons involving
`NaN` return `false` (the comparability ERROR result).
*Tiny Gremlin limitations:*
* Only `T.id` and `T.label` are supported as key tokens. `T.key` and `T.value` are not supported because
`properties()` is not in the Tiny Gremlin step set and there is no other way to produce `Property` traversers.
=== hasId()
*Gremlin Semantics reference:* <<hasid-step,hasId()>>
Filters elements by their ID.
*Tiny Gremlin limitations:*
* `hasId([1, 2, 3])` — a single list argument — is unrolled and treated as `hasId(1, 2, 3)`, consistent with
`V([1,2,3])` semantics.
* When more than one argument is provided, each argument is treated as a distinct ID value. A list in that position
is compared as-is and will not match any scalar ID (e.g. `hasId(1, [2, 3])` matches only id `1`). This
intentionally differs from the standard `hasId()` implementation which unrolls lists in all positions (see
TINKERPOP-2863).
=== hasKey()
*Gremlin Semantics reference:* <<haskey-step,hasKey()>>
Filters `Property` traversers by key. Operates on the output of `properties()`.
=== hasLabel()
*Gremlin Semantics reference:* <<haslabel-step,hasLabel()>>
Filters elements by label.
=== hasNot()
*Gremlin Semantics reference:* <<hasnot-step,hasNot()>>
Filters elements by the absence of a property.
=== hasValue()
*Gremlin Semantics reference:* <<hasvalue-step,hasValue()>>
Filters `Property` traversers by value. Operates on the output of `properties()`.
=== id()
*Gremlin Semantics reference:* <<id-step,id()>>
Maps an element to its ID.
=== in()
*Gremlin Semantics reference:* <<in-step,in()>>
Maps a vertex to its adjacent incoming vertices.
=== inE()
*Gremlin Semantics reference:* <<ine-step,inE()>>
Maps a vertex to its incoming edges.
=== inV()
*Gremlin Semantics reference:* <<inv-step,inV()>>
Maps an edge to its incoming vertex (the head of the arrow).
=== is()
*Gremlin Semantics reference:* <<is-step,is()>>
Filters the stream, keeping only traversers whose value equals the given object or satisfies the given predicate, for
example `is(32)` or `is(P.gte(29))`. It pairs naturally with `loops()` to bound a `repeat()`, as in
`until(__.loops().is(2))`.
=== key()
*Gremlin Semantics reference:* <<key-step,key()>>
Maps a `Property` traverser to its key.
=== label()
*Gremlin Semantics reference:* <<label-step,label()>>
Maps an element to its label.
=== limit()
*Gremlin Semantics reference:* <<limit-step,limit()>>
Truncates the result stream to at most `n` elements.
*Tiny Gremlin limitations:*
* The `Scope` form is not supported and will raise an error.
=== loops()
*Gremlin Semantics reference:* <<loops-step,loops()>>
Maps each traverser to the loop count of the nearest enclosing `repeat()` — zero on the first body pass, incrementing
each iteration. It is most often used as an exit condition with `is()`, so `repeat(traversal).until(__.loops().is(2))`
is equivalent to `repeat(traversal).times(2)`. Outside any `repeat()` it is zero.
*Tiny Gremlin limitations:*
* The `loops("label")` form, which references a labeled loop, is not supported because Tiny Gremlin has no step labels.
=== order()
*Gremlin Semantics reference:* <<order-step,order()>>
Sorts all elements.
Supports a single `by()` modulator with the following forms:
* `by(String key)` — sort by property value ascending.
* `by(String key, Order)` — sort by property value with explicit direction (`Order.asc` or `Order.desc`).
* `by(T.id)` / `by(T.id, Order)` and `by(T.label)` / `by(T.label, Order)` — sort by the element's ID or label
with optional direction.
* `by(Order)` — sort by natural element value with explicit direction.
* `by()` — natural ascending sort (same as omitting `by()`).
*Tiny Gremlin limitations:*
* Only a single `by()` modulator is supported per `order()`.
* Ordering follows the <<gremlin-semantics-orderability,Gremlin Orderability semantics>>, providing a total order
across all types: `null` < `Boolean` < `Number` < `Date` < `String` < `Vertex` < `Edge` < `VertexProperty`
< `Property` < `Path` < `Set` < `List` < `Map` < `Unknown`. `NaN` is ordered after `+Infinity` within the
numeric type space.
* The `Scope` form (`order(Scope)`) is not supported and will raise an error.
* `by(Traversal)`, `by(Traversal, Order)`, and `by(Function)` are not supported and will raise an error.
=== otherV()
*Gremlin Semantics reference:* <<otherv-step,otherV()>>
Maps an edge to the vertex that was not the vertex traversed from.
=== out()
*Gremlin Semantics reference:* <<out-step,out()>>
Maps a vertex to its adjacent outgoing vertices.
=== outE()
*Gremlin Semantics reference:* <<oute-step,outE()>>
Maps a vertex to its outgoing edges.
=== outV()
*Gremlin Semantics reference:* <<outv-step,outV()>>
Maps an edge to its outgoing vertex (the tail of the arrow).
=== path()
*Gremlin Semantics reference:* <<path-step,path()>>
Maps the current traverser to the `Path` of elements it has visited.
`by()` modulation is supported to project each element in the path. Multiple `by()` modulators are applied
round-robin across the path elements (the first element uses the first `by()`, the second the second, and so on,
cycling back to the first). The following forms are supported:
* `by(String)` — project the named property value (the first value for multi-properties).
* `by(T)` — project `T.id` or `T.label`.
* `by()` — natural identity (the raw element), equivalent to omitting `by()` for that position.
* `by(Traversal)` — restricted to a *single value-extraction step*: `id()`, `label()`, `key()`, `value()`,
`values()`, `valueMap()`, or `elementMap()`. `values()` projects the first value. Any other traversal shape
raises an error.
A non-productive `by()` (for example, `by("age")` on an element without an `age` property) filters the whole
path traverser, matching default Gremlin semantics. `ProductiveByStrategy` is not supported, so there is no
null-producing variant.
*Tiny Gremlin limitations:*
* `from()` and `to()` selection within a path is not supported because `as()` step labels are excluded from Tiny
Gremlin.
=== property()
*Gremlin Semantics reference:* <<property-step,property()>>
Sets a property on the current element.
*Tiny Gremlin limitations:*
* Only the `property(key, value)` two-argument form is supported. The cardinality form
`property(Cardinality, key, value)` and the map form `property(map)` are not supported.
* For vertices, an existing property with the same key is replaced (single cardinality semantics regardless of graph
configuration).
=== range()
*Gremlin Semantics reference:* <<range-step,range()>>
Emits elements at positions `[low, high)` in the stream, zero-indexed.
*Tiny Gremlin limitations:*
* The `Scope` form is not supported and will raise an error.
=== repeat()
*Gremlin Semantics reference:* <<repeat-step,repeat()>>
Repeatedly applies a child traversal to each traverser, forming a loop. The loop is bounded by a `times()` or
`until()` modulator, and `emit()` may side-output traversers as they pass through the loop. The position of a
modulator relative to `repeat()` determines its semantics, matching full Gremlin:
* `repeat(traversal).times(n)` runs the loop body `n` times. Declared after `repeat()`, the body always executes at
least once, so `times(0)` still runs the body once. Declared before, as in `times(n).repeat(traversal)`, the count
is checked first, so `times(0)` emits the input unchanged without running the body.
* `repeat(traversal).until(condition)` is do-while: the body runs and then the condition is tested. Reversing the
order to `until(condition).repeat(traversal)` is while: the condition is tested before the body.
* `emit()` side-outputs every traverser, and `emit(condition)` side-outputs only those satisfying the condition.
Declared after `repeat()` it emits traversers leaving the body, and declared before it emits them entering the body.
A `condition` for `until()` or `emit()` is an anonymous filter traversal, satisfied when it produces at least one
result. The loop body and these conditions may themselves contain any supported step, including `order().by()`,
`path()`, or `loops()` — so `until(__.loops().is(2))` bounds the loop by iteration count, equivalent to `times(2)`.
The loop is driven breadth-first over the whole frontier: at each loop iteration every surviving traverser advances
in lockstep through one shared evaluation of the body. A barrier step such as `order().by()` inside the body
therefore sees all traversers at that loop position and orders them globally, matching full Gremlin rather than
ordering within the descendants of a single input.
Nested `repeat()` is supported. A `repeat()` may appear inside another `repeat()`, whether in the loop body or in
an `until()` or `emit()` condition, to any depth. For example,
`repeat(__.out().repeat(__.in()).times(2)).until(__.hasId(10))` is valid, and the global barrier behaviour above is
preserved through the nesting, so `order().by()` within an inner `repeat()` still orders the whole frontier.
*Tiny Gremlin limitations:*
* The loop-label form (`repeat("label", traversal)`) is not supported because `as()`-style step labels and the
`loops("label")` reference are excluded from Tiny Gremlin.
* The predicate form (`until(P)` and `emit(P)`) is not supported. It corresponds to Java's `Predicate<Traverser>`
lambda overload rather than a value predicate, so the anonymous-traversal form (for example
`until(__.has("name", "ripple"))`) must be used instead.
* Mutation steps (`addV()`, `addE()`, `property()`) are not permitted within a `repeat()` body or its `until()` and
`emit()` conditions, since mutating the graph while looping over it has no well-defined semantics. This is enforced
at any nesting depth, including inside a nested `repeat()`.
* A `repeat()` with no `times()` or `until()` bound terminates only when its body stops producing traversers, as on
an acyclic graph. As in full Gremlin, an unbounded loop over cyclic data does not terminate, so a bound is the
caller's responsibility.
=== skip()
*Gremlin Semantics reference:* <<skip-step,skip()>>
Discards the first `n` elements of the stream.
*Tiny Gremlin limitations:*
* The `Scope` form is not supported and will raise an error.
=== tail()
*Gremlin Semantics reference:* <<tail-step,tail()>>
Emits the last `n` elements of the stream. This is a barrier step.
*Tiny Gremlin limitations:*
* The `Scope` form is not supported and will raise an error.
=== V()
*Gremlin Semantics reference:* <<v-step,V()>>
Produces vertices from the local graph. When called with no arguments, all vertices are produced. When called with one
or more IDs, only matching vertices are produced. A single list argument is also accepted.
=== value()
*Gremlin Semantics reference:* <<value-step,value()>>
Maps a `Property` traverser to its value.
=== valueMap()
*Gremlin Semantics reference:* <<valueMap-step,valueMap()>>
Maps an element to a `Map` of property keys to lists of property values.
*Tiny Gremlin limitations:*
* `by()` modulation is not supported.
* `with(WithOptions.tokens)` configuration is not supported; use `valueMap(true)` instead.
=== values()
*Gremlin Semantics reference:* <<values-step,values()>>
Maps an element to the values of its properties.