This is an automated email from the ASF dual-hosted git repository.
Cole-Greer pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/master by this push:
new fb262302b1 Multilabel Docs Update (#3523)
fb262302b1 is described below
commit fb262302b13bc98d8214d6eca0e3c542d935832f
Author: Yang Xia <[email protected]>
AuthorDate: Fri Jul 17 15:13:18 2026 -0700
Multilabel Docs Update (#3523)
---------
Co-authored-by: Cole Greer <[email protected]>
---
docs/src/dev/provider/gremlin-semantics.asciidoc | 10 +-
docs/src/dev/provider/index.asciidoc | 6 ++
docs/src/reference/the-traversal.asciidoc | 21 ++++
docs/src/upgrade/release-4.x.x.asciidoc | 117 ++++++++---------------
4 files changed, 76 insertions(+), 78 deletions(-)
diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc
b/docs/src/dev/provider/gremlin-semantics.asciidoc
index ddb0b95b9e..b13b13e6dc 100644
--- a/docs/src/dev/provider/gremlin-semantics.asciidoc
+++ b/docs/src/dev/provider/gremlin-semantics.asciidoc
@@ -552,7 +552,7 @@
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addedge-step[reference]
*Description:* Adds a vertex to the graph.
-*Syntax:* `addV()` | `addV(vertexLabel: STRING)` | `addV(vertexLabelTraversal:
Traversal<any, STRING>)`
+*Syntax:* `addV()` | `addV(vertexLabel: STRING, moreLabels: STRING...)` |
`addV(vertexLabelTraversal: Traversal<any, STRING>, moreLabelTraversals:
Traversal<any, STRING>...)`
[width="100%",options="header"]
|=========================================================
@@ -563,7 +563,9 @@
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addedge-step[reference]
*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:*
@@ -572,11 +574,15 @@ 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.
+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)],
diff --git a/docs/src/dev/provider/index.asciidoc
b/docs/src/dev/provider/index.asciidoc
index f2f505c368..9262cb8191 100644
--- a/docs/src/dev/provider/index.asciidoc
+++ b/docs/src/dev/provider/index.asciidoc
@@ -192,6 +192,12 @@ The following bullets provide some tips to consider when
implementing the struct
`Graph open(Configuration)` method where the `Configuration` is an Apache
Commons class of that name. Alternatively, the
`Graph` implementation can have the `GraphFactoryClass` annotation which
specifies a class with that static
`Graph open(Configuration)` method.
+* `Vertex`
+** Providers must opt into a specific `VertexLabelCardinality` of `ONE`,
`ONE_OR_MORE`, or `ZERO_OR_MORE`. Any graph
+supporting a `VertexLabelCardinality` of `ONE_OR_MORE`, or `ZERO_OR_MORE` must
use a `Vertex` implementation which
+overrides the default `Element.labels()`, `Element.addLabel(String,
String...)`, `Element.dropLabel(String, String...)`,
+and `Element.dropLabels()` methods. See the
+link:https://tinkerpop.apache.org/docs/x.y.z/reference/#vertex-labels[Vertex
Labels] reference docs for more details.
* `VertexProperty`
** This interface is both a `Property` and an `Element` as `VertexProperty` is
a first-class graph element in that it
can have its own properties (i.e. meta-properties). Even if the implementation
does not intend to support
diff --git a/docs/src/reference/the-traversal.asciidoc
b/docs/src/reference/the-traversal.asciidoc
index 065952a3f3..a44084506a 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -655,10 +655,27 @@ g.V().has('name','nothing')
g.V().has('name','nothing').bothE()
----
+On a graph configured with a `LabelCardinality` that permits more than one
label (`ONE_OR_MORE` or `ZERO_OR_MORE`),
+`addV()` accepts additional labels so a vertex can be created with several
labels at once:
+
+[gremlin-groovy]
+----
+conf = new BaseConfiguration()
+conf.setProperty("gremlin.tinkergraph.vertexLabelCardinality", "ZERO_OR_MORE")
+graph = TinkerGraph.open(conf)
+g = traversal().with(graph)
+g.addV('person','employee').property('name','marko').labels() <1>
+----
+
+<1> The vertex is created carrying both the "person" and "employee" labels.
Use <<addlabel-step,`addLabel()`>> to
+add further labels after creation. With the default `LabelCardinality.ONE`,
supplying more than one label to `addV()`
+throws, since a vertex may carry only a single immutable label.
+
*Additional References*
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addV()++[`addV()`],
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addV(java.lang.String)++[`addV(String)`],
+link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addV(java.lang.String,java.lang.String...)++[`addV(String,
String...)`],
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addV(org.apache.tinkerpop.gremlin.process.traversal.Traversal)++[`addV(Traversal)`]
[[addlabel-step]]
@@ -2773,6 +2790,9 @@
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre
The `label()`-step (*map*) takes an `Element` and extracts its label from it.
+NOTE: As of 4.0.0, `label()` is deprecated in favor of the
<<labels-step,`labels()`>> step, which reliably returns
+every label an element carries.
+
[gremlin-groovy,modern]
----
g.V().label()
@@ -2802,6 +2822,7 @@
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre
The `labels()`-step (*flatMap*) takes an `Element` and emits each of its
labels as a separate traverser. For elements
with a single label this behaves identically to `label()`. For multi-label
elements, it emits one traverser per label.
+A vertex with no labels (possible only under `LabelCardinality.ZERO_OR_MORE`)
emits nothing.
[gremlin-groovy,theZoo]
----
diff --git a/docs/src/upgrade/release-4.x.x.asciidoc
b/docs/src/upgrade/release-4.x.x.asciidoc
index 39f72fa69f..5221c0a9a0 100644
--- a/docs/src/upgrade/release-4.x.x.asciidoc
+++ b/docs/src/upgrade/release-4.x.x.asciidoc
@@ -97,70 +97,47 @@ See:
link:https://tinkerpop.apache.org/docs/4.0.0/reference/#match-step[match()]
Until now, vertices in the property graph model were limited to a single,
immutable label assigned at creation. This
release introduces configurable label cardinality, allowing vertices to carry
multiple labels that can be added and
removed over their lifetime. This is an optional feature for graph providers
and not all will support it in the same
-way. TinkerGraph offers a configurable approach to enabling the the feature
using one of the `LabelCardinality` settings
-as a graph-level setting that defaults to `ONE`, preserving the existing
single-label behavior. To enable multi-label
-in TinkerGraph, set the vertex label cardinality in the graph properties:
+way. TinkerGraph enables it through a graph-level `LabelCardinality` setting
that defaults to `ONE`, preserving the
+existing single-label behavior:
-```
-gremlin.tinkergraph.vertexLabelCardinality=ZERO_OR_MORE
-```
+[source,groovy]
+----
+gremlin.tinkergraph.vertexLabelCardinality=ONE
+----
-The three modes are `ONE` (single, immutable, the 3.x default), `ONE_OR_MORE`
(mutable, minimum one), and
-`ZERO_OR_MORE` (fully flexible). Edge labels remain fixed at `ONE`.
+The three modes are `ONE` (exactly one immutable label per vertex),
`ONE_OR_MORE` (requires at least one label but
+permits additional labels to be added or removed), and
+`ZERO_OR_MORE` (allows a vertex to have any number of labels including none).
Edge labels remain fixed at `ONE`.
-The following examples demonstrate how Gremlin behaves with multi-label
enabled:
+With `ONE_OR_MORE` or `ZERO_OR_MORE`, `addLabel()` and `dropLabel()` mutate a
vertex's labels. The new `labels()` step
+produces all labels, while the legacy `label()` step continues to produce a
single label only, and is now deprecated:
-```text
+[source,groovy]
+----
gremlin> g.addV('person','employee').property('name','marko')
==>v[0]
-gremlin> g.V().has('name','marko').addLabel('manager')
-==>v[0]
-gremlin> g.V().has('name','marko').labels()
+gremlin> g.V().has('name','marko').addLabel('manager').labels()
==>person
==>employee
==>manager
gremlin> g.V().has('name','marko').dropLabel('employee').labels()
==>person
==>manager
-```
-
-The `labels()` step is a flatMap that emits one traverser per label, unlike
`label()` which returns a single string.
-For single-label vertices the two behave identically.
-
-The `mergeV()` step accepts a list for `T.label` to match or create
multi-label vertices. The `onMatch` option uses
-append-only semantics: new labels are added, existing labels are preserved:
+----
-```text
-gremlin> g.mergeV([(T.label): ['person','employee'], name: 'marko'])
-==>v[0]
-gremlin> g.mergeV([(T.label): 'person', name: 'marko']).option(Merge.onMatch,
[(T.label): 'director'])
-==>v[0]
-```
-
-By default, `elementMap()` and `valueMap()` continue to return labels as a
single string. To receive all labels as a
-set, use `with("multilabel")` either per-traversal or as a persistent source
configuration:
-
-```text
-// assuming a vertex with labels [person, employee, manager] and name "marko"
-gremlin> g.with("multilabel").V().has('name','marko').elementMap()
-==>{id=0, label=[manager, person, employee], name=marko}
-gremlin> g.V().has('name','marko').elementMap()
-==>{id=0, label=manager, name=marko}
-```
-
-To avoid repeating `with("multilabel")` on every traversal, create a
persistent source:
-
-```text
-gremlin> gml = g.with("multilabel")
-==>graphtraversalsource[tinkergraph[vertices:1 edges:0], standard]
-gremlin> gml.V().has('name','marko').elementMap()
-==>{id=0, label=[manager, person, employee], name=marko}
-gremlin> gml.V().has('name','marko').valueMap(true)
-==>{id=0, label=[manager, person, employee], name=[marko]}
-```
+`mergeV()` now optionally accepts a list for `T.label` to match or create
multi-label vertices. By default
+`elementMap()` and `valueMap()` still return a single label string, and a new
traversal-source level
+`with("multilabel")` config alters the results to wrap labels in lists. The
reference documentation covers each of these
+behaviors in detail.
See: link:https://issues.apache.org/jira/browse/TINKERPOP-3261[TINKERPOP-3261],
-link:https://tinkerpop.apache.org/docs/4.0.0/reference/#tinkergraph-multi-label[Multi-Label],
link:https://tinkerpop.apache.org/docs/4.0.0/reference/#labels-step[labels()],
+link:https://tinkerpop.apache.org/docs/4.0.0/reference/#tinkergraph-multi-label[Multi-Label],
+link:https://tinkerpop.apache.org/docs/4.0.0/reference/#vertex-labels[Vertex
Labels],
+link:https://tinkerpop.apache.org/docs/4.0.0/reference/#labels-step[labels()],
+link:https://tinkerpop.apache.org/docs/4.0.0/reference/#addlabel-step[addLabel()],
+link:https://tinkerpop.apache.org/docs/4.0.0/reference/#elementmap-step[elementMap()],
+link:https://tinkerpop.apache.org/docs/4.0.0/reference/#valuemap-step[valueMap()],
+link:https://tinkerpop.apache.org/docs/4.0.0/reference/#mergevertex-step[mergeV()],
link:https://tinkerpop.apache.org/docs/4.0.0/reference/#the-zoo-toy-graph[The
Zoo]
==== More Dynamic Arguments in Gremlin
@@ -1081,33 +1058,21 @@ expected for the 4.0.0 major release.
===== Multi-Label Support
-Providers declare their supported label cardinality via
`Graph.Features.getLabelCardinality()`, which defaults to
-`LabelCardinality.ONE` (single label, immutable, the 3.x behavior). To support
multi-label, return `ONE_OR_MORE` or
-`ZERO_OR_MORE` from this method. This value may be a static constant for a
graphs which do not offer configurable label
-cardinality. The label cardinality should reflect the current `Graph` instance.
-
-The `LabelCardinality` enum exposes `min()`, `max()`, and `supportsMutation()`
for programmatic introspection of
-constraints. Constraint enforcement is handled by the
`LabelCardinalityValidator` utility class, which providers may
-use directly or replace with their own validation logic tailored to their
storage backend.
-
-Providers implementing multi-label must:
-
-- Store and return a `Set<String>` from `Element.labels()`
-- Implement `Element.addLabel(String, String...)` and
`Element.dropLabel(String, String...)`
-- Ensure `hasLabel("a", "b")` uses OR semantics (matches vertices with label
"a" or "b")
-- Serialize/deserialize the label set via the V4 GraphBinary format (which
sends labels as a list)
-
-The default `elementMap()` and `valueMap()` implementations determine label
output format solely from the
-`with("multilabel")` source option, not from the graph's `LabelCardinality`.
This means a multi-label graph still
-returns a single label string from these steps unless `with("multilabel")` is
explicitly configured. The intent is
-to eventually deprecate the single-string path entirely and always return
labels as a set, aligning `elementMap()`
-and `valueMap()` with `labels()`. Until then, `with("singlelabel")` allows
users to override a source-level
-`with("multilabel")` back to single-string output.
-
-Providers who want `elementMap()`/`valueMap()` to return the full label set by
default (without requiring users to
-set `with("multilabel")`) should override `PropertyMapStep` and
`ElementMapStep` to tie label output to their
-server-side label cardinality configuration. In that case,
`with("singlelabel")` should still be respected as an
-explicit user override back to single-string output.
+The TinkerPop data model now allows providers to configure label cardinalities
on vertices. Providers declare their
+supported label cardinality via
`Graph.Features.VertexFeatures#getLabelCardinality()`, which must be set at one
of three
+levels: `LabelCardinality.ONE` (consistent with TP3 semantics), `ONE_OR_MORE`
or `ZERO_OR_MORE`. A choice of either
+`ONE_OR_MORE` or `ZERO_OR_MORE` requires an updated `Vertex` implementation,
which overrides the default
+`Element.labels()`, `Element.addLabel()`, `Element.dropLabel()` and
`Element.dropLabels()` methods.
+
+Providers may also choose default semantics for the `elementMap()` and
`valueMap()` steps. The `T.label` fields in these
+maps may either be a `String` (consistent with TP3), or a `List<String>` (full
compatibility with multi-labeled
+vertices). Users can provide a `with("singlelabel")` or `with("multilabel")`
config on their traversal to explicitly opt
+into either of these behaviors. The reference implementation of `elementMap()`
and `valueMap()` defaults to
+"singlelabel" semantics for an unconfigured traversal. All providers must
either skip the `@SingleLabelDefault` or the
+`@MultiLabelDefault` tagged feature tests depending on their choice of default
behavior.
+
+See the
link:https://tinkerpop.apache.org/docs/4.0.0/reference/#vertex-labels[vertex
labels] reference docs more details
+regarding the new vertex label cardinalities.
===== Traversal-Accepting Steps - HasContainer Guard