This is an automated email from the ASF dual-hosted git repository.

xiazcy pushed a commit to branch multi-label-docs-update
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 1d8d15e17526371e3de00073127ca0d4378f2de9
Author: Yang Xia <[email protected]>
AuthorDate: Fri Jul 17 12:28:29 2026 -0700

    simplified multi-label upgrade section, updated reference/semantics & 
expanded provider docs
---
 docs/src/dev/provider/gremlin-semantics.asciidoc | 10 ++-
 docs/src/dev/provider/index.asciidoc             | 40 ++++++++++
 docs/src/reference/the-traversal.asciidoc        | 21 ++++++
 docs/src/upgrade/release-4.x.x.asciidoc          | 93 ++++++------------------
 4 files changed, 93 insertions(+), 71 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 773d5d6221..e5e7ed42a5 100644
--- a/docs/src/dev/provider/index.asciidoc
+++ b/docs/src/dev/provider/index.asciidoc
@@ -192,12 +192,52 @@ 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`
+** By default a vertex has exactly one immutable label. Graphs that want to 
support multiple, mutable, or zero
+labels should follow the <<multi-label-support,Multi-Label Support>> section 
below.
 * `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
 meta-properties, the `VertexProperty` needs to be implemented as an `Element`. 
`VertexProperty` should return empty
 iterable for properties if meta-properties is not supported.
 
+[[multi-label-support]]
+==== Multi-Label Support
+
+By default a vertex has exactly one immutable label, matching the 3.x 
behavior. Providers that want to support
+multiple, mutable, or zero labels declare their supported label cardinality via
+`Graph.Features.VertexFeatures#getLabelCardinality()`, which defaults to 
`LabelCardinality.ONE`. To support
+multi-label, return `ONE_OR_MORE` or `ZERO_OR_MORE` from this method. This 
value may be a static constant for
+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...)`, `Element.dropLabel(String, 
String...)`, and `Element.dropLabels()`
+- 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")` lets users 
force single-string output on sources
+that default to multi-label semantics. Because the two options request 
opposite formats, they cannot both be set on
+the same source — doing so raises a `VerificationException`.
+
+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.
+
+See the 
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#vertex-labels[Vertex 
Labels] reference and the
+<<gremlin-semantics,Gremlin Semantics>> section for the behavioral details.
+
 [[olap-implementations]]
 ==== OLAP Implementations
 
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 4e869d9039..4860c27c48 100644
--- a/docs/src/upgrade/release-4.x.x.asciidoc
+++ b/docs/src/upgrade/release-4.x.x.asciidoc
@@ -97,9 +97,8 @@ 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
@@ -108,14 +107,13 @@ gremlin.tinkergraph.vertexLabelCardinality=ZERO_OR_MORE
 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 following examples demonstrate how Gremlin behaves with multi-label 
enabled:
+With multi-label enabled, `addLabel()` and `dropLabel()` mutate a vertex's 
labels, and the new `labels()` step
+reliably returns every label — unlike `label()`, which returns only one and is 
now deprecated:
 
 ```text
 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
@@ -124,43 +122,17 @@ gremlin> 
g.V().has('name','marko').dropLabel('employee').labels()
 ==>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()` accepts a list for `T.label` to match or create multi-label 
vertices, with append-only `onMatch`
+semantics. By default `elementMap()` and `valueMap()` still return a single 
label string; use `with("multilabel")`,
+per-traversal or as a persistent source, to receive the full set. 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/#the-zoo-toy-graph[The 
Zoo]
 
 ==== Transactions
@@ -1094,33 +1066,16 @@ 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.
+Providers declare their supported label cardinality via 
`Graph.Features.VertexFeatures#getLabelCardinality()`, which
+defaults to `LabelCardinality.ONE` (the immutable single-label 3.x behavior). 
Returning `ONE_OR_MORE` or
+`ZERO_OR_MORE` opts a graph into multi-label, which then requires storing and 
returning a `Set<String>` from
+`Element.labels()`, implementing the `addLabel()`/`dropLabel()`/`dropLabels()` 
mutators, applying OR semantics in
+`hasLabel()`, and serializing labels as a list over the V4 GraphBinary format. 
The `LabelCardinalityValidator` utility
+enforces the min/max/mutation constraints of each cardinality. Label output 
from `elementMap()`/`valueMap()` is driven
+by the `with("multilabel")`/`with("singlelabel")` source options rather than 
the graph's cardinality.
+
+See the 
link:https://tinkerpop.apache.org/docs/4.0.0/dev/provider/#multi-label-support[provider
 documentation] for the
+full implementation checklist and `elementMap()`/`valueMap()` override 
guidance.
 
 ===== Traversal-Accepting Steps - HasContainer Guard
 

Reply via email to