////
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.
////
[[gremlin-semantics]]
= Gremlin Semantics

image:tinkerpop-meeting-room.png[]

This section provides details on Gremlin language operational semantics. Describing these semantics and reinforcing
them with tests in the Gremlin test suite makes it easier for providers to implement the language and for the
TinkerPop Community to have better consistency in their user experiences. While the general Gremlin test suite offers
an integrated approach to testing Gremlin queries, the `@StepClassSemantics` oriented tests found
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features[here] are especially focused to the
definitions found in this section.

== Types

The Gremlin type system is built on a fixed set of types named by the `GType` enum. Users see these names via
`P.typeOf(GType)` and `asNumber(GType)`. This specification references types by the same names.

The primitive types are:

* Numerics (supertype `NUMBER`):
  ** `BYTE`, `SHORT`, `INT`, `LONG` (8/16/32/64-bit fixed-width signed integers)
  ** `BIGINT` (arbitrary-precision integer)
  ** `FLOAT`, `DOUBLE` (32-bit and 64-bit floating point, including `±Infinity` and `NaN`)
  ** `BIGDECIMAL` (arbitrary-precision decimal)
* `BOOLEAN`
* `STRING`, `CHAR`
* `UUID`
* `BINARY`
* `DATETIME`, `DURATION`
* `NULL` (has only one value, the "undefined" value `null`)

The collection types are `LIST`, `MAP`, and `SET`.

The graph types are:

* Graph elements: `VERTEX`, `EDGE`, `VPROPERTY`
* `PROPERTY` represents edge properties and meta-properties as a (Key, Value) pair where Key is `STRING` and Value
is any primitive type listed above
* `PATH` is an ordered sequence of graph elements traversed
* `TREE` is a hierarchical structure of graph elements
* `GRAPH` is an entire graph value

Providers can register custom types outside the TinkerPop type space using the `GlobalTypeCache`, making them available
for `P.typeOf(String)` filtering. Unregistered string inputs raise an `Argument Error`.

The `registerDataType()` method registers the class's simple name by default, or accepts a custom string name. Providers
should use PascalCase and prefix type names to avoid conflicts
(e.g., `ProviderPrefix:SimpleTypeName`).

The type cache only enables type recognition for filtering. Custom types still require separate serialization support.
Clients without custom serializers cannot deserialize these types.

Graph providers may not support all of these types depending on the architecture and implementation. Therefore
TinkerPop must provide a way for Graph providers to override the behavior while it has its own default behavior. Also
when some types are not supported Graph providers need to map unsupported types into supported types internally. This
mapping can be done in either information-preserving manner or non-preserving manner. Graph providers must tell which
mapping they support through `Graph.Features` as well as which types they support.

* Which primitive types are supported
  ** `BOOLEAN`, all integer types, all decimal types, `STRING`, `UUID` and `DATETIME`
  ** TinkerPop by default supports all of them
* Which integer types are supported
  ** TinkerPop by default supports `BYTE`, `SHORT`, `INT`, `LONG` and `BIGINT`
* Which float types are supported
  ** TinkerPop by default supports `FLOAT`, `DOUBLE`, and `BIGDECIMAL`

=== Numeric Type Promotion

TinkerPop performs type promotion (also known as type casting) for Numbers. The number types are `BYTE`, `SHORT`, `INT`, `LONG`,
`FLOAT`, `DOUBLE`, `BIGINT`, and `BIGDECIMAL`. In general, numbers are compared using semantic equivalence, without regard for
their specific type, e.g. `1 == 1.0`.

For comparisons numeric types are promoted as follows:

* First determine whether to use floating point or not. If any numbers in the comparison are floating point then we
convert all of them to floating point.
* Next determine the maximum bit size of the numerics being compared.
* If any floating point are present:
** If the maximum bit size is 32 (up to `INT`/`FLOAT`), we compare as `FLOAT`
** If the maximum bit size is 64 (up to `LONG`/`DOUBLE`), we compare as `DOUBLE`
** Otherwise we compare as `BIGDECIMAL`
* If no floating point are present:
** If the maximum bit size is 8 we compare as `BYTE`
** If the maximum bit size is 16 we compare as `SHORT`
** If the maximum bit size is 32 we compare as `INT`
** If the maximum bit size is 64 we compare as `LONG`
** Otherwise we compare as `BIGINT`

`BIGDECIMAL` and `BIGINT` may not be supported depending on the language and Storage, therefore the behavior of type
casting for these two types can vary depending on a Graph provider.

For numeric type mathematical operations (div/mul/add/sub), types are promoted as follows:

On math operations (when used in steps such as sum/sack) the highest common number class between
the two inputs is used. Any number being floating point forces a floating point result.
If an overflow occurs (either integer or floating-point), the method promotes the precision
by increasing the bit width, until a suitable type is found. If no suitable type exists
(e.g., for very large integers beyond 64-bit), an Arithmetic Exception is thrown.
For floating-point numbers, if a 64-bit float overflows, the result is `+Infinity`
or `-Infinity` instead of an exception.

[[gremlin-semantics-errors]]
== Errors

The Gremlin error model defines a small set of named error categories. Step `*Exceptions:*` sections refer to these
categories rather than to Java exception class names, since the categories are language-agnostic. Each GLV maps a
category onto its language's idiomatic error type, and remote responses surface the server message text (which
providers may localize while preserving the category).

[width="100%",cols="2,6,3,4",options="header"]
|=========================================================
|Category |Description |Java reference exception |Example message
|`Argument Error` |The caller supplied an invalid argument such as the wrong type, `null` where forbidden, an out-of-range value, or unparseable input. |`IllegalArgumentException`, `NumberFormatException` |"Format string for Format step can't be null."
|`State Error` |The traversal construction or runtime state is invalid for the operation being attempted, including a traversal rejected during strategy verification. |`IllegalStateException`, `VerificationException` |"The repeat()-traversal was not defined"
|`Arithmetic Error` |A numeric operation cannot produce a defined result, such as overflow during type narrowing or division by zero. |`ArithmeticException` |"long overflow"
|`Unsupported Operation` |The requested step, modulator, or option is not implemented or not supported by the current provider or execution mode. |`UnsupportedOperationException` |"Option Merge.outV for Merge is not supported"
|=========================================================

[[gremlin-semantics-concepts]]
== Comparability, Equality, Orderability, and Equivalence

This section of the document attempts to more clearly define the semantics for different types of value comparison
within the Gremlin language and reference implementation. There are four concepts related to value comparison:

**Equality**

Equality semantics is used by the equality operators (`P.eq/neq`) and contains operators derived from them
(`P.within/without`). It is also used for implicit `P.eq` comparisons, for example `g.V().has("age", 25)` - equality
semantics are used to look up vertices by `age` when considering the value.

**Comparability**

Comparability semantics is used by the compare operators (`P.lt/lte/gt/gte`) and operators derived from them
(`P.inside/outside/between`) and defines the semantics of how to compare two values.

**Orderability**

Orderability semantics defines how two values are compared in the context of an `order()` operation. These semantics
have important differences from Comparability.

**Equivalence**

Equivalence semantics are slightly different from Equality and are used for operations such as `dedup()` and `group()`.
Key differences include handling of numeric types and NaN.

Both Equality and Equivalence can be understood as complete, i.e. the result of equality and equivalence checks is
always either `TRUE` or `FALSE` (in particular, it never returns `NULL` or throws an exception). Similarly,
Orderability can be also understood as complete - any two values can be compared without error for ordering purposes.
Comparability semantics are not complete with respect to traditional binary boolean semantics, as certain comparisons
cannot be proved as either `TRUE` or `FALSE`. Common examples of such cases are Comparability against `NaN`, cross-type
Comparability (e.g. `STRING` vs `Numeric`). For the purposes of Comparability within Gremlin, any such incomputable
comparison is defined to be `FALSE`, and traditional binary boolean semantics apply thereafter.

[[gremlin-semantics-equality-comparability]]
=== Equality and Comparability

<<Equality,Equality>> and <<Comparability,Comparability>> can be understood to be semantically aligned with one another.
As mentioned above, <<Equality,Equality>> is used for `P.eq/neq` (and derived predicates) and
<<Comparability,Comparability>> is used for `P.lt/lte/gt/gte` (and derived predicates).
If we define Comparability using a `compare()` function over `A` and `B` as follows:

[source,text]
----
If (A, B) are Comparable per Gremlin semantics, then:
  For A < B,  Comparability.compare(A, B) < 0
  For A > B,  Comparability.compare(A, B) > 0
  For A == B, Comparability.compare(A, B) == 0
If (A, B) not Comparable, then:
              Comparability.compare(A, B) => ERROR
----

Then we can define Equality using an `equals()` function over `A` and `B` that acts as a strict binary
reduction of `Comparability.compare(A, B) == 0`:

[source,text]
----
For any (A, B):
  Comparability.compare(A, B) == 0     implies Equality.equals(A, B) == TRUE
  Comparability.compare(A, B) <> 0     implies Equality.equals(A, B) == FALSE
  Comparability.compare(A, B) => ERROR implies Equality.equals(A, B) == FALSE
----

The following table illustrates how <<Equality,Equality>> and <<Comparability,Comparability>> operate under various
classes of comparison:

|===
|Class|Arguments|<<Comparability,Comparability>>|<<Equality,Equality>>

|Comparisons Involving `NaN`|`(NaN,X)` +

where `X` = any value, including `NaN`|`FALSE` +

Comparing `NaN` to anything (including itself) cannot be evaluated.|`FALSE`

|Comparisons Involving `null`|`(null,null)`|`compare() == 0`|`TRUE`
||`(null, X)`|`FALSE` +

Since `NULL` is its own type, this falls under the umbrella of cross-type comparisons. |`FALSE`
|Comparisons within the same type family (i.e. String vs. String, Number vs. Number, etc.)|`(X, Y)` +

where `X` and `Y` of same type|Result of `compare()` depends on type semantics, defined below.|`TRUE` iff `compare() == 0`
|Comparisons across types (i.e. String vs. Number)|`(X, Y)` +

where `X` and `Y` of different type|`FALSE`|`FALSE`

|===

==== Equality and Comparability Semantics by Type

For <<Equality,Equality>> and <<Comparability,Comparability>> evaluation of values within the same type family, we
define the semantics per type family as follows.

===== NUMBER

Numbers are compared using type promotion, described above. As such, `1 == 1.0`.

Edge cases:

* `-0.0 == 0.0 == +0.0`
* `+INF == +INF`,  `-INF == -INF`,  `-INF != +INF`
** `±Infinity` follows the same type promotion rules for both 32-bit and 64-bit floats.
* As described above `NaN` is not <<Equality,Equal>> and not <<Comparability,Comparable>> to any Number (including itself).

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/semantics/Equality.feature[Equality Tests - Scenarios prefixed with "Primitives_Number_"]

===== NULL

As described in the table above, `null == null`, but is not <<Equality,Equal>> and not <<Comparability,Comparable>> to
any non-`null` value.

===== BOOLEAN

For Booleans, `TRUE == TRUE`, `FALSE == FALSE`, `TRUE != FALSE`, and `FALSE < TRUE`.

===== STRING / CHAR

We assume the common lexicographical order over unicode strings and characters. `A` and `B` are compared
lexicographically, and `A == B` if `A` and `B` are of the same type and lexicographically equal.

===== UUID

UUID is evaluated based on its String representation. However, `UUID("b46d37e9-755c-477e-9ab6-44aabea51d50")` and the
String `"b46d37e9-755c-477e-9ab6-44aabea51d50"` are not <<Equality,Equal>> and not <<Comparability,Comparable>>.

===== BINARY

Binary values are evaluated based on lexicographical comparison of their byte sequences, comparing each byte as a
signed 8-bit value. `A == B` if `A` and `B` have the same bytes in the same order.

===== DATETIME

Dates are evaluated based on the numerical comparison of Unix Epoch time.

===== DURATION

Durations are evaluated based on the numerical comparison of their total length. `A == B` if `A` and `B` represent the
same total length.

===== Graph Elements (VERTEX / EDGE / VPROPERTY)

If they are the same type of Element, these are compared by the value of their `T.id` according to the semantics for
the particular primitive type used for ids (implementation-specific). Elements of different types are
not <<Equality,Equal>> and not <<Comparability,Comparable>>.

===== PROPERTY

Properties are compared first by key (`STRING` semantics), then by value, according to the semantics for the particular
primitive type of the value. Properties with values in different type families are
not <<Equality,Equal>> and not <<Comparability,Comparable>>.

===== LIST

Lists are compared pairwise, element-by-element, in their natural list order. For each element, if the pairs are
<<Equality and Comparability,Equal>>, we simply move on to the next element pair until we encounter a pair which is not
comparable, or whose `Comparability.compare()` value is non-zero, and we return that value. Lists can be evaluated
for <<Equality and Comparability,Equality and Comparability>> even if they contain multiple types of elements, so long
as their elements are pairwise comparable per <<Equality and Comparability,Equality/Comparability>> semantics. During
this element by element comparison, if iteration `A` exhausts its elements before iteration `B` then `A < B`, and
vice-versa.

Empty lists are equal to other empty lists and less than non-empty lists.

|===
|`A`|`B`|`compare(A,B)`|`P`|Reason

|`[]`|`[]`|`0`|`P.eq`|empty lists are equal
|`[]`|`[1]`|`-1`|`P.lt`|empty < non-empty
|`[1]`|`[]`|`1`|`P.gt`|non-empty > empty
|`[1,2,3]`|`[1,2,3]`|`0`|`P.eq`|pairwise equal
|`[1,2,3]`|`[1,2,4]`|`-1`|`P.lt`|pairwise equal until last element: `3 < 4`
|`[1,2,3]`|`[1,2,3,4]`|`-1`|`P.lt`|`A` exhausts first
|`[1,2,3,4]`|`[1,2,3]`|`1`|`P.gt`|`B` exhausts first
|`[1,2]`|`[1.0,2.0]`|`0`|`P.eq`|type promotion
|`[1,"a"]`|`[1,"b"]`|`-1`|`P.lt`|pairwise <<Comparability,Comparable>> and `"a" < "b"`
|`[1]`|`["a"]`|`ERROR`|`P.neq`|cross-type comparison
|===

===== PATH

<<Equality and Comparability,Equality and Comparability>> semantics for `Paths` are similar to those for `Lists`, described above (though
`Paths` and `Lists` are still of different types and thus not <<Equality,Equal>> and not <<Comparability,Comparable>>).

===== SET

`Sets` are compared pairwise, element-by-element, in the same way as `Lists`, but they are compared in sorted order
using <<Orderability,Orderability>> semantics to sort (described further below). We use <<Orderability,Orderability>>
semantics for ordering so that `Sets` containing multiple element types can be properly sorted before being compared.

For example:

|===
|`A`|`B`|`compare(A,B)`|`P`|Reason

|`{1, 2}`|`{2, 1}`|`0`|`P.eq`|sort before compare
|`{1, "foo"}`|`{"foo", 1}`|`0`|`P.eq`|we use <<Orderability,Orderability>> semantics to sort across types
|===

Sets do introduce a bit of semantic stickiness, in that on the one hand they do respect type promotion semantics for
Equality and Comparability:

[source,text]
----
{1, 2} == {1.0, 2.0}
----

But on the other hand they also allow two elements that would be equal (and thus duplicates) according to type
promotion:

[source,text]
----
{1, 1.0, 2} is a valid set and != {1, 2}
----

We allow some "wiggle-room" in the implementation for providers to decide how to handle this logical inconsistency. The
reference implementation allows for semantically equivalent numerics to appear in a set (e.g `{1, 1.0}`), while at the
same time evaluating the same semantically equivalent numerics as equal during pairwise comparison across sets (e.g.
`{1,2} == {1.0,2.0}`).

===== MAP

'Map' semantics can be thought of as similar to `SET` semantics for the entry set the comprises the `MAP`. So again,
we compare pairwise, entry-by-entry, in the same way as `Lists`, and again, we first sort the entries using
<<Orderability,Orderability>> semantics. Map entries are compared first by key, then by value using the
<<Equality and Comparability Semantics by Type,Equality and Comparability>> semantics that apply to the specific type
of key and value.

Maps semantics have the same logical inconsistency as set semantics, because of type promotion. Again, we leave room
for providers to decide how to handle this in their implementation. The reference implementation allows for semantically
equivalent keys to appear in a map (e.g. `1` and `1.0` can both be keys in the same map), but when comparing maps we
treat pairwise entries with semantically equivalent keys as the same.

[[gremlin-semantics-orderability]]
=== Orderability

<<Equality and Comparability,Equality and Comparability>> were described in depth in the sections above, and their
semantics map to the `P` predicates. <<Comparability,Comparability>> in particular is limited to
comparison of values within the same type family. Comparability is complete within a given type (except for `NaN`,
which results in `FALSE` for any comparison), but returns `FALSE` for comparisons across types (e.g., an integer cannot
be compared to a string).

Orderability semantics are very similar to Comparability for the most part, except that Orderability will never result
in `ERROR` for comparison of any two values - even if two values are incomparable according to Comparability semantics
we will still be able to determine their respective order. This allows for a total order across all Gremlin values. In
the reference implementation, any step using `Order.asc` or `Order.desc` (e.g. OrderGlobalStep, OrderLocalStep) will
follow these semantics.

To achieve this globally complete order, we need to address any cases in Comparability which are incomputable, we must
define a global order across type families, and we must provide semantics for ordering "unknown"
values (for in-process implementations where the host language's value space exceeds the GType enum).

We define the type space, and the global order across the type space as follows:

[source,text]
----
1.  NULL
2.  BOOLEAN
3.  NUMBER
4.  DATETIME
5.  STRING
6.  UUID
7.  Vertex
8.  Edge
9.  VertexProperty
10. Property
11. Path
12. Set
13. List
14. Map
15. Unknown
----

Values in different type spaces will be ordered according to their priority (e.g. all Numbers < all Strings).

Within a given type space, Orderability determines if two values are ordered at the same position or one value is
positioned before or after the another. When the position is identical, which value comes first (in other words,
whether it should perform stable sort) depends on graph providers' implementation.

To allow for this total ordering, we must also address the cases in <<Equality and Comparability,Comparability>> where
the comparison is incomputable:

|===
|Incomputable Scenario|Comparability|Orderability

|Comparison against `NaN`|`NaN` not comparable to anything, including itself.|`NaN` appears after `+Infinity` in the numeric type space.
|Comparison across types|Cannot compare values of different types. This includes the `NULL`.|Subject to a total
type ordering where every value of type A appears before or after every value of Type B per the priorty list above.
|===

==== Key differences from Comparability

One key difference to note is that we use Orderability semantics to compare values within containers (`LIST`, `SET`,
`Path`, `MAP`, `Property`) rather than using Comparability semantics (i.e. Orderability all the way down).

===== Numeric Ordering

Same as Comparability, except `NaN` is equivalent to `NaN` and is greater than all other Numbers, including `+Infinity`.
Additionally, because of type promotion (`1` == `1.0`), numbers of the same value but of different numeric types will
not have a stable sort order (`1` can appear either before or after `1.0`).

===== PROPERTY

Same as Comparability, except Orderability semantics are used for the property value.

===== Iterables (PATH, LIST, SET, MAP)

Same as Comparability, except Orderability semantics apply for the pairwise element-by-element comparisons.

===== Unknown Types

For Orderability semantics, we allow for the possibility of "unknown" types. If the "unknown" arguments are of the same
type, we use the type's native equality and ordering operations (if defined) to determine their natural order.
If the unknown arguments are of different types or do not define a natural order, we order first by type identity,
then by canonical string representation.

[[gremlin-semantics-equivalence]]
=== Equivalence

Equivalence defines how TinkerPop deals with two values to be grouped or de-duplicated. Specifically it is necessary
for the `dedup()` and `group()` steps in Gremlin.

For example:

[source,text]
----
// deduplication needs equivalence over two property values
gremlin> g.V().dedup().by("name")
// grouping by equivalence over two property values
gremlin> g.V().group().by("age")
----

Like Equality, Equivalence checks always return `true` or `false`, never `NULL` or `error`, nor do they produce
exceptions. For the most part Equivalence and Equality are the same, with the following key differences:

* Equivalence ignores type promotion semantics, i.e. two values of different types (e.g. 2^^int vs. 2.0^^float) are
always considered to be non-equivalent.
* `NaN` Equivalence is the reverse of Equality: `NaN` is equivalent to `NaN` and not
Equivalent to any other `NUMBER`.

=== Further Reference

==== Mapping for P

The following table maps the notions proposed above to the various `P` operators:

[%header]
|================
|Predicate|Concept
|P.eq     |Equality
|P.neq    |Equality
|P.within |Equality
|P.without|Equality
|P.lt     |Comparability
|P.gt     |Comparability
|P.lte    |Equality, Comparability
|P.gte    |Equality, Comparability
|P.inside |Comparability
|P.outside|Comparability
|P.between|Equality, Comparability
|================

==== See Also

link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/semantics/Equality.feature[Equality Tests],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/semantics/Comparability.feature[Comparability Tests]

== Steps

While TinkerPop has a full test suite for validating functionality of Gremlin, tests alone aren't always exhaustive or
fully demonstrative of Gremlin step semantics. It is also hard to simply read the tests to understand exactly how a
step is meant to behave. This section discusses the semantics for individual steps to help users and providers
understand implementation expectations.

Each step entry uses the following labeled fields in order: `Description`, `Syntax`, a start/mid/modulated/domain/range
table, `Arguments`, `Modulation`, `Considerations`, `Exceptions`, and a closing `See:` line linking to the source file
and the corresponding reference documentation. When a step has no entries for `Arguments`, `Modulation`, or `Exceptions`,
the field is still present with `None` as its body. When adding a new step or revising an existing one, follow the
pattern from a neighboring entry rather than introducing a new structure.

[[adde-step]]
=== addE()

*Description:* Adds an edge to the graph.

*Syntax:* `addE(edgeLabel: STRING)` | `addE(edgeLabelTraversal: Traversal<any, STRING>)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|Y |Y |`from()`/`to()` |`any` |`Edge`
|=========================================================

*Arguments:*

* `edgeLabel` - The label of the edge to add.
* `edgeLabelTraversal` - A traversal that produces the label of the edge to add.

*Modulation:*

* `from()` - Specifies the source vertex for the edge.
* `to()` - Specifies the target vertex for the edge.

*Considerations:*

The `addE()` step can be used as both a start step and a mid-traversal step. When used as a start step, both `from()`
and `to()` must be specified. When used as a mid-traversal step, the current traverser becomes the source vertex and
only `to()` needs to be specified.

The gremlin-lang grammar only permits `Traversal` and `STRING` (alias for `__.select(String)`) arguments in `from()` and
`to()`. The `Traversal` must either produce a `Vertex` which is attachable to the graph, or it must produce the id of an
existing `Vertex` in the graph. `GraphTraversal` implementations in GLVs may optionally support `from(Vertex)` and
`to(Vertex)` as syntactic sugar. If translating to gremlin-lang scripts, these sugared modulators must be converted to
`from(__.V(vertex.id()))` or `from(__.constant(vertex.id()))` (and equivalents for `to()`).

*Exceptions:*

* If the edge label is null, an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java[source (start)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addedge-step[reference]

[[addv-step]]
=== addV()

*Description:* Adds a vertex to the graph.

*Syntax:* `addV()` | `addV(vertexLabel: STRING, moreLabels: STRING...)` | `addV(vertexLabelTraversal: Traversal<any, STRING>, moreLabelTraversals: Traversal<any, STRING>...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|Y |Y |N |`any` |`Vertex`
|=========================================================

*Arguments:*

* `vertexLabel` - The label of the vertex to add.
* `moreLabels` - Additional labels for the vertex, on graphs that permit more than one label per vertex.
* `vertexLabelTraversal` - A traversal that produces the label of the vertex to add.
* `moreLabelTraversals` - Additional traversals that each produce a further label for the vertex.

*Modulation:*

None

*Considerations:*

The `addV()` step can be used as both a start step and a mid-traversal step. If no label is provided, the default
vertex label for the graph will be used. On a graph that permits more than one label per vertex, additional labels
may be supplied to create a multi-label vertex; the resulting set of labels must satisfy the graph's label
cardinality.

*Exceptions:*

* If the vertex label is null, an `Argument Error` is raised.
* If the number of labels supplied violates the graph's label cardinality (for example, more than one label on a
single-label graph), a `State Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java[source (start)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addvertex-step[reference]

=== addLabel()

*Description:* Adds one or more labels to an element.

*Syntax:* `addLabel(label: STRING, moreLabels: STRING...)` | `addLabel(labelTraversal: Traversal<any, STRING>, moreLabelTraversals: Traversal<any, STRING>...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` |`Element`
|=========================================================

*Arguments:*

* `label` - The label to add.
* `moreLabels` - Additional labels to add.
* `labelTraversal` - The first (or only) traversal that produces a label to add.
* `moreLabelTraversals` - Additional label-producing traversals (may be empty for single-traversal behavior).

*Considerations:*

Label add and remove operations require a graph that permits the set of labels on an element to change. Where a graph
fixes a single immutable label per vertex, that label is assigned at creation and can be neither added to nor removed.
Adding a label that already exists on the element is a no-op. The step returns the element (sideEffect semantics).
Each traversal argument is iterated for a single result only. If exactly one traversal is provided and it produces
a `LIST` or `SET` of `STRING`, the collection will be automatically unfolded into the set of labels added. If more
than one traversal is provided, each must resolve to a single `STRING` label.

*Exceptions*

* If the graph does not permit label modification, a `State Error` is raised.
* If any label argument is null or empty, an `Argument Error` is raised.
* If more than one traversal is provided and any of them produces a `LIST` or `SET`, an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddLabelStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addlabel-step[reference]

=== dropLabel()

*Description:* Removes one or more specific labels from an element, or removes all labels.

*Syntax:* `dropLabel(label: STRING, moreLabels: STRING...)` | `dropLabel(labelTraversal: Traversal<any, STRING>, moreLabelTraversals: Traversal<any, STRING>...)` | `dropLabels()`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` |`Element`
|=========================================================

*Arguments:*

* `label` - The label to remove.
* `moreLabels` - Additional labels to remove.
* `labelTraversal` - The first (or only) traversal that produces a label to remove.
* `moreLabelTraversals` - Additional label-producing traversals (may be empty for single-traversal behavior).

*Considerations:*

Label add and remove operations require a graph that permits the set of labels on an element to change. Dropping a
label that does not exist on the element is a no-op. `dropLabels()` removes all labels and is only valid on a graph
that allows an element to carry no labels at all. The step returns the
element (sideEffect semantics).
Each traversal argument is iterated for a single result only. If exactly one traversal is provided and it produces
a `LIST` or `SET` of `STRING`, the collection will be automatically unfolded into the set of labels removed. If more
than one traversal is provided, each must resolve to a single `STRING` label.

*Exceptions*

* If the graph does not permit label modification, a `State Error` is raised.
* If `dropLabel()` or `dropLabels()` would leave the element with fewer labels than the graph requires, a `State Error`
is raised.
* If more than one traversal is provided and any of them produces a `LIST` or `SET`, an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/DropLabelsStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#droplabel-step[reference]

[[all-step]]
=== all()

*Description:* Filters list data from the traversal stream if all of the list's items match the supplied predicate.

*Syntax:* `all(predicate: P)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`LIST`
|=========================================================

*Arguments:*

* `predicate` - The predicate to use to test each value in the list data.

*Modulation:*

None

*Considerations:*

Each value will be tested using the supplied predicate. Empty lists always pass through and null/non-list traversers
will be filtered out of the Traversal Stream.

*Exceptions:*

* An `Argument Error` is raised if the input predicate is `null`.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AllStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#all-step[reference]

[[aggregate-step]]
=== aggregate()

*Description:* Collects all objects in the traversal into a collection.

*Syntax:* `aggregate(sideEffectKey: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()` |`any` |`any`
|=========================================================

*Arguments:*

* `sideEffectKey` - The name of the side-effect key that will hold the aggregated objects.

*Modulation:*

* `by()` - Determines how to transform the object before aggregating. If not specified, the object itself is used.

*Considerations:*

The `aggregate()` step is a side-effect step that collects objects but passes the traverser to the next step unchanged.
The aggregated objects can be accessed later using the `cap()` step.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AggregateStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#aggregate-step[reference]

[[and-step]]
=== and()

*Description:* Ensures that all provided traversals yield a result.

*Syntax:* `and(andTraversals: Traversal<any, any>...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`any`
|=========================================================

*Arguments:*

* `andTraversals` - One or more traversals that will be executed against the current object.

*Modulation:*

None

*Considerations:*

The `and()` step is a filter step that allows the traverser to pass if all of the provided traversals yield a result.
It follows the ternary boolean logic described in the <<gremlin-semantics-concepts,Ternary Boolean Logics>> section.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AndStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#and-step[reference]

[[any-step]]
=== any()

*Description:* Filters list data from the Traversal Stream if any of the list's items match the supplied predicate.

*Syntax:* `any(predicate: P)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`LIST`
|=========================================================

*Arguments:*

* `predicate` - The predicate to use to test each value in the list data.

*Modulation:*

None

*Considerations:*

Each value will be tested using the supplied predicate. Empty lists, null traversers, and non-list traversers will be
filtered out of the Traversal Stream.

*Exceptions:*

* An `Argument Error` is raised if the input predicate is `null`.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AnyStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#any-step[reference]

[[as-step]]
=== as()

*Description:* Labels a step for later access by steps that make use of such labels.

*Syntax:* `as(stepLabel: STRING, stepLabels: STRING...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`any`
|=========================================================

*Arguments:*

* `stepLabel` - The label to assign to the step.
* `stepLabels` - Additional labels to assign to the step.

*Modulation:*

None

*Considerations:*

The `as()` step is not a real step, but a "step modulator" similar to `by()` and `option()`. It allows labeling a step
for later access by steps like `select()` and `match()`. A step can have any number of labels associated with it, which
is useful for referencing the same step multiple times in a future step.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#as-step[reference]

[[asBool-step]]
=== asBool()

*Description:* Parse the value of the incoming traverser as boolean.

*Syntax:* `asBool()`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`NUMBER`/`STRING`/`BOOLEAN` |`BOOLEAN`
|=========================================================

*Arguments:*

None

*Modulation:*

None

*Considerations:*

Booleans are passed as is, numbers evaluate to `true` if non-zero, and `false` if zero or `NaN`. Strings only accept "true" or "false" (case-insensitive).

*Exceptions:*

If the incoming traverser type is unsupported, a string other than "true" or "false", or `null`, then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsBoolStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asBool-step[reference]

[[asDate-step]]
=== asDate()

*Description:* Parse the value of incoming traverser as date. Supported ISO-8601 strings and Unix time numbers.

*Syntax:* `asDate()`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`any`
|=========================================================

*Arguments:*

None

*Modulation:*

None

*Considerations:*

Incoming date remains unchanged.

*Exceptions:*

* If the incoming traverser is a non-`STRING`/`NUMBER`/`DATETIME` value then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asDate-step[reference]

[[asNumber-step]]
=== asNumber()

*Description:* converts the incoming traverser to the nearest parsable type if no argument is provided, or to the desired numerical type, based on the type token (`GType`) provided.

*Syntax:* `asNumber()` | `asNumber(typeToken: GType)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`NUMBER`/`STRING`/`DATE` |`NUMBER`
|=========================================================

*Arguments:*

* `typeToken` - The enum `GType` to denote the desired type to parse/cast to.

*Modulation:*

None

*Considerations:*

*Modulation:*

None

*Considerations:*

If no type token is provided, the incoming number remains unchanged. A `DATE` input is converted to milliseconds since epoch. A `null` input passes through as `null`.

*Exceptions:*

* If any overflow occurs during narrowing of types, an `Arithmetic Error` is raised.
* If the incoming string cannot be parsed into a valid number format, an `Argument Error` is raised.
* If the incoming traverser is a non-`STRING`/`NUMBER`/`DATE` value then an `Argument Error` is raised.
* If the supplied type token is not a number type, then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsNumberStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asNumber-step[reference]

[[asString-step]]
=== asString()

*Description:* Returns the value of incoming traverser as strings, or if `Scope.local` is specified, returns each element inside
incoming list traverser as string.

*Syntax:* `asString()` | `asString(scope: Scope)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`STRING`/`LIST`
|=========================================================

*Arguments:*

* `scope` - Determines the type of traverser it operates on. The `global` scope operates on individual traversers.
The `local` scope operates on list traversers, converting each element. Non-list
traversers under `Scope.local` behave as under `Scope.global`.

*Modulation:*

None

*Considerations:*

Each value is converted to its canonical String representation.

*Exceptions:*

* If the incoming traverser is a `null` value then an `Argument Error` is raised.
* Under `Scope.local`, an `Argument Error` is also raised if any element of the list traverser is `null`.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsStringLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asString-step[reference]

[[barrier-step]]
=== barrier()

*Description:* Turns the lazy traversal pipeline into a bulk-synchronous pipeline.

*Syntax:* `barrier()` | `barrier(maxBarrierSize: INT)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`any`
|=========================================================

*Arguments:*

* `maxBarrierSize` - The maximum number of traversers that can be held in the barrier before being processed.

*Modulation:*

None

*Considerations:*

The `barrier()` step is useful in the following situations:
* When everything prior to `barrier()` needs to be executed before moving onto the steps after the `barrier()` (i.e., ordering).
* When "stalling" the traversal may lead to a "bulking optimization" in traversals that repeatedly touch many of the
same elements (i.e., optimizing).

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/NoOpBarrierStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#barrier-step[reference]

[[both-step]]
=== both()

*Description:* Maps a vertex to its adjacent vertices given the edge labels.

*Syntax:* `both(edgeLabels: STRING...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Vertex` |`Vertex`
|=========================================================

*Arguments:*

* `edgeLabels` - The edge labels to traverse. If no labels are provided, all edges are traversed.

*Modulation:*

None

*Considerations:*

The `both()` step is a vertex-centric step that traverses both incoming and outgoing edges from the current vertex to
adjacent vertices. It is equivalent to the union of `in()` and `out()`.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#vertex-steps[reference]

[[bothe-step]]
=== bothE()

*Description:* Maps a vertex to its incident edges given the edge labels.

*Syntax:* `bothE(edgeLabels: STRING...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Vertex` |`Edge`
|=========================================================

*Arguments:*

* `edgeLabels` - The edge labels to traverse. If no labels are provided, all edges are traversed.

*Modulation:*

None

*Considerations:*

The `bothE()` step is a vertex-centric step that traverses both incoming and outgoing edges from the current vertex. It
is equivalent to the union of `inE()` and `outE()`.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#vertex-steps[reference]

[[bothv-step]]
=== bothV()

*Description:* Maps an edge to its incident vertices.

*Syntax:* `bothV()`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Edge` |`Vertex`
|=========================================================

*Arguments:*

None

*Modulation:*

None

*Considerations:*

The `bothV()` step is an edge-centric step that traverses to both the incoming and outgoing vertices of the current
edge. It is equivalent to the union of `inV()` and `outV()`.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/EdgeVertexStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#vertex-steps[reference]

[[branch-step]]
=== branch()

*Description:* Splits the traverser to all the specified traversals.

*Syntax:* `branch(branchTraversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`option()` |`any` |`any`
|=========================================================

*Arguments:*

* `branchTraversal` - The traversal to branch the traverser to.
* `function` - The function to branch the traverser to.

*Modulation:*

* `option()` - Specifies the branch options for the traverser.

*Considerations:*

The `branch()` step is a general step that splits the traverser to all the child traversals provided to it. It's the
basis for more robust steps like `choose()` and `union()`. The branch step is typically used with the `option()` step
to specify the branch options.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#branch-step[reference]

[[cap-step]]
=== cap()

*Description:* Iterates the traversal up to itself and emits the sideEffect referenced by the provided key.

*Syntax:* `cap(sideEffectKey: STRING, sideEffectKeys: STRING...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`any`
|=========================================================

*Arguments:*

* `sideEffectKey` - The side-effect to emit.
* `sideEffectKeys` - Other side-effects to emit.

*Modulation:*

None

*Considerations:*

The `cap()` step is a barrier step that iterates the traversal up to itself and emits the sideEffect referenced by the
provided key. If multiple keys are provided, then a `MAP<STRING, any>` of sideEffects is emitted.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectCapStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#cap-step[reference]

[[call-step]]
=== call()

*Description:* Provides support for provider-specific service calls.

*Syntax:* `call(service: STRING)` | `call(service: STRING, params: MAP)` | `call(service: STRING, childTraversal: Traversal)` | `call(service: STRING, params: MAP, childTraversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|Y |Y |`with()` |`any` |`any`
|=========================================================

*Arguments:*

* `service` - The name of the service call.
* `params` - A collection of static parameters relevant to the particular service call. Keys and values can be any
type currently supported by the Gremlin type system.
* `childTraversal` - A traversal used to dynamically build at query time a collection of parameters relevant to the service
call.

*Modulation:*

* `with(key, value)` - Sets an additional static parameter relevant to the service call. Key and value can be any
type currently supported by the Gremlin type system.
* `with(key, Traversal)` - Sets an additional dynamic parameter relevant to the service call. Key can be any
type currently supported by the Gremlin type system.

How static and dynamic parameters are merged is a detail left to the provider implementation. The reference implementation
(`CallStep`) uses effectively a "left to right" merge of the parameters - it starts with the static parameter `MAP`
argument, then merges in the parameters from the dynamic `Traversal` argument, then merges in each `with` modulation
one by one in the order they appear.

Service calls in the reference implementation can be specified as `Start` (start of traversal), `Streaming`
(mid-traversal flat map step), and `Barrier` (mid-traversal barrier step). Furthermore, the `Barrier` type can be
all-at-once or with a maximum chunk size. A single service can support more than one of these modes, and if it does,
must provide semantics for how to configure the mode at query time via parameters.

Providers using the reference implementation to support service call with need to provide a `ServiceFactory` for each
named service that can create `Service` instances for execution during traversal. The `ServiceFactory` is a singleton
that is registered with the `ServiceRegistry` located on the provider `Graph`. The `Service` instance is local to
each traversal, although providers can choose to re-use instances across traversals provided there is no state.

*Considerations:*

Providers using the reference implementation can return `Traverser` output or raw value output - the `CallStep` will
handle either case appropriately. In the case of a `Streaming` service, where there is exactly one input to each
call, the reference implementation can preserve `Path` information by splitting the input `Traverser` when receiving
raw output from the call. In the case of `Barrier` however, it is the responsibility of the `Service` to preserve
`Path` information by producing its own `Traversers` as output, since the `CallStep` cannot match input and output
across a barrier. The ability to split input `Traversers` and generate output is provided by the reference
implementation's `ServiceCallContext` object, which is supplied to the `Service` during execution.

There are three execution methods in the reference implementation service call API:

* `execute(ServiceCallContext, Map)` - execute a service call to start a traversal
* `execute(ServiceCallContext, Traverser, Map)` - execute a service call mid-traversal streaming (one input)
* `execute(ServiceCallContext, TraverserSet, Map)` - execute a service call mid-traversal barrier

The Map is the merged collection of all static and dynamic parameters. In the case of `Barrier` execution, notice
that there is one `MAP` for many input. Since the `call()` API supports dynamic parameters, this implies that all
input must reduce to the same set of parameters for `Barrier` execution. In the reference implementation, if more
than one parameter set is detected, this will cause an exception and the traversal will halt. Providers that
implement their own version of a call operation may decide on other strategies to handle this case - for example
it may be sensible to group traversers by Map in the case where multiple parameter sets are detected.

The no-arg version of the `call()` API is meant to be a directory service and should only be used to start a traversal.
The reference implementation provides a default version, which will produce a list of service names or a service
description if run with `verbose=true`. Providers using their own implementation of the call operation must provide their
own directory listing service with the service name `"--list"`.

*Exceptions:*

* If a named service does not support the execution mode implied by the traversal, for example, using a `Streaming` or
`Barrier` step as a traversal source, this raises an `Unsupported Operation`.
* As mentioned above, dynamic property parameters (`Traversals`) that reduce to more than one property set for a chunk
of input is not supported in the reference implementation and raises an `Unsupported Operation`.
* Use of the reference implementation's built-in directory service - `call()` or `call("--list")` - mid-traversal
raises an `Unsupported Operation`.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CallStep.java[CallStep],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/service/Service.java[Service],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/service/ServiceRegistry.java[ServiceRegistry],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#call-step[reference]

[[choose-step]]
=== choose()

*Description:* A branch step that routes the traverser to different paths based on a choice criterion.

*Syntax:* `choose(choice: Traversal | T)` | `choose(choice: Traversal | P, trueChoice: Traversal)` | `choose(choice: Traversal | P, trueChoice: Traversal, falseChoice: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |Y |`any` |`any`
|=========================================================

*Arguments:*

* `choice` - A `Traversal`, or `T` that produces a value used to determine which option to take. In the `if-then` forms, this value may be a `P` to determine `true` or `false`.
* `trueChoice` - The traversal to take if the predicate traversal returns a value (has a next element).
* `falseChoice` - The traversal to take if the predicate traversal returns no value (has no next element).

*Modulation:*

* `option(pickToken, traversalOption)` - Adds a traversal option to the `choose` step. The `pickToken` is matched
against the result of the choice traversal. The `pickToken` may be a literal value, a predicate `P` or a `Pick` enum
value. `Traversal` is not allowed as a `pickToken` here and raises an `Argument Error`. If a match is
found, the traverser is routed to the corresponding `traversalOption`.

*Considerations:*

The `choose()` step is a branch step that routes the traverser to different paths based on a choice criterion. There are
two main forms of the `choose()` step:

1. *if-then form*: `choose(predicate, trueChoice, falseChoice)` - If the predicate traversal or `P` returns a value
(has a next element), the traverser is routed to the trueChoice traversal. Otherwise, it is routed to the falseChoice
traversal. If the predicate is unproductive or if the falseChoice is not specified, then the traverser passes through.

2. *switch form*: `choose(choice).option(pickValue, resultTraversal)` - The choice which may be a `Traversal` or
`T` produces a value that is matched against the pickValue of each option. If a match is found, the traverser is routed
to the corresponding resultTraversal and no further matches are attempted. If no match is found then the traverser
passes through by default or can be matched on `Pick.none`. If the choiceTraversal is unproductive, then the traverser
passes through by default or can be matched on `Pick.unproductive`.

`choose` does not allow more than one traversal to be assigned to a single `Pick`. The first `Pick` assigned via
`option` is the one that will be used, similar to how the first pickValue match that is found is used.

The `choose()` step ensures that only one option is selected for each traverser, unlike other branch steps like
`union()` that can route a traverser to multiple paths. As it is like `union()`, note that each `option` stream will
behave like one:

[gremlin-groovy,modern]
----
g.V().union(__.has("name", "vadas").values('age').fold(), __.has('name',neq('vadas')).values('name').fold())
g.V().choose(__.has("name", "vadas"), __.values('age').fold(), __.values('name').fold())
----

*Exceptions:*

* An `Argument Error` is raised if `Pick.any` is used as an option token, as only one option per traverser is allowed.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#choose-step[reference]

[[combine-step]]
=== combine()

*Description:* Appends one list to the other and returns the result to the Traversal Stream.

*Syntax:* `combine(values: any)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`LIST`
|=========================================================

*Arguments:*

* `values` - A list of items or a traversal that will produce a list of items.

*Modulation:*

None

*Considerations:*

A list is returned after the combine operation is applied so duplicates are allowed. `Merge` can be used instead if
duplicates aren't wanted. This step only applies to list types which means that non-iterable types (including null)
will cause exceptions to be thrown.

*Exceptions:*

* If the incoming traverser isn't a list then an `Argument Error` is raised.
* If the argument doesn't resolve to a list then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/CombineStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#combine-step[reference],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#merge-step[merge() reference]

[[coin-step]]
=== coin()

*Description:* Filters traversers from the Traversal Stream based on a biased coin toss.

*Syntax:* `coin(probability: NUMBER)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`any`
|=========================================================

*Arguments:*

* `probability` - The probability (between 0.0 and 1.0) that the traverser will pass through the filter.

*Modulation:*

None

*Considerations:*

Each traverser is subject to a random filter based on the provided probability. A probability of 0.0 means no
traversers pass through, while a probability of 1.0 means all traversers pass through.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#coin-step[reference]

[[concat-step]]
=== concat()

*Description:* Concatenates the incoming String traverser with the input String arguments, and return the joined String.

*Syntax:* `concat()` | `concat(concatStrings: STRING...)` | `concat(concatTraversal: Traversal<any, STRING>, otherConcatTraversals: Traversal<any, STRING>...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING` |`STRING`
|=========================================================

*Arguments:*

* `concatStrings` - Varargs of `STRING`. If one or more String values are provided, they will be concatenated together
with the incoming traverser. If no argument is provided, the String value from the incoming traverser is returned.
* `concatTraversal` - A `Traversal` whose value must resolve to a `STRING`. The first result returned from the traversal will
be concatenated with the incoming traverser.
* `otherConcatTraversals` - Varargs of `Traversal`. Each `Traversal` value must resolve to a `STRING`. The first result
returned from each traversal will be concatenated with the incoming traverser and the previous traversal arguments.

*Modulation:*

None

*Considerations:*

Any `null` String values will be skipped when concatenated with non-`null` String values. If two `null` value are
concatenated, the `null` value will be propagated and returned.

*Exceptions:*

* If the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConcatStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#concat-step[reference]

[[dateAdd-step]]
=== dateAdd()

*Description:* Increase value of input Date.

*Syntax:* `dateAdd(dateToken: DT, value: INT)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`DATETIME` |`DATETIME`
|=========================================================

*Arguments:*

* `dateToken` - Date token enum. Supported values `second`, `minute`, `hour`, `day`.
* `value` - The number of units, specified by the DT Token, to add to the incoming values. May be negative for subtraction.

*Modulation:*

None

*Considerations:*

The step computes a duration from `dateToken` and `value` at step construction time and adds that duration to
the incoming date.

*Exceptions:*

* If the incoming traverser is a non-`DATETIME` value then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateAddStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#dateAdd-step[reference]

[[dateDiff-step]]
=== dateDiff()

*Description:* Returns the difference between two Dates in epoch time.

*Syntax:* `dateDiff(value: DATETIME)` | `dateDiff(dateTraversal: Traversal<any, DATETIME>)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`DATETIME` |`DATETIME`
|=========================================================

*Arguments:*

* `value` - Date for subtraction.
* `dateTraversal` - The `Traversal` value must resolve to a `DATETIME`. The first result returned from the traversal will be
subtracted with the incoming traverser.

*Modulation:*

None

*Considerations:*

If argument resolves as `null` then incoming date will not be changed.

*Exceptions:*

* If the incoming traverser is a non-`DATETIME` value then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#dateDiff-step[reference]

[[dedup-step]]
=== dedup()

*Description:* Removes repeatedly seen results from the Traversal Stream.

*Syntax:* `dedup()` | `dedup(labels: STRING...)` | `dedup(scope: Scope, labels: STRING...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()` |`any` |`any`
|=========================================================

*Arguments:*

* `scope` - Determines the scope in which `dedup` is applied.
The `global` scope will drop duplicate values across the global stream of traversers. The `local` scope operates at the
individual traverser level, and will remove duplicate values from within a collection.

* `labels` - If `dedup()` is provided a list of labels, then it will ensure that the de-duplication
is not with respect to the current traverser object, but to the path history of the traverser.

For Example:

[source,groovy]
----
g.V().as('a').out('created').as('b').in('created').as('c').
  dedup('a','b').select('a','b','c')
----

will filter out any such `a` and `b` pairs which have previously been seen.

*Modulation:*

* `by()` - Performs dedup according to the property specified in the by modulation. For example:
`g.V().dedup().by("name")` will filter out vertices with duplicate names.

*Considerations:*

* There is no guarantee that ordering of results will be preserved across a dedup step.
* There is no guarantee which element is selected as the survivor when filtering duplicates.

For example, given a graph with the following three vertices:

[width="100%",options="header"]
|=========================================================
|name |age
|Alex |38
|Bob |45
|Chloe |38
|=========================================================

and the traversal of:

[source,groovy]
----
g.V().order().by("name", Order.asc).
  dedup().by("age").values("name")
----

can return any of:

`["Alex", "Bob"]`, `["Bob", "Alex"]`, `["Bob", "Chloe"]`, or `["Chloe", "Bob"]`

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DedupLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#dedup-step[reference]

[[difference-step]]
=== difference()

*Description:* Adds the difference of two lists to the Traversal Stream. 

*Syntax:* `difference(values: any)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`SET`
|=========================================================

*Arguments:*

* `values` - A list of items or a `Traversal` that will produce a list of items.

*Modulation:*

None

*Considerations:*

Set difference (`A-B`) is an ordered operation. The incoming traverser is treated as `A` and the provided argument is
treated as `B`. A set is returned after the difference operation is applied so there won't be duplicates. This step only
applies to list types which means that non-iterable types (including null) will cause exceptions to be thrown.

*Exceptions:*

* If the incoming traverser isn't a list then an `Argument Error` is raised.
* If the argument doesn't resolve to a list then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DifferenceStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#difference-step[reference]

[[disjunct-step]]
=== disjunct()

*Description:* Adds the disjunct set to the Traversal Stream.

*Syntax:* `disjunct(values: any)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`SET`
|=========================================================

*Arguments:*

* `values` - A list of items or a `Traversal` that will produce a list of items.

*Modulation:*

None

*Considerations:*

A set is returned after the disjunct operation is applied so there won't be duplicates. This step only applies to list
types which means that non-iterable types (including null) will cause exceptions to be thrown.

*Exceptions:*

* If the incoming traverser isn't a list then an `Argument Error` is raised.
* If the argument doesn't resolve to a list then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DisjunctStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#disjunct-step[reference]

[[element-step]]
=== element()

*Description:* Traverse from `Property` to its `Element`.

*Syntax:* `element()`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Property` |`Element`
|=========================================================

*Arguments:*

None

*Modulation:*

None

*Considerations:*

The `element()` step traverses from a `Property` to the `Element` that owns it. A `VertexProperty`
yields its `Vertex`, an edge `Property` yields its `Edge`, and a meta `Property` (a property on a
`VertexProperty`) yields the `VertexProperty`. The step is a pure navigation step and does not
consult the underlying graph.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#element-step[reference]

[[elementmap-step]]
=== elementMap()

*Description:* Converts elements to a `Map` representation containing the element's `id`, `label`, and properties.
For an `Edge`, the map also includes the `IN` and `OUT` vertex structures (each a nested map of `id` and `label`).

*Syntax:* `elementMap(String... propertyKeys)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` |`Map`
|=========================================================

*Arguments:*

* `propertyKeys` - If `elementMap()` is provided a list of propertyKeys, then the result map will only contain
property entries specified by propertyKeys. If the list is empty, then all properties are included.

*Considerations:*

The label format in the map is controlled by source-level `with("multilabel")` and `with("singlelabel")` options, not
by the number of labels the graph permits per element. When `"multilabel"` is present, the label value is a
`SET<STRING>`. Likewise when
`"singlelabel"` is present, it is a single `STRING`. The two options are mutually exclusive. Configuring both
`"multilabel"` and `"singlelabel"` on the same source is rejected with a `State Error`. Note that it is a
provider choice whether an unconfigured `GraphTraversal` defaults to `multilabel` or `singlelabel` semantics. The
reference implementation defaults to `singlelabel` semantics.

*Exceptions*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ElementMapStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#elementmap-step[reference]

[[e-step]]
=== E()

*Description:* Reads edges from the graph by their identifiers. Can be used as a start step or mid-traversal.

*Syntax:* `E(edgeIds: any...)` | `E(idTraversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|Y |Y |N |`any` |`Edge`
|=========================================================

*Arguments:*

* `edgeIds` - Zero or more edge identifiers. If none are provided, all edges are returned. Arrays and list-valued
  arguments are flattened (unrolled) into individual IDs.
* `idTraversal` - A child traversal which supplies edge identifiers. The child traversal must be read-only
  (no mutating steps).

*Modulation:*

None

*Considerations:*

* When used as a start step with a traversal argument, a synthetic traverser seeds the child traversal.
* When used mid-traversal with a traversal argument, the current traverser seeds the child traversal.
* If no edges match the provided identifiers, the step produces no results.
* Graph providers may optimize `E()` with subsequent `has()` filters by folding `HasContainer` predicates into the
  step for index-backed lookups.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#graph-step[reference]

[[format-step]]
=== format()

*Description:* Returns a string built by substituting variable references in a template with values from the
incoming traverser.

*Syntax:* `format(formatString: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()` |`any` |`STRING`
|=========================================================

*Arguments:*

* `formatString` - The template string. Variables are written as `%{variable_name}` and positional arguments as
`%{_}`. Either form may appear multiple times.

*Modulation:*

* `by()` - Provides the value for each `%{_}` positional argument in declaration order. For example:
`g.V().format("%{name} has %{_} connections").by(bothE().count())`.

*Considerations:*

For each `%{variable_name}` reference, the step first looks for a property of that name on the incoming `Element`,
and if none is found, falls back to a scope value such as an `as()` label. For each `%{_}` reference, the step
consumes the next `by()` traversal in declaration order. If any variable cannot be resolved, the entire result
is filtered out of the traversal stream.

*Exceptions:*

* If `formatString` is `null`, an `Argument Error` is raised at step construction.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#format-step[reference]

[[group-step]]
=== group()

*Description:* Groups objects in the traversal stream by a key and applies a reducing operation to the grouped values.

*Syntax:* `group()` | `group(sideEffectKey: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()` |`any` |`any`
|=========================================================

*Arguments:*

* `sideEffectKey` - The name of the side-effect key that will hold the aggregated grouping. When provided, the step
operates as a side-effect barrier step and passes the traverser to the next step unchanged. If `sideEffectKey` is
omitted, the step operates as a reducing barrier step, and a single traverser containing the computed map is passed to
the next step.

*Modulation:*

* `by()` - The first `by()` determines the key for grouping. The second `by()` determines the value to be reduced for
each group. If no key `by()` is provided, the object itself is used as the key. If no value `by()` is provided, the
objects are collected into a list.

*Considerations:*

The `group()` step can be used as both a reducing barrier step and a side-effect step. As a reducing barrier step (no
side-effect key), it returns a `MAP<any, any>` where keys are the grouping criteria and values are the reduced
results. As a side-effect step, it stores the grouping in a side-effect and passes the traverser to the next step
unchanged. In both forms, this step is a barrier and must fully iterate the traversal before returning any results.

*Exceptions:*

* If more than 2 `by()` modulators are provided, a `State Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupSideEffectStep.java[source (sideEffect)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#group-step[reference]

[[groupcount-step]]
=== groupCount()

*Description:* Counts the number of times a particular object has been part of a traversal, returning a `MAP` where the
object is the key and the value is the count.

*Syntax:* `groupCount()` | `groupCount(sideEffectKey: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()` |`any` |`any`
|=========================================================

*Arguments:*

* `sideEffectKey` - The name of the side-effect key that will hold the aggregated grouping. When provided, the step
operates as a side-effect step and passes the traverser to the next step unchanged.

*Modulation:*

* `by()` - Determines how to transform the object before counting. If not specified, the object itself is used as the
key.

*Considerations:*

The `groupCount()` step can be used as both a map step and a side-effect step. As a map step, it returns a
`MAP<any, LONG>` with the counted objects as keys and their counts as values. As a side-effect step, it stores the
counts in a side-effect and passes the traverser to the next step unchanged. Note that both the map and side-effect
forms of this step are barriers that must fully iterate the traversal before returning any results.

*Exceptions:*

* If multiple `by()` modulators are provided, a `State Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroupCountStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountSideEffectStep.java[source (sideEffect)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#groupcount-step[reference]

[[has-step]]
=== has()

*Description:* Filters traversers by property existence, property value, label, or identifier.

*Syntax:* `has(key: STRING)` | `has(key: STRING, value: any)` | `has(key: STRING, predicate: P)` | `has(key: STRING, traversal: Traversal)` | `has(accessor: T, value: any)` | `has(accessor: T, predicate: P)` | `has(accessor: T, traversal: Traversal)` | `has(label: STRING, key: STRING, value: any)` | `has(label: STRING, key: STRING, predicate: P)` | `has(label: STRING, key: STRING, traversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` / `Property` |`Element` / `Property`
|=========================================================

*Arguments:*

* `key` - The property key to check.
* `accessor` - A `T` accessor: `T.id` (element identifier), `T.label` (element label), `T.key` (property key), or
  `T.value` (property value).
* `value` - The value to compare against using equality.
* `predicate` - A `P` predicate for comparison. The predicate value may be a literal or a `Traversal`.
* `traversal` - A child traversal whose first result is used as the comparison value (implicitly wrapped in `P.eq()`).
  Must be read-only (no mutating steps). The child traversal is evaluated per-traverser: the current traverser seeds
  the child, and the first result is used for comparison.
* `label` - A label filter applied before the property check (three-argument form).

*Modulation:*

None

*Considerations:*

* `has(key)` filters to elements that have the specified property (existence check).
* `has(T.label, value)` is equivalent to `hasLabel(value)`. `has(T.id, value)` is equivalent to `hasId(value)`.
* When the domain is a `Property` (e.g., from `properties()`), `T.key` and `T.value` accessors are used to filter
  by property key or property value respectively.
* When the predicate value for `T.id` is a `STRING`, the element's `id().toString()` is used for comparison.
* For multi-valued properties (list/set cardinality), `has(key, value)` passes if *any* property with that key matches.
* When a `Traversal` is supplied as a value or inside a `P`, it is resolved per-traverser and its first result is used.
* Child traversals must be read-only. Mutating steps are rejected with an `Argument Error`.
* `has(key, null)` filters to elements where the property value is `null` (not an existence check).
* `has(null)` and `has(null, value)` always produce an empty result.

*Exceptions:*

* An `Argument Error` is raised if a child traversal contains mutating steps.
* A `State Error` is raised if the traverser is not an `Element` or `Property`.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#has-step[reference]

[[hasid-step]]
=== hasId()

*Description:* Filters elements by their identifier.

*Syntax:* `hasId(id: any, otherIds: any...)` | `hasId(predicate: P)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` |`Element`
|=========================================================

*Arguments:*

* `id` - One or more identifiers, or Traversals supplying identifiers to match against. Arrays and list-valued
  arguments are flattened (unrolled) into individual IDs. Multiple IDs are combined into `P.within(...)`.
* `predicate` - A `P` predicate for identifier comparison. The predicate value may contain a `Traversal`.

*Modulation:*

None

*Considerations:*

* Equivalent to `has(T.id, ...)`.
* When the predicate value is a `STRING`, the element's `id().toString()` is used for comparison.
* `hasId(null)` filters all elements since `T.id` cannot be null.
* Child traversals must be read-only; mutating steps are rejected with an `Argument Error`.

*Exceptions:*

* An `Argument Error` is raised if a child traversal contains mutating steps.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#has-step[reference]

[[haskey-step]]
=== hasKey()

*Description:* Filters properties by their key.

*Syntax:* `hasKey(key: STRING, otherKeys: STRING...)` | `hasKey(predicate: P)` | `hasKey(traversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Property` / `VertexProperty` |`Property` / `VertexProperty`
|=========================================================

*Arguments:*

* `key` - One or more keys to match against.
* `predicate` - A `P` predicate for key comparison. The predicate value may contain a `Traversal`.
* `traversal` - A child traversal whose first result is used as the key (implicitly wrapped in `P.eq()`).
  Must be read-only.

*Modulation:*

None

*Considerations:*

* Equivalent to `has(T.key, ...)`.
* Operates on `Property` or `VertexProperty` traversers (typically reached via `properties()`).
* Not intended for testing key existence on vertices/edges, use `has(key)` for that purpose.
* Child traversals must be read-only; mutating steps are rejected with an `Argument Error`.

*Exceptions:*

* An `Argument Error` is raised if a child traversal contains mutating steps.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#has-step[reference]

[[haslabel-step]]
=== hasLabel()

*Description:* Filters elements by their label.

*Syntax:* `hasLabel(label: STRING, otherLabels: STRING...)` | `hasLabel(predicate: P)` | `hasLabel(traversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` |`Element`
|=========================================================

*Arguments:*

* `label` - One or more labels to match against.
* `predicate` - A `P` predicate for label comparison. The predicate value may contain a `Traversal`.
* `traversal` - A child traversal whose first result is used as the label (implicitly wrapped in `P.eq()`).
  Must be read-only.

*Modulation:*

None

*Considerations:*

* Equivalent to `has(T.label, ...)`.
* Child traversals must be read-only; mutating steps are rejected with an `Argument Error`.

The label test is applied to each label the element carries and the element is retained when at least one label
satisfies it. For single-label elements this is equivalent to testing the label directly. For multi-label vertices a
negation predicate therefore retains any vertex carrying at least one other label, so a vertex labelled both "person"
and "employee" satisfies `hasLabel(neq("person"))`. Excluding every element that carries a given label requires
`not(hasLabel("person"))`. The same rule makes `hasLabel("a", "b")` a disjunction, matching when either label is
present, while chained `hasLabel("a").hasLabel("b")` is a conjunction.

*Exceptions:*

* An `Argument Error` is raised if a child traversal contains mutating steps.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#has-step[reference]

[[hasvalue-step]]
=== hasValue()

*Description:* Filters properties by their value.

*Syntax:* `hasValue(value: any, otherValues: any...)` | `hasValue(predicate: P)` | `hasValue(traversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Property` / `VertexProperty` |`Property` / `VertexProperty`
|=========================================================

*Arguments:*

* `value` - One or more values to match against.
* `predicate` - A `P` predicate for value comparison. The predicate value may contain a `Traversal`.
* `traversal` - A child traversal whose first result is used as the value (implicitly wrapped in `P.eq()`).
  Must be read-only.

*Modulation:*

None

*Considerations:*

* Equivalent to `has(T.value, ...)`.
* Operates on `Property` or `VertexProperty` traversers (typically reached via `properties()`).
* Child traversals must be read-only; mutating steps are rejected with an `Argument Error`.

*Exceptions:*

* An `Argument Error` is raised if a child traversal contains mutating steps.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#has-step[reference]

[[label-step]]
=== label()

*Description:* Maps an element to its label as a single string value.

*Syntax:* `label()`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` |`String`
|=========================================================

*Considerations:*

The `label()` step returns exactly one label string per element. For graphs that support multiple labels per vertex,
`label()` returns only one of the vertex's labels and the choice is non-deterministic (implementation-dependent
ordering). Use `labels()` to retrieve all labels reliably.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LabelStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#label-step[reference]

[[labels-step]]
=== labels()

*Description:* Maps an element to its labels, emitting one traverser per label.

*Syntax:* `labels()`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` |`String`
|=========================================================

*Considerations:*

For vertices with a single label, `labels()` emits one traverser (equivalent to `label()`). For multi-label vertices,
it emits one traverser per label. For vertices that carry no labels, no traversers are emitted.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LabelsStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#labels-step[reference]

[[length-step]]
=== length()

*Description:* Returns the length of the incoming string or list, if `Scope.local` is specified, returns
the length of each string element inside incoming list traverser.

*Syntax:* `length()` | `length(scope: Scope)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING`/`LIST` |`INT`/`LIST`
|=========================================================

*Arguments:*

* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LengthGlobalStep.java[source],
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(localTraversal: Traversal)`

[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.

*Exceptions:*

None

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()

*Description:* Adds the intersection to the Traversal Stream.

*Syntax:* `intersect(values: any)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`SET`
|=========================================================

*Arguments:*

* `values` - A list of items or a `Traversal` that will produce a list of items.


*Modulation:*

None

*Considerations:*

A set is returned after the intersect operation is applied so there won't be duplicates. This step only applies to list
types which means that non-iterable types (including null) will cause exceptions to be thrown.

*Exceptions:*

* If the incoming traverser isn't a list then an `Argument Error` is raised.
* If the argument doesn't resolve to a list then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/IntersectStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#intersect-step[reference]

[[is-step]]
=== is()

*Description:* Filters scalar values by testing them against a predicate or value.

*Syntax:* `is(value: any)` | `is(predicate: P)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`any`
|=========================================================

*Arguments:*

* `value` - A literal value or a `Traversal` to test equality against. `value` is implicitly wrapped
  in `P.eq(value)`.
* `predicate` - A `P` predicate for comparison. The predicate value may be a literal or a `Traversal`.

*Modulation:*

None

*Considerations:*

* When a `Traversal` is supplied as a value or inside a `P`, it is resolved per-traverser: the current traverser seeds
  the child traversal and its first result is used for comparison.
* If the child traversal produces no result, the comparison is aborted and the traverser is filtered out.
* Child traversals must be read-only; mutating steps are rejected with an `Argument Error`.
* Type mismatches during comparison (e.g., comparing a number to a string) result in the traverser being filtered.

*Exceptions:*

* An `Argument Error` is raised if a child traversal contains mutating steps.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#is-step[reference]

[[conjoin-step]]
=== conjoin()

*Description:* Joins every element in a list together into a String. 

*Syntax:* `conjoin(delimiter: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`STRING`
|=========================================================

*Arguments:*

* `delimiter` - A delimiter to use to join the elements together. Can't be null.

*Modulation:*

None

*Considerations:*

Every element in the list (except `null`) is converted to a String. Null values are ignored. The delimiter is inserted
between neighboring elements to form the final result. This step only applies to list types which means that
non-iterable types (including `null`) will cause exceptions to be thrown.

*Exceptions:*

* If the incoming traverser isn't a list then an `Argument Error` is raised.
* If the argument doesn't resolve to a list then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConjoinStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#conjoin-step[reference]

[[lTrim-step]]
=== lTrim()

*Description:* Returns a string with leading whitespace removed.

*Syntax:* `lTrim()` | `lTrim(scope: Scope)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING`/`LIST` |`STRING`/`LIST`
|=========================================================

*Arguments:*

* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LTrimGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/LTrimLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#lTrim-step[reference]

[[match-step]]
=== match()

*Description:* Executes a declarative pattern match query against the graph using a provider-supported query
string. The query string format is provider-specific; providers may use TinkerPop's optional `gql-gremlin` module
(which implements the <<tinkergql,TinkerGQL>> dialect) or supply their own engine. Users should consult their
graph system's documentation to determine what format is supported. The second argument, when provided, supplies
bound parameters to the query; how (and whether) these are used is left to the provider.

*Syntax:*

* `match(matchQuery: STRING)` +
* `match(matchQuery: STRING, params: MAP<STRING, any>)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|Y |Y |Y (via `with()`) |`any` |`MAP<STRING, any>`
|=========================================================

*Arguments:*

* `matchQuery` - An opaque query string evaluated by the graph provider. For example, a GQL MATCH expression:
  `"MATCH (a:person)-[:knows]->(b:person)"`.
* `params` - Optional bound parameters passed to the provider alongside the query string. May be `null` or empty.

*Modulation:*

`with("queryLanguage", value)` - specifies which query language the provider should use to evaluate the string.
When omitted, the provider uses its native or default language.

*Considerations:*

* The step is a provider-delegation point. `gremlin-core` supplies only the placeholder `DeclarativeMatchStep`;
graph providers replace it with their own implementation via a `TraversalStrategy`.
* The traverser value emitted by the step is a `MAP<STRING, any>` containing one entry per named variable in
the matched pattern, keyed by variable name. Providers must set this map as the traverser value for each result
row.
* Each named variable is also recorded as a labeled entry in the traverser's path, so results remain retrievable
by name via `select()` when downstream steps require individual values.
* Anonymous elements in the pattern (those without a variable name) are not exposed in the map or path and cannot be
referenced by `select()`.
* When used as a spawn step on `GraphTraversalSource`, no upstream traversers are consumed; the provider
executes the query once and emits one traverser per result row.

*Exceptions:*

* If the step is reached and no supporting strategy has replaced the placeholder, an `Unsupported Operation` is
raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DeclarativeMatchStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#match-step[reference]

[[merge-step]]
=== merge()

*Description:* Adds the union of two sets (or two maps) to the Traversal Stream.

*Syntax:* `merge(values: any)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST`/`MAP` |`SET`/`MAP`
|=========================================================

*Arguments:*

* `values` - A list of items, a `MAP`, or a `Traversal` that will produce a list of items.

*Modulation:*

None

*Considerations:*

For iterable types, a set is returned after the merge operation is applied so there won't be duplicates. For maps, if
both maps contain the same key then the value yielded from the argument will be the value put into the merged map. This
step only applies to list types or maps which means that other non-iterable types (including null) will cause exceptions
to be thrown.

*Exceptions:*

* If the incoming traverser isn't a list or map then an `Argument Error` is raised.
* If the argument doesn't resolve to a list or map then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#merge-step[reference]

[[merge-e-step]]
=== mergeE()

*Description:* Provides upsert-like functionality for edges.

*Syntax:* `mergeE()` | `mergeE(searchCreate: MAP)` | `mergeE(searchCreate: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|Y |Y |`option()` |`MAP`/`Vertex` |`Edge`
|=========================================================

*Arguments:*

* `searchCreate` - A `MAP` used to match an `Edge` and if not found will be the default set of data to create the new one.
* `onCreate` - A `MAP` used to specify additional existence criteria and/or properties not already specified in `searchCreate`.
* `onMatch` - A `MAP` used to update the `Edge` that is found using the `searchCreate` criteria.
* `outV` - A `Vertex` that will be late-bound into the `searchCreate` and `onCreate` `Maps` for the `Direction.OUT` key,
or else another `MAP` used to search for that `Vertex`
* `inV` - A `Vertex` that will be late-bound into the `searchCreate` and `onCreate` `Maps` for the `Direction.IN` key,
or else another `MAP` used to search for that `Vertex`

The `searchCreate` and `onCreate` `MAP` instances must consist of any combination of:

* `T` - `id`, `label`
* `Direction` - `IN` or `to`, `OUT` or `from`
* Arbitrary `STRING` keys (which are assumed to be vertex properties).

The `onMatch` `MAP` instance only allows for `STRING` keys as the `id` and `label` of a `Vertex` are immutable as are
the incident vertices. Values for these valid keys that are `null` will be treated according to the semantics of the
`addE()` step.

The `MAP` that is used as the argument for `searchCreate` may be assigned from the incoming `Traverser` for the no-arg
`mergeE()`. If `mergeE(Map)` is used, then it will override the incoming `Traverser`. If `mergeE(Traversal)` is used,
the `Traversal` argument must resolve to a `MAP` and it would also override the incoming Traverser. The `onCreate` and
`onMatch` arguments are assigned via modulation as described below.

If `onMatch` is triggered the `Traverser` becomes the matched `Edge`, but the traversal still must return a `MAP`
instance to be applied. `Null` is considered semantically equivalent to an empty `MAP`.

[width="100%",options="header"]
|=========================================================
|Event |Empty `MAP` (or Null)
|Search |Matches all edges
|Create |New edge with defaults
|Update |No update to matched edge
|=========================================================

If `T.id` is used for `searchCreate` or `onCreate`, it may be ignored for edge creation if the `Graph` does not
support user supplied identifiers. `onCreate` inherits from `searchCreate` - values for `T.id`, `T.label`, and
`Direction.OUT/IN` do not need to be specified twice. Additionally, `onCreate` cannot override values in `searchCreate`
(i.e. `if (exists(x)) return(x) else create(y)` is not supported). An important point here is that validation for
`onCreate` must occur at traversal construction for static values (i.e. a `MAP`) and at runtime for dynamic values
(i.e. a `Traversal`).

*Modulation:*

* `option(Merge, Map)` - Sets the `onCreate` or `onMatch` arguments directly.
* `option(Merge, Traversal)` - Sets the `onCreate` or `onMatch` arguments dynamically where the `Traversal` must
resolve to a `MAP`.
* `option(Merge.outV/inV)` can also accept a `Traversal` that resolves to a `Vertex`, allowing `mergeE` to be combined
with `mergeV` via a `select` operation.

*Exceptions:*

* `MAP` arguments are validated for their keys resulting in exception if they do not meet requirements defined above.
* Use of `T.label` should always have a value that is a `STRING`.
* If `T.id`, `T.label`, and/or `Direction.IN/OUT` are specified in `searchCreate`, they cannot be overridden in `onCreate`.
* For late binding of the from and to vertices, `Direction.OUT` must be set to `Merge.outV` and `Direction.IN` must
be set to `Merge.inV`. Other combinations are not allowed and will result in exception.

*Considerations:*

* `mergeE()` (i.e. the zero-arg overload) can only be used mid-traversal. It is not a start step.
* As is common to Gremlin, it is expected that `Traversal` arguments may utilize `sideEffect()` steps.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#mergee-step[reference]

[[merge-v-step]]
=== mergeV()

*Description:* Provides upsert-like functionality for vertices.

*Syntax:* `mergeV()` | `mergeV(searchCreate: MAP)` | `mergeV(searchCreate: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|Y |Y |`option()` |`MAP` |`Vertex`
|=========================================================

*Arguments:*

* `searchCreate` - A `MAP` used to match a `Vertex` and if not found will be the default set of data to create the new one.
* `onCreate` - A `MAP` used to specify additional existence criteria and/or properties not already specified in `searchCreate`.
* `onMatch` - A `MAP` used to update the `Vertex` that is found using the `searchCreate` criteria.

The `searchCreate` and `onCreate` `MAP` instances must consist of any combination of `T.id`, `T.label`, or arbitrary
`STRING` keys (which are assumed to be vertex properties). The `onMatch` `MAP` instance only allows for `STRING` keys
as the `id` and `label` of a `Vertex` are immutable. `null` values for these valid keys are not allowed.

The `MAP` that is used as the argument for `searchCreate` may be assigned from the incoming `Traverser` for the no-arg
`mergeV()`. If `mergeV(Map)` is used, then it will override the incoming `Traverser`. If `mergeV(Traversal)` is used,
the `Traversal` argument must resolve to a `MAP` and it would also override the incoming Traverser. The `onCreate` and
`onMatch` arguments are assigned via modulation as described below.

If `onMatch` is triggered the `Traverser` becomes the matched `Vertex`, but the traversal still must return a `MAP`
instance to be applied. `Null` is considered semantically equivalent to an empty `MAP`.

[width="100%",options="header"]
|=========================================================
|Event |Empty `MAP` (or Null)
|Search |Matches all vertices
|Create |New vertex with defaults
|Update |No update to matched vertex
|=========================================================

If `T.id` is used for `searchCreate` or `onCreate`, it may be ignored for vertex creation if the `Graph` does not
support user supplied identifiers. `onCreate` inherits from `searchCreate` - values for `T.id`, `T.label` do not need
to be specified twice. Additionally, `onCreate` cannot override values in `searchCreate` (i.e.
`if (exists(x)) return(x) else create(y)` is not supported). An important point here is that validation for `onCreate`
must occur at traversal construction for static values (i.e. a `MAP`) and at runtime for dynamic values (i.e. a
`Traversal`).

*Modulation:*

* `option(Merge, Map)` - Sets the `onCreate` or `onMatch` arguments directly.
* `option(Merge, Traversal)` - Sets the `onCreate` or `onMatch` arguments dynamically where the `Traversal` must
resolve to a `MAP`.

*Exceptions:*

* `MAP` arguments are validated for their keys resulting in exception if they do not meet requirements defined above.
* Use of `T.label` should always have a value that is a `STRING`.
* If `T.id` and/or `T.label` are specified in `searchCreate`, they cannot be overridden in `onCreate`.

*Considerations:*

* `mergeV()` (i.e. the zero-arg overload) can only be used mid-traversal. It is not a start step.
* As is common to Gremlin, it is expected that `Traversal` arguments may utilize `sideEffect()` steps.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#mergev-step[reference]

[[none-step]]
=== none()

*Description:* Filters list data from the traversal stream if none of the list's items match the supplied predicate.

*Syntax:* `none(predicate: P)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`LIST`
|=========================================================

*Arguments:*

* `predicate` - The predicate used to test each value in the list data.

*Modulation:*

None

*Considerations:*

Each value will be tested using the supplied predicate. Empty lists always pass through and null/non-list traversers
will be filtered out of the traversal stream.

*Exceptions:*

* An `Argument Error` is raised if the input predicate is `null`.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#none-step[reference]

[[product-step]]
=== product()

*Description:* Adds the cartesian product to the Traversal Stream.

*Syntax:* `product(values: any)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`LIST` |`LIST(LIST)`
|=========================================================

*Arguments:*

* `values` - A list of items or a `Traversal` that will produce a list of items.

*Modulation:*

None

*Considerations:*

A list of lists is returned after the product operation is applied with the inner list being a result pair. This step only
applies to list types which means that non-iterable types (including null) will cause exceptions to be thrown.

*Exceptions:*

* If the incoming traverser isn't a list then an `Argument Error` is raised.
* If the argument doesn't resolve to a list then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProductStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#product-step[reference]

[[project-step]]
=== project()

*Description:* Projects the current object in the stream into a `MAP` that is keyed by the provided labels.

*Syntax:* `project(projectKey: STRING, otherProjectKeys: STRING...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()` |`any` |`MAP<STRING, any>`
|=========================================================

*Arguments:*

* `projectKey` - The first key to use in the resulting map.
* `otherProjectKeys` - Additional keys to use in the resulting map.

*Modulation:*

* `by()` - Determines how to transform the current object for each key in the resulting map. The number of `by()`
modulations should match the number of keys provided.

*Considerations:*

The `project()` step is similar to `select()` but instead of retrieving historic traverser state, it modulates the
current state of the traverser. Each key in the resulting map corresponds to a `by()` modulation in the order they are
provided. If a `by()` modulation doesn't produce a value for a particular key (not productive), that key will be omitted
from the resulting `MAP`.

*Exceptions:*

* If duplicate keys are provided, an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ProjectStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#project-step[reference]

[[property-step]]
=== property()

*Description:* Adds or sets properties on elements. This is a sideEffect step: it returns the element, not the
property it created.

*Syntax:* `property(key: any, value: any, keyValues: any...)` | `property(cardinality: Cardinality, key: any, value: any, keyValues: any...)` | `property(keyValues: MAP)` | `property(cardinality: Cardinality, keyValues: MAP)` | `property(mapTraversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Element` |`Element`
|=========================================================

*Arguments:*

* `key` - The property key. May be a literal or a `Traversal` whose first result is used as the key.
* `value` - The property value. May be a literal or a `Traversal` whose first result is used as the value.
* `keyValues` - Additional key/value pairs for meta-properties on vertex properties.
* `cardinality` - The vertex property cardinality (`single`, `list`, `set`). If `null`, uses the graph's default.
* `keyValues` (`MAP`) - A map of property key-value pairs to set. Values may use `CardinalityValue` to override
  the cardinality on a per-entry basis.
* `mapTraversal` - A child traversal that must produce a `MAP`. Each entry becomes a property on the element.
  Must be read-only.

*Modulation:*

None

*Considerations:*

* When following `addV()` or `addE()`, the step may be folded into the creation operation (only when cardinality is
  not specified and meta-properties are not included).
* Key and value arguments may be traversals; their first result is used per-traverser.
* A `MAP` argument (literal or from a traversal) sets multiple properties in one step.
* `property(null)` and `property(Cardinality, null)` are no-ops that return the input unchanged.
* Child traversals must be read-only; mutating steps are rejected with an `Argument Error`.
* If a map traversal does not produce a `MAP`, a `State Error` is raised.

*Exceptions:*

* An `Argument Error` is raised if a child traversal contains mutating steps.
* A `State Error` is raised if a map traversal does not produce a `MAP`.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#property-step[reference]

[[repeat-step]]
=== repeat()

*Description:* Iteratively applies a traversal (the "loop body") to all incoming traversers until a stopping
condition is met. Optionally, it can emit traversers on each iteration according to an emit predicate. The
repeat step supports loop naming and a loop counter via `loops()`.

*Syntax:* `repeat(repeatTraversal: Traversal)` | `repeat(loopName: STRING, repeatTraversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`emit()`, `until()`, `times()` |`any` |`any`
|=========================================================

*Arguments:*

* `repeatTraversal` - The traversal that represents the loop body to apply on each iteration.
* `loopName` - Optional name used to identify the loop for nested loops and to access a specific counter via
`loops(loopName)`.

*Modulation:*

* `emit()` | `emit(Traversal<?, ?> emitTraversal)` | `emit(Predicate<Traverser<?>> emitPredicate)` - Controls if/when a
  traverser is emitted to the downstream of repeat() in addition to being looped again. If supplied before `repeat(...)`
  the predicate is evaluated prior to the first iteration (pre-emit). If supplied after `repeat(...)`, the predicate is
  evaluated after each completed iteration (post-emit). Calling `emit()` without arguments is equivalent to a predicate
  that always evaluates to true at the given check position.
* `until(Traversal<?, ?> untilTraversal)` | `until(Predicate<Traverser<?>> untilPredicate)` - Controls when repetition
  stops. If supplied before `repeat(...)` the predicate is evaluated prior to the first iteration (pre-check). If the
  predicate is true, the traverser will pass downstream without any loop iteration. If supplied after `repeat(...)`, the
  predicate is evaluated after each completed iteration (post-check). When the predicate is true, the traverser stops
  repeating and passes downstream.
* `times(int n)` - Convenience for a loop bound. Equivalent to `until(loops().is(n))` when placed after `repeat(...)`
  (post-check), and equivalent to `until(loops().is(n))` placed before `repeat(...)` (pre-check) when specified before.
  See Considerations for details and examples.

*Considerations:*

- Evaluation order matters. The placement of `emit()` and `until()` relative to `repeat()` controls whether their
predicates are evaluated before the first iteration (pre) or after each iteration (post) allowing for `while/do` or
`do/while` semantics respectively:
  - Pre-check / pre-emit: when the modulator appears before `repeat(...)`.
  - Post-check / post-emit: when the modulator appears after `repeat(...)`.
- Global traversal scope: The `repeatTraversal` is a global child. This means all traversers entering the repeat body
are processed together as a unified stream with global semantics. `Barrier` (`order()`, `sample()`, etc.) steps within
the repeat traversal operate across all traversers collectively rather than in isolation per traverser.
- Loop counter semantics:
  - The loop counter for a given named or unnamed repeat is incremented once per completion of the loop body (i.e.,
after the body finishes), not before. Therefore, `loops()` reflects the number of completed iterations.
  - `loops()` without arguments returns the counter for the closest (innermost) `repeat()`. `loops("name")` returns the
counter for the named loop.
- Re-queuing for the next iteration:
  - After each iteration, if `until` is not satisfied at the post-check, the traverser is sent back into the loop body
for another iteration. If it is satisfied, the traverser exits the loop and proceeds downstream.
- Interaction of `times(n)`:
  - `g.V().repeat(x).times(2)` applies `x` exactly twice; no values are emitted unless `emit()` is specified.
  - `g.V().emit().repeat(x).times(2)` emits the original input (pre-emit) and then the results of each iteration.
  - Placing `times(0)` before `repeat(...)` yields no iterations and passes the input downstream unchanged.
  - Placing `times(0)` after `repeat(...)` yields the same as `times(1)` because of `do/while` semantics.
- Errors when `repeatTraversal` is missing:
  - Using `emit()`, `until()`, or `times()` without an associated `repeat()` will raise an error at iteration time with a
message containing: `The repeat()-traversal was not defined`.
- Nested repeats and loop names:
  - Nested `repeat()` steps maintain separate loop counters. Use `repeat("a", ...)` and `loops("a")` to reference a
    specific counter inside nested loops.

*Exceptions:*

* Using `emit()`, `until()`, or `times()` without a matching `repeat()` raises a `State Error` at runtime
  when the step is initialized during iteration with the message containing: `The repeat()-traversal was not defined`.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#repeat-step[reference]

[[replace-step]]
=== replace()

*Description:* Returns a string with the specified characters in the original string replaced with the new characters.

*Syntax:* `replace(oldChar: STRING, newChar: STRING)` | `replace(scope: Scope, oldChar: STRING, newChar: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING`/`LIST` |`STRING`/`LIST`
|=========================================================

*Arguments:*

* `oldChar` - The string character(s) in the original string to be replaced. Nullable, a null input will be a no-op and
the original string will be returned
* `newChar` - The string character(s) to replace with. Nullable, a null input will be a no-op and the original string
will be returned
* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ReplaceGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ReplaceLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#replace-step[reference]

[[reverse-step]]
=== reverse()

*Description:* Returns the reverse of the incoming traverser

*Syntax:* `reverse()`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`any` |`any`
|=========================================================

*Arguments:*

None

*Modulation:*

None

*Considerations:*

The behavior of reverse depends on the type of the incoming traverser. If the traverser is a string, then the string is
reversed. If the traverser is iterable then a list containing the items in reverse
order are returned. All other types (including null) are not processed and are returned unmodified.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ReverseStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#reverse-step[reference]

[[rTrim-step]]
=== rTrim()

*Description:* Returns a string with trailing whitespace removed.

*Syntax:* `rTrim()` | `rTrim(scope: Scope)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING`/`LIST` |`STRING`/`LIST`
|=========================================================

*Arguments:*

* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/RTrimGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/RTrimLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#rTrim-step[reference]

[[split-step]]
=== split()

*Description:* Returns a list of strings created by splitting the incoming string traverser around the matches of the given separator.

*Syntax:* `split(separator: STRING)` | `split(scope: Scope, separator: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING`/`LIST` |`LIST`
|=========================================================

*Arguments:*

* `separator` - The string character(s) used as delimiter to split the input string. Nullable, a null separator will split on
whitespaces. An empty string separator will split on each character.
* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#split-step[reference]

[[subgraph-step]]
=== subgraph()

*Description:* Extracts an edge-induced subgraph from the current graph based on the edges encountered during traversal.

*Syntax:* `subgraph(sideEffectKey: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`Edge` |`Edge`
|=========================================================

*Arguments:*

* `sideEffectKey` - The name of the side-effect key that will hold the extracted subgraph.

*Modulation:*

None

*Considerations:*

The `subgraph()` step is a side-effect step that extracts edges encountered during traversal and their incident vertices
into a new subgraph. The step passes traversers through unchanged while building the subgraph as a side-effect. Subgraph
step is a barrier and must fully iterate the traversal before returning any results.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SubgraphStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#subgraph-step[reference]

[[substring-step]]
=== substring()

*Description:* Returns a substring of the incoming string traverser with a 0-based start index (inclusive) and end index (exclusive).

*Syntax:* `substring(startIndex: INT, endIndex: INT)` | `substring(scope: Scope, startIndex: INT, endIndex: INT)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING`/`LIST` |`STRING`/`LIST`
|=========================================================

*Arguments:*

* `startIndex` - The start index, 0 based. If the start index is negative then it will begin at the specified index counted
from the end of the string, or 0 if it exceeds the string length.
* `endIndex` - The end index, 0 based. Optional, if it is not specific then all remaining characters will be returned. End
index &leq; start index will return the empty string.
* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SubstringLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#substring-step[reference]

[[toLower-step]]
=== toLower()

*Description:* Returns the lowercase representation of incoming string traverser, or if `Scope.local` is specified, returns
the lowercase representation of each string elements inside incoming list traverser.

*Syntax:* `toLower()` | `toLower(scope: Scope)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING` |`STRING`/`LIST`
|=========================================================

*Arguments:*

* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToLowerLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#toLower-step[reference]

[[toUpper-step]]
=== toUpper()

*Description:* Returns the uppercase representation of incoming string traverser, or if `Scope.local` is specified, returns
the uppercase representation of each string elements inside incoming list traverser.

*Syntax:* `toUpper()` | `toUpper(scope: Scope)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING`/`LIST` |`STRING`/`LIST`
|=========================================================

*Arguments:*

* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ToUpperLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#toUpper-step[reference]

[[tree-step]]
=== tree()

*Description:* Aggregates the paths of the traversal into a tree data structure.

*Syntax:* `tree()` | `tree(sideEffectKey: STRING)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()` |`any` |`any`
|=========================================================

*Arguments:*

* `sideEffectKey` - The name of the side-effect key that will hold the aggregated tree. When provided, the step
operates as a side-effect step and passes the traverser to the next step unchanged. If omitted, the step acts as a
reducing barrier step, and a single traverser containing the aggregated tree is passed to the next step.

*Modulation:*

* `by()` - Determines how to transform path objects before adding them to the tree. If not specified, the objects
themselves are used as tree nodes. If multiple `by()` modulators are provided, they will be applied to each level of the
tree according to a round robin. For example, if 2 `by()` modulators are present, the first `by()` will apply to the
first, third, fifth... levels of the tree, and the second `by()` will apply to the second, fourth, sixth... levels of
the tree. An unlimited number of `by()` modulators may be provided.

*Considerations:*

The `tree()` step can be used as both a map step and a side-effect step. As a map step, it returns a `Tree` data
structure representing the hierarchical paths taken during traversal. As a side-effect step, it stores the tree in a
side-effect and passes the traverser to the next step unchanged. The tree structure reflects the branching paths of the
traversal, with each level representing a step in the path.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TreeStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/TreeSideEffectStep.java[source (sideEffect)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#tree-step[reference]

[[trim-step]]
=== trim()

*Description:* Returns a string with leading and trailing whitespace removed.

*Syntax:* `trim()` | `trim(scope: Scope)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`STRING`/`LIST` |`STRING`/`LIST`
|=========================================================

*Arguments:*

* `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers.
The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside.

*Modulation:*

None

*Considerations:*

Null values from the incoming traverser are not processed and remain as null when returned.

*Exceptions:*

* For `Scope.global` or parameterless function calls, if the incoming traverser is a non-`STRING` value then an `Argument Error` is raised.
* For `Scope.local`, if the incoming traverser is not a string or a list of strings then an `Argument Error` is raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TrimGlobalStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TrimLocalStep.java[source (local)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#trim-step[reference]

[[v-step]]
=== V()

*Description:* Reads vertices from the graph by their identifiers. Can be used as a start step or mid-traversal.

*Syntax:* `V(vertexIds: any...)` | `V(idTraversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|Y |Y |N |`any` |`Vertex`
|=========================================================

*Arguments:*

* `vertexIds` - Zero or more vertex identifiers. If none are provided, all vertices are returned. Arrays and
  list-valued arguments are flattened (unrolled) into individual IDs.
* `idTraversal` - A child traversal which supplies vertex identifiers. The child traversal must be read-only
  (no mutating steps).

*Modulation:*

None

*Considerations:*

* When used as a start step with a traversal argument, a synthetic traverser seeds the child traversal.
* When used mid-traversal with a traversal argument, the current traverser seeds the child traversal.
* If no vertices match the provided identifiers, the step produces no results.
* Graph providers may optimize `V()` with subsequent `has()` filters by folding `HasContainer` predicates into the
  step for index-backed lookups.

*Exceptions:*

None

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#graph-step[reference]

[[valueMap-step]]
=== valueMap()

*Description:* Converts elements to a map representation of their properties.

*Syntax:* `valueMap(propertyKeys: STRING...)` | `valueMap(includeTokens: BOOLEAN, propertyKeys: STRING...)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()`/`with()` |`Element` |`MAP`
|=========================================================

*Arguments:*

* `includeTokens` - Determines if the result map should contain entries from `T` (such as `label` and `id`).

* `propertyKeys` - If `valueMap()` is provided a list of propertyKeys, then the result map will only contain entries
specified by propertyKeys. If the list is empty, then all properties are included.

*Modulation:*

* `by(Traversal)` - Only a single by() modulator is allowed and it applies to all map values.
* `with(key, value)` - Determines which optional entries to add to the map. The key is "tinkerpop.valueMap.tokens" and
the possible values are:
** 0 for no tokens
** 1 for including id (Element)
** 2 for including label (Vertex/Edge)
** 4 for including keys (VertexProperty)
** 8 for including values (VertexProperty)
** 15 for including all

*Considerations:*

A `Vertex` may carry multiple values for a single property key (`Cardinality.list` or `Cardinality.set`). For such
keys, the resulting map entry contains a `LIST` of the values. An `Edge` and a `VertexProperty` have at most one
value per key and the value is returned directly.

The label format in the map is controlled by source-level `with("multilabel")` and `with("singlelabel")` options, not
by the number of labels the graph permits per element. When `"multilabel"` is present, the label value is a
`SET<STRING>`. Likewise when
`"singlelabel"` is present, it is a single `STRING`. The two options are mutually exclusive. Configuring both
`"multilabel"` and `"singlelabel"` on the same source is rejected with a `State Error`. Note that it is a
provider choice whether an unconfigured `GraphTraversal` defaults to `multilabel` or `singlelabel` semantics. The
reference implementation defaults to `singlelabel` semantics.

*Exceptions*

None

*Exceptions:*

* If more than one `by()` modulator is supplied, an `Argument Error` is raised with the message
"valueMap()/propertyMap() step can only have one by modulator".
* If the `WithOptions.tokens` configuration receives any non-`INT` argument, an `Argument Error` is
raised.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/PropertyMapStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#valuemap-step[reference],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#propertymap-step[reference]

[[where-step]]
=== where()

*Description:* Filters traversers based on a predicate, a traversal, or a comparison between labeled steps.

*Syntax:* `where(predicate: P)` | `where(startKey: STRING, predicate: P)` | `where(whereTraversal: Traversal)`

[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |`by()` |`any` |`any`
|=========================================================

*Arguments:*

* `predicate` - A `P` predicate. The predicate value may reference a labeled step (scope key) or contain a `Traversal`
  whose first result is used as the comparison value.
* `startKey` - A label identifying the traverser to use as the subject of the comparison.
* `whereTraversal` - A filtering traversal; traversers pass if the traversal produces at least one result.

*Modulation:*

* `by()` - Extracts comparison values. The first `by()` applies to the subject (current traverser or `startKey`),
  the second applies to the predicate target. If a `by()` modulator is non-productive (produces no value), the
  traverser is filtered out.

*Considerations:*

* `where(P)` with a scope-label value compares the current traverser (or labeled traverser) against the labeled value.
* `where(P)` with a traversal value resolves the child traversal per-traverser and compares directly against the
  current value.
* `where(P)` can combine scope-label and traversal values in a `ConnectiveP`
  (e.g., `P.neq('a').and(P.gt(traversal))`).
* `where(Traversal)` is an existence filter: traversers pass if the child traversal produces at least one result.
* Child traversals inside predicates must be read-only; mutating steps are rejected with an `Argument Error`.
* In OLAP, the anonymous traversal of `where()` processes the current object "locally" within the vertex's
star graph. It cannot traverse to adjacent vertices' properties or edges.

*Exceptions:*

* An `Argument Error` is raised if a child traversal contains mutating steps.

See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java[source],
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java[source (traversal)],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#where-step[reference]
