You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2026/07/16 18:21:57 UTC
(tinkerpop) branch master updated (bcf238090b -> 27f2006dc0)
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
from bcf238090b Merge branch '3.8-dev'
new 4271c0f0c4 Register theZoo graph in the docs build
new 5cc84a043d Document hasLabel() label matching and fix singlelabel docs
new 08f779d883 Reorder 4.0.0-beta.3 upgrade docs by significance
new def6f26306 Document hasLabel() label matching semantics
new f1abcc4bf8 Remove Java implementation references from multi-label semantics
new 27f2006dc0 Correct type and error guidance in the tinker-doc skill
The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.skills/tinker-doc/references/books-and-voice.md | 39 +-
docs/src/dev/provider/gremlin-semantics.asciidoc | 71 ++--
docs/src/reference/the-traversal.asciidoc | 45 +-
docs/src/upgrade/release-4.x.x.asciidoc | 456 ++++++++++-----------
.../gremlin/docs/GremlinTreeprocessor.java | 1 +
5 files changed, 320 insertions(+), 292 deletions(-)
(tinkerpop) 03/06: Reorder 4.0.0-beta.3 upgrade docs by significance
Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 08f779d88366c1d67a37a4ea2cde2717a3964b0d
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Thu Jul 16 12:31:04 2026 -0400
Reorder 4.0.0-beta.3 upgrade docs by significance
Assisted-by: Claude Code:claude-opus-4-8
---
docs/src/upgrade/release-4.x.x.asciidoc | 456 +++++++++++++++-----------------
1 file changed, 219 insertions(+), 237 deletions(-)
diff --git a/docs/src/upgrade/release-4.x.x.asciidoc b/docs/src/upgrade/release-4.x.x.asciidoc
index 11c89536fc..8ef445cb7a 100644
--- a/docs/src/upgrade/release-4.x.x.asciidoc
+++ b/docs/src/upgrade/release-4.x.x.asciidoc
@@ -32,151 +32,6 @@ complete list of all the modifications that are part of this release.
=== Upgrading for Users
-==== Bindings are now Parameters
-
-The map of named values substituted into a Gremlin query was previously called "bindings" in some places and
-"parameters" in others. TinkerPop now refers to these consistently as *query parameters*, reserving "bindings" for
-the distinct concept of `ScriptEngine` variable bindings. As a result, the driver methods for supplying query
-parameters have been renamed. Update any code that sets them:
-
-- Java: `RequestMessage.Builder.addBindings(...)` is now `addParameters(...)`.
-- Python: `client.submit(..., bindings=...)` is now `submit(..., parameters=...)`, and the `request_options`
-key `bindings` is now `parameters`.
-- Go: `SetBindings`/`SetBindingsString`/`AddBinding` are now
-`SetParameters`/`SetParametersString`/`AddParameter`.
-- .NET: `AddBinding`/`AddBindings`/`AddBindingsString` are now
-`AddParameter`/`AddParameters`/`AddParametersString`.
-- JavaScript: `addBinding`/`addBindings`/`addBindingsString` are now
-`addParameter`/`addParameters`/`addParametersString`, and the `RequestOptions.bindings` field is now `parameters`.
-
-For example, in the Java driver:
-
-[source,java]
-----
-final Map<String, Object> params = new HashMap<>();
-params.put("x", 1);
-
-// 3.x
-RequestMessage.build("g.V(x)").addBindings(params).create();
-
-// 4.x
-RequestMessage.build("g.V(x)").addParameters(params).create();
-----
-
-See: link:https://issues.apache.org/jira/browse/TINKERPOP-3262[TINKERPOP-3262]
-
-==== Standardizing GLV Connection Options
-
-TinkerPop 4.x standardizes connection option names and defaults across all five Gremlin Language Variants (Java, Python,
-.NET, Go, and JavaScript). Each driver using its language-idiomatic casing (`camelCase`, `PascalCase`, or `snake_case`).
-These renames are breaking, the old option names have been removed.
-
-NOTE: Timeouts use a millisecond-suffixed canonical name (`connectTimeoutMillis`, `readTimeoutMillis`,
-`idleTimeoutMillis`, `keepAliveTimeMillis`, and the `_millis` form in Python). Java, Go, .NET, and Python also accept an
-idiomatic duration companion for the same setting (Java `Duration`, Go `time.Duration`, .NET `TimeSpan`, Python seconds);
-set only one form per option. JavaScript exposes only the millisecond form.
-
-===== Standardized options (cross-GLV)
-
-The table lists each standardized option by driver. Defaults are shown in parentheses; "n/a" means the driver does not
-expose that option.
-
-[width="100%",cols="2,2,2,2,2,2",options="header"]
-|=========================================================
-|Concept |Java (`Cluster.Builder`) |Python (kwarg) |.NET (`ConnectionSettings`) |Go (settings field) |JavaScript (option)
-|Max pooled connections |`maxConnections` (128) |`max_connections` (128) |`MaxConnections` (128) |`MaxConnections` (128) |`maxConnections` (128)
-|Connect timeout |`connectTimeoutMillis` (5000) |`connect_timeout_millis` (5000) |`ConnectTimeoutMillis` (5000) |`ConnectTimeoutMillis` (5000) |n/a
-|Idle-read timeout |`readTimeoutMillis` (0/off) |`read_timeout_millis` (off) |`ReadTimeoutMillis` (0/off) |`ReadTimeoutMillis` (0/off) |`readTimeoutMillis` (undici default)
-|Pool idle timeout |`idleTimeoutMillis` (180000) |`idle_timeout_millis` (180000) |`IdleTimeout` (180s) |`IdleTimeoutMillis` (180000) |n/a
-|TCP keep-alive idle |`keepAliveTimeMillis` (30000) |`keep_alive_time_millis` (30000) |`KeepAliveTime` (30s) |`KeepAliveTimeMillis` (30000) |`keepAliveTimeMillis` (30000)
-|Compression |`compression` (`DEFLATE`) |`compression` (`'deflate'`) |`Compression` (`Deflate`) |`Compression` (`CompressionDeflate`) |`compression` (`'deflate'`)
-|Connection-level batch size |`batchSize` (64) |`batch_size` (64) |`BatchSize` (64) |`BatchSize` (64) |`batchSize` (64)
-|Connection-level bulkResults |`bulkResults` (false) |`bulk_results` (false) |`BulkResults` (false) |`BulkResults` (false) |`bulkResults` (false)
-|Max response header bytes |`maxResponseHeaderBytes` (8192) |n/a |`MaxResponseHeaderBytes` (handler default) |`MaxResponseHeaderBytes` (net/http default) |`maxResponseHeaderBytes` (undici default)
-|TLS configuration |`ssl(SslContext)` + keystore builders |`ssl` (`SSLContext`) |`Ssl` (`SslClientAuthenticationOptions`) |`Ssl` (`*tls.Config`) |runtime (`NODE_EXTRA_CA_CERTS`, etc.)
-|HTTP proxy |`proxy(ProxyOptions)` |`proxy` |`Proxy` (`IWebProxy`) |`Proxy` (env default) |`proxy` (undici `ProxyAgent`)
-|Request interceptors |`interceptors` |`interceptors` |interceptor delegates |`Interceptors` |`interceptors`
-|=========================================================
-
-===== Behavior changes (all drivers)
-
-These change runtime behavior on upgrade even if you do not change your configuration:
-
-- *Compression defaults to on.* Every driver now defaults compression to `deflate` and sends `Accept-Encoding: deflate`.
- Disable it with the compression option's "none" value (`Compression.NONE`, `'none'`, `Compression.None`,
- `CompressionNone`).
-- *Connect timeout lowered to 5s* (from 15s, where applicable) and is now actually applied to transport establishment
- (TCP connect plus TLS handshake), not the whole request.
-- *`readTimeout` is an idle-read timeout*, reset on each inbound response chunk, so it is streaming-safe and bounds only
- the gap between chunks, not total response duration. It is off by default.
-- *`idleTimeout` reaps only pooled connections* that are idle between requests; it no longer bounds an in-flight
- response (that is `readTimeout`'s job).
-
-===== Driver-specific notes
-
-- *Java* (`gremlin-driver`): Renamed: `maxConnectionPoolSize`->`maxConnections`, `connectionSetupTimeoutMillis`->`connectTimeoutMillis`,
- `idleConnectionTimeoutMillis`->`idleTimeoutMillis`, `resultIterationBatchSize`->`batchSize`, `serializer`->`responseSerializer`,
- and `RequestOptions` `addG`->`traversalSource`. New: `readTimeoutMillis`, `keepAliveTimeMillis`,
- `maxResponseHeaderBytes`, `proxy(ProxyOptions)`, `url(String)`, `ssl(SslContext)`. Removed: `maxResponseContentLength`.
- `validationRequest` default reconciled to `g.inject(0)`.
-- *Python* (`gremlin-python`): Renamed: `pool_size`->`max_connections` (default 8->128) and `ssl_options`->`ssl`.
- New: `connect_timeout_millis`, `read_timeout_millis`, `idle_timeout_millis`, `keep_alive_time_millis`, `compression`,
- `batch_size`, `proxy`, `trust_env`. `auth.sigv4` gained an optional credentials provider. Removed: `headers` (use interceptors)
- and `max_content_length`.
-- *.NET* (`gremlin-dotnet`): Renamed: `MaxConnectionsPerServer`->`MaxConnections`, `ConnectionTimeout`->`ConnectTimeout`,
- `IdleConnectionTimeout`->`IdleTimeout`, `KeepAliveInterval`->`KeepAliveTime`, `EnableCompression`->`Compression`, and
- `Auth.BasicAuth`/`Auth.SigV4Auth`->`Auth.Basic`/`Auth.Sigv4`. New: `ReadTimeout`, `MaxResponseHeaderBytes`,
- `Proxy`, `Ssl`, `BulkResults`.
-- *Go* (`gremlin-go`): Renamed: `MaximumConcurrentConnections`->`MaxConnections`, `IdleConnectionTimeout`->`IdleTimeout`,
- `KeepAliveInterval`->`KeepAliveTime`, `ConnectionTimeout`->`ConnectTimeout`, `TlsConfig`->`Ssl`, `RequestInterceptors`->`Interceptors`,
- `EnableCompression`->`Compression`. Auth helpers moved from package `gremlingo`
- (`BasicAuth`/`SigV4Auth`/`SigV4AuthWithCredentials`) into a new `auth` sub-package (`auth.Basic`/`auth.SigV4`/`auth.SigV4WithCredentials`).
- New: `ReadTimeout(Millis)`, `MaxResponseHeaderBytes`, `Proxy` (defaults to `http.ProxyFromEnvironment`), `BatchSize`, `BulkResults`.
-- *JavaScript* (`gremlin-javascript`): adopted `undici` as a pinned dependency providing the default dispatcher built from the
- options above. Renamed: `reader`->`responseSerializer`. New: `readTimeoutMillis`, `keepAliveTimeMillis`, `maxResponseHeaderBytes`,
- `proxy`, `compression`, `batchSize`, `bulkResults`, `logger`. Removed `headers` (use interceptors) and the
- `ca`/`cert`/`pfx`/`rejectUnauthorized`/`agent` options (TLS is configured through the Node/undici runtime). undici is swapped
- out in browser bundles, where these connection-pool options are managed by the browser.
-
-See: link:https://lists.apache.org/thread/yqtr2wnb1kq2pqqq4002cz511q5o0bkg[[DISCUSS] Standardizing GLV connection options in TinkerPop 4].
-
-==== Renaming `evaluationTimeout` to `timeoutMillis`
-
-The per-request execution timeout is now referred to by a single name, `timeoutMillis`, everywhere. `timeoutMillis` is
-the maximum time in milliseconds that a request is allowed to execute on the server before it times out. It can be
-configured server-wide and overridden on a per-request basis. Previously the same concept was called
-`evaluationTimeout` in the server configuration, the `with()` script token, and several driver APIs. The `Millis`
-suffix aligns it with the driver connection options such as `connectTimeoutMillis` and `readTimeoutMillis`.
-
-This is a breaking change with no backward-compatible alias. The old `evaluationTimeout` name (and the long-deprecated
-`scriptEvaluationTimeout`) are no longer recognized anywhere. Update each surface as follows:
-
-- *Server config*: the `gremlin-server.yaml` key `evaluationTimeout` becomes `timeoutMillis` (default still 30000).
-- *Script token*: `g.with('evaluationTimeout', 500)` becomes `g.with('timeoutMillis', 500)`.
-- *Java driver*: `RequestOptions.Builder.timeout(long)` becomes `timeoutMillis(long)` and `getTimeout()` becomes `getTimeoutMillis()`.
-- *Go driver*: `RequestOptionsBuilder.SetEvaluationTimeout(int)` becomes `SetTimeoutMillis(int)`.
-- *.NET driver*: `Tokens.ArgsEvalTimeout` becomes `Tokens.ArgsTimeoutMillis` and `RequestMessage.Builder.AddEvaluationTimeout(...)`
- becomes `AddTimeoutMillis(...)`.
-- *JavaScript driver*: the request option `{ evaluationTimeout: N }` becomes `{ timeoutMillis: N }`.
-- *Python driver*: use the token `timeoutMillis` (e.g. `g.with_('timeoutMillis', 500)` or `request_options={'timeoutMillis': 500}`).
-
-Driver and server should be upgraded together. A driver sending the old `evaluationTimeout` field to a 4.x server has
-that field silently ignored and falls back to the server's default timeout, as with any unrecognized request argument.
-
-==== Java Runtime Upgrade
-
-TinkerPop 4.0 raises the minimum Java version from 11 to 17 for both building and running, and adds support for running
-on Java 21 and Java 25. Supporting Java 25 required upgrading Groovy to 4.0.32, Hadoop to 3.4.3, Spark to 4.1.x, and
-Netty to 4.2.x. These upgrades collectively prohibit building on Java 11, but enable support in Java 21 and 25.
-
-As with the earlier JDK 17 support, some libraries still rely on deep reflection (most notably the Kryo serialization
-library used with OLAP), so it may be necessary to `--add-opens` or `--add-exports` certain modules at runtime. The set
-of options used by TinkerPop's own tests is unchanged.
-
-==== .NET Runtime Upgrade
-
-The minimum target framework is now `net8.0` (previously `netstandard2.0;net6.0`).
-
==== Declarative Pattern Matching
Gremlin has always offered both imperative and declarative styles to writing graph queries. While the imperative style
@@ -235,46 +90,14 @@ TinkerGraph uses it out of the box with no configuration required. Graph provide
can add the `gql-gremlin` dependency and register `GqlDeclarativeMatchStrategy` as described in the
link:https://tinkerpop.apache.org/docs/x.y.z/dev/provider/#tinkerpop-providers-tinkergql[provider documentation].
-==== Request Interceptors
-
-When TinkerPop supported WebSockets prior to 4.0.0, the Java driver offered a `RequestInterceptor` interface (and
-its predecessor, `HandshakeInterceptor`) that allowed modification of the raw Netty `FullHttpRequest`. For WebSocket
-connections, the interceptor only ran on the initial HTTP upgrade handshake. With the move to HTTP, the notion of
-the "interceptor" has shifted to a per-request concern that is now standardized across all GLVs.
-
-All GLVs now support request interceptors, which allow modification of the HTTP request before it is sent to the
-server. An interceptor is a function that receives the mutable HTTP request object and can modify headers, the
-request body, the URI, or the HTTP method. Interceptors are run in the order they are registered.
-
-The most common use case for interceptors is authentication (e.g., SigV4 signing), but they can also be used to add
-provider-specific fields, inject custom headers, or transform the request body.
-
-Here is a simple Java example that adds a custom header:
-
-[source,java]
-----
-Cluster cluster = Cluster.build("localhost")
- .interceptors(request -> request.headers().put("X-Custom-Header", "value"))
- .create();
-----
-
-Authentication is also an interceptor. Each GLV provides convenience methods (e.g., `Auth.basic()`, `Auth.sigv4()`)
-that return interceptors and can be registered alongside custom ones.
-
-For full details on the interceptor API for each language variant, refer to the RequestInterceptor section in
-each GLV's documentation in the
-link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Gremlin Drivers and Variants]
-reference.
-
==== Multi-Label Support
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.
-
-The feature is controlled by `LabelCardinality`, 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:
+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:
```
gremlin.tinkergraph.vertexLabelCardinality=ZERO_OR_MORE
@@ -283,7 +106,7 @@ 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`.
-With multi-label enabled, several new traversal steps become available:
+The following examples demonstrate how Gremlin behaves with multi-label enabled:
```text
gremlin> g.addV('person','employee').property('name','marko')
@@ -334,49 +157,6 @@ gremlin> gml.V().has('name','marko').valueMap(true)
==>{id=0, label=[manager, person, employee], name=[marko]}
```
-Note the following behavioral details:
-
-* The deprecated `label()` step returns a single, non-deterministic label when multiple labels are present. Always
- use `labels()` to retrieve the full set reliably.
-* When a source is configured with `with("multilabel")`, the label output can be forced back to a single string
- per-traversal using `with("singlelabel")`. This is useful for providers that enable multi-label output by default
- but need an escape hatch for backward compatibility. Note that when both options are present on the same source,
- `"singlelabel"` always takes precedence regardless of the order in which they were applied.
-* Adding a label that already exists on the vertex is a no-op (when multi-label is enabled). Dropping a label that
- does not exist is also a no-op. With cardinality `ONE`, both `addLabel()` and `dropLabel()` throw since labels are
- immutable.
-* `hasLabel()` tests the predicate against each label on the element and returns `true` if at least one label
- satisfies it. For multi-label vertices, this means `hasLabel(P.neq("person"))` returns `true` when the vertex also
- carries other labels, a natural consequence of testing a negation predicate against a set of labels, where the
- non-matching labels will satisfy `neq`. To exclude vertices that carry a specific label, use the negation pattern:
- `g.V().not(__.hasLabel("person"))`. For single-label vertices the behavior is unchanged.
-
-==== Inconfigurable Request Serialization
-
-TinkerPop 3.x drivers used a single serializer configuration (for example, `serializer` in the Java driver or
-`message_serializer` in Python) that controlled both how a `RequestMessage` was serialized on the way to the server
-and how responses were deserialized on the way back. TinkerPop 4 splits these concerns. Requests are now always
-serialized as JSON (`application/json`) and that behavior is not configurable. The remaining serializer configuration
-is named `responseSerializer` (cased idiomatically per language) and controls only the `Accept` header and response
-deserialization.
-
-Applications that require a different request body encoding, such as GraphBinary for a server that expects it, can
-register a request interceptor that serializes the `RequestMessage` and replaces the body and `Content-Type` header.
-See the RequestInterceptor section for each GLV in the
-link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Gremlin Drivers and Variants]
-reference.
-
-See: link:https://issues.apache.org/jira/browse/TINKERPOP-3250[TINKERPOP-3250]
-
-==== Gremlator
-
-link:https://gremlator.com[Gremlator] has been rebuilt entirely in JavaScript as a browser-based single-page
-application and is now an official part of the Apache TinkerPop project. It translates Gremlin queries into
-equivalent representations in all supported language variants: Groovy, Java, Python, JavaScript, Go, .NET, and an
-anonymized form. The original gremlator.com was a prototype built by TinkerPop committer Dave Bechberger; the
-previous implementation required Java and a running Gremlin Server, whereas the new version runs entirely in the
-browser with no server infrastructure needed.
-
==== Transactions
TinkerPop 4.0 brings a set of related transaction changes across the drivers and embedded graphs: explicit remote
@@ -454,6 +234,220 @@ partial work is discarded if the user forgets to call `commit()`. In Java (both
can still be overridden via `tx.onClose(Transaction.CLOSE_BEHAVIOR.COMMIT)`. The non-Java GLVs do not support
configuring close behavior and always rollback.
+==== Subgraph Support in GLVs
+
+All GLVs now support the `subgraph()` step. Previously, calling `subgraph()` from a GLV produced an unknown-type error
+because the variant could not interpret the `Graph` payload that the server returned. Applications can now extract a
+portion of a remote graph as part of a normal traversal and inspect its vertices and edges directly from the client,
+without having to re-issue queries to reconstruct the result. See: <<subgraph-step>>.
+
+In the GLVs, the result is a detached snapshot of the captured vertices and edges, not a traversable `Graph` instance.
+It cannot be passed to `traversal().with(...)`, and mutating its collections has no effect on the source graph. To
+re-query elements against the original graph, extract their ids and call `g.V(id)` or `g.E(id)` against the original
+`GraphTraversalSource`.
+
+==== Gremlator
+
+link:https://gremlator.com[Gremlator] has been rebuilt entirely in JavaScript as a browser-based single-page
+application and is now an official part of the Apache TinkerPop project. It translates Gremlin queries into
+equivalent representations in all supported language variants: Groovy, Java, Python, JavaScript, Go, .NET, and an
+anonymized form. The original gremlator.com was a prototype built by TinkerPop committer Dave Bechberger; the
+previous implementation required Java and a running Gremlin Server, whereas the new version runs entirely in the
+browser with no server infrastructure needed.
+
+==== Bindings are now Parameters
+
+The map of named values substituted into a Gremlin query was previously called "bindings" in some places and
+"parameters" in others. TinkerPop now refers to these consistently as *query parameters*, reserving "bindings" for
+the distinct concept of `ScriptEngine` variable bindings. As a result, the driver methods for supplying query
+parameters have been renamed. Update any code that sets them:
+
+- Java: `RequestMessage.Builder.addBindings(...)` is now `addParameters(...)`.
+- Python: `client.submit(..., bindings=...)` is now `submit(..., parameters=...)`, and the `request_options`
+key `bindings` is now `parameters`.
+- Go: `SetBindings`/`SetBindingsString`/`AddBinding` are now
+`SetParameters`/`SetParametersString`/`AddParameter`.
+- .NET: `AddBinding`/`AddBindings`/`AddBindingsString` are now
+`AddParameter`/`AddParameters`/`AddParametersString`.
+- JavaScript: `addBinding`/`addBindings`/`addBindingsString` are now
+`addParameter`/`addParameters`/`addParametersString`, and the `RequestOptions.bindings` field is now `parameters`.
+
+For example, in the Java driver:
+
+[source,java]
+----
+final Map<String, Object> params = new HashMap<>();
+params.put("x", 1);
+
+// 3.x
+RequestMessage.build("g.V(x)").addBindings(params).create();
+
+// 4.x
+RequestMessage.build("g.V(x)").addParameters(params).create();
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3262[TINKERPOP-3262]
+
+==== Standardizing GLV Connection Options
+
+TinkerPop 4.x standardizes connection option names and defaults across all five Gremlin Language Variants (Java, Python,
+.NET, Go, and JavaScript). Each driver using its language-idiomatic casing (`camelCase`, `PascalCase`, or `snake_case`).
+These renames are breaking, the old option names have been removed.
+
+NOTE: Timeouts use a millisecond-suffixed canonical name (`connectTimeoutMillis`, `readTimeoutMillis`,
+`idleTimeoutMillis`, `keepAliveTimeMillis`, and the `_millis` form in Python). Java, Go, .NET, and Python also accept an
+idiomatic duration companion for the same setting (Java `Duration`, Go `time.Duration`, .NET `TimeSpan`, Python seconds);
+set only one form per option. JavaScript exposes only the millisecond form.
+
+===== Standardized options (cross-GLV)
+
+The table lists each standardized option by driver. Defaults are shown in parentheses; "n/a" means the driver does not
+expose that option.
+
+[width="100%",cols="2,2,2,2,2,2",options="header"]
+|=========================================================
+|Concept |Java (`Cluster.Builder`) |Python (kwarg) |.NET (`ConnectionSettings`) |Go (settings field) |JavaScript (option)
+|Max pooled connections |`maxConnections` (128) |`max_connections` (128) |`MaxConnections` (128) |`MaxConnections` (128) |`maxConnections` (128)
+|Connect timeout |`connectTimeoutMillis` (5000) |`connect_timeout_millis` (5000) |`ConnectTimeoutMillis` (5000) |`ConnectTimeoutMillis` (5000) |n/a
+|Idle-read timeout |`readTimeoutMillis` (0/off) |`read_timeout_millis` (off) |`ReadTimeoutMillis` (0/off) |`ReadTimeoutMillis` (0/off) |`readTimeoutMillis` (undici default)
+|Pool idle timeout |`idleTimeoutMillis` (180000) |`idle_timeout_millis` (180000) |`IdleTimeout` (180s) |`IdleTimeoutMillis` (180000) |n/a
+|TCP keep-alive idle |`keepAliveTimeMillis` (30000) |`keep_alive_time_millis` (30000) |`KeepAliveTime` (30s) |`KeepAliveTimeMillis` (30000) |`keepAliveTimeMillis` (30000)
+|Compression |`compression` (`DEFLATE`) |`compression` (`'deflate'`) |`Compression` (`Deflate`) |`Compression` (`CompressionDeflate`) |`compression` (`'deflate'`)
+|Connection-level batch size |`batchSize` (64) |`batch_size` (64) |`BatchSize` (64) |`BatchSize` (64) |`batchSize` (64)
+|Connection-level bulkResults |`bulkResults` (false) |`bulk_results` (false) |`BulkResults` (false) |`BulkResults` (false) |`bulkResults` (false)
+|Max response header bytes |`maxResponseHeaderBytes` (8192) |n/a |`MaxResponseHeaderBytes` (handler default) |`MaxResponseHeaderBytes` (net/http default) |`maxResponseHeaderBytes` (undici default)
+|TLS configuration |`ssl(SslContext)` + keystore builders |`ssl` (`SSLContext`) |`Ssl` (`SslClientAuthenticationOptions`) |`Ssl` (`*tls.Config`) |runtime (`NODE_EXTRA_CA_CERTS`, etc.)
+|HTTP proxy |`proxy(ProxyOptions)` |`proxy` |`Proxy` (`IWebProxy`) |`Proxy` (env default) |`proxy` (undici `ProxyAgent`)
+|Request interceptors |`interceptors` |`interceptors` |interceptor delegates |`Interceptors` |`interceptors`
+|=========================================================
+
+===== Behavior changes (all drivers)
+
+These change runtime behavior on upgrade even if you do not change your configuration:
+
+- *Compression defaults to on.* Every driver now defaults compression to `deflate` and sends `Accept-Encoding: deflate`.
+ Disable it with the compression option's "none" value (`Compression.NONE`, `'none'`, `Compression.None`,
+ `CompressionNone`).
+- *Connect timeout lowered to 5s* (from 15s, where applicable) and is now actually applied to transport establishment
+ (TCP connect plus TLS handshake), not the whole request.
+- *`readTimeout` is an idle-read timeout*, reset on each inbound response chunk, so it is streaming-safe and bounds only
+ the gap between chunks, not total response duration. It is off by default.
+- *`idleTimeout` reaps only pooled connections* that are idle between requests; it no longer bounds an in-flight
+ response (that is `readTimeout`'s job).
+
+===== Driver-specific notes
+
+- *Java* (`gremlin-driver`): Renamed: `maxConnectionPoolSize`->`maxConnections`, `connectionSetupTimeoutMillis`->`connectTimeoutMillis`,
+ `idleConnectionTimeoutMillis`->`idleTimeoutMillis`, `resultIterationBatchSize`->`batchSize`, `serializer`->`responseSerializer`,
+ and `RequestOptions` `addG`->`traversalSource`. New: `readTimeoutMillis`, `keepAliveTimeMillis`,
+ `maxResponseHeaderBytes`, `proxy(ProxyOptions)`, `url(String)`, `ssl(SslContext)`. Removed: `maxResponseContentLength`.
+ `validationRequest` default reconciled to `g.inject(0)`.
+- *Python* (`gremlin-python`): Renamed: `pool_size`->`max_connections` (default 8->128) and `ssl_options`->`ssl`.
+ New: `connect_timeout_millis`, `read_timeout_millis`, `idle_timeout_millis`, `keep_alive_time_millis`, `compression`,
+ `batch_size`, `proxy`, `trust_env`. `auth.sigv4` gained an optional credentials provider. Removed: `headers` (use interceptors)
+ and `max_content_length`.
+- *.NET* (`gremlin-dotnet`): Renamed: `MaxConnectionsPerServer`->`MaxConnections`, `ConnectionTimeout`->`ConnectTimeout`,
+ `IdleConnectionTimeout`->`IdleTimeout`, `KeepAliveInterval`->`KeepAliveTime`, `EnableCompression`->`Compression`, and
+ `Auth.BasicAuth`/`Auth.SigV4Auth`->`Auth.Basic`/`Auth.Sigv4`. New: `ReadTimeout`, `MaxResponseHeaderBytes`,
+ `Proxy`, `Ssl`, `BulkResults`.
+- *Go* (`gremlin-go`): Renamed: `MaximumConcurrentConnections`->`MaxConnections`, `IdleConnectionTimeout`->`IdleTimeout`,
+ `KeepAliveInterval`->`KeepAliveTime`, `ConnectionTimeout`->`ConnectTimeout`, `TlsConfig`->`Ssl`, `RequestInterceptors`->`Interceptors`,
+ `EnableCompression`->`Compression`. Auth helpers moved from package `gremlingo`
+ (`BasicAuth`/`SigV4Auth`/`SigV4AuthWithCredentials`) into a new `auth` sub-package (`auth.Basic`/`auth.SigV4`/`auth.SigV4WithCredentials`).
+ New: `ReadTimeout(Millis)`, `MaxResponseHeaderBytes`, `Proxy` (defaults to `http.ProxyFromEnvironment`), `BatchSize`, `BulkResults`.
+- *JavaScript* (`gremlin-javascript`): adopted `undici` as a pinned dependency providing the default dispatcher built from the
+ options above. Renamed: `reader`->`responseSerializer`. New: `readTimeoutMillis`, `keepAliveTimeMillis`, `maxResponseHeaderBytes`,
+ `proxy`, `compression`, `batchSize`, `bulkResults`, `logger`. Removed `headers` (use interceptors) and the
+ `ca`/`cert`/`pfx`/`rejectUnauthorized`/`agent` options (TLS is configured through the Node/undici runtime). undici is swapped
+ out in browser bundles, where these connection-pool options are managed by the browser.
+
+See: link:https://lists.apache.org/thread/yqtr2wnb1kq2pqqq4002cz511q5o0bkg[[DISCUSS] Standardizing GLV connection options in TinkerPop 4].
+
+==== Renaming `evaluationTimeout` to `timeoutMillis`
+
+The per-request execution timeout is now referred to by a single name, `timeoutMillis`, everywhere. `timeoutMillis` is
+the maximum time in milliseconds that a request is allowed to execute on the server before it times out. It can be
+configured server-wide and overridden on a per-request basis. Previously the same concept was called
+`evaluationTimeout` in the server configuration, the `with()` script token, and several driver APIs. The `Millis`
+suffix aligns it with the driver connection options such as `connectTimeoutMillis` and `readTimeoutMillis`.
+
+This is a breaking change with no backward-compatible alias. The old `evaluationTimeout` name (and the long-deprecated
+`scriptEvaluationTimeout`) are no longer recognized anywhere. Update each surface as follows:
+
+- *Server config*: the `gremlin-server.yaml` key `evaluationTimeout` becomes `timeoutMillis` (default still 30000).
+- *Script token*: `g.with('evaluationTimeout', 500)` becomes `g.with('timeoutMillis', 500)`.
+- *Java driver*: `RequestOptions.Builder.timeout(long)` becomes `timeoutMillis(long)` and `getTimeout()` becomes `getTimeoutMillis()`.
+- *Go driver*: `RequestOptionsBuilder.SetEvaluationTimeout(int)` becomes `SetTimeoutMillis(int)`.
+- *.NET driver*: `Tokens.ArgsEvalTimeout` becomes `Tokens.ArgsTimeoutMillis` and `RequestMessage.Builder.AddEvaluationTimeout(...)`
+ becomes `AddTimeoutMillis(...)`.
+- *JavaScript driver*: the request option `{ evaluationTimeout: N }` becomes `{ timeoutMillis: N }`.
+- *Python driver*: use the token `timeoutMillis` (e.g. `g.with_('timeoutMillis', 500)` or `request_options={'timeoutMillis': 500}`).
+
+Driver and server should be upgraded together. A driver sending the old `evaluationTimeout` field to a 4.x server has
+that field silently ignored and falls back to the server's default timeout, as with any unrecognized request argument.
+
+==== Java Runtime Upgrade
+
+TinkerPop 4.0 raises the minimum Java version from 11 to 17 for both building and running, and adds support for running
+on Java 21 and Java 25. Supporting Java 25 required upgrading Groovy to 4.0.32, Hadoop to 3.4.3, Spark to 4.1.x, and
+Netty to 4.2.x. These upgrades collectively prohibit building on Java 11, but enable support in Java 21 and 25.
+
+As with the earlier JDK 17 support, some libraries still rely on deep reflection (most notably the Kryo serialization
+library used with OLAP), so it may be necessary to `--add-opens` or `--add-exports` certain modules at runtime. The set
+of options used by TinkerPop's own tests is unchanged.
+
+==== .NET Runtime Upgrade
+
+The minimum target framework is now `net8.0` (previously `netstandard2.0;net6.0`).
+
+==== Request Interceptors
+
+When TinkerPop supported WebSockets prior to 4.0.0, the Java driver offered a `RequestInterceptor` interface (and
+its predecessor, `HandshakeInterceptor`) that allowed modification of the raw Netty `FullHttpRequest`. For WebSocket
+connections, the interceptor only ran on the initial HTTP upgrade handshake. With the move to HTTP, the notion of
+the "interceptor" has shifted to a per-request concern that is now standardized across all GLVs.
+
+All GLVs now support request interceptors, which allow modification of the HTTP request before it is sent to the
+server. An interceptor is a function that receives the mutable HTTP request object and can modify headers, the
+request body, the URI, or the HTTP method. Interceptors are run in the order they are registered.
+
+The most common use case for interceptors is authentication (e.g., SigV4 signing), but they can also be used to add
+provider-specific fields, inject custom headers, or transform the request body.
+
+Here is a simple Java example that adds a custom header:
+
+[source,java]
+----
+Cluster cluster = Cluster.build("localhost")
+ .interceptors(request -> request.headers().put("X-Custom-Header", "value"))
+ .create();
+----
+
+Authentication is also an interceptor. Each GLV provides convenience methods (e.g., `Auth.basic()`, `Auth.sigv4()`)
+that return interceptors and can be registered alongside custom ones.
+
+For full details on the interceptor API for each language variant, refer to the RequestInterceptor section in
+each GLV's documentation in the
+link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Gremlin Drivers and Variants]
+reference.
+
+==== Inconfigurable Request Serialization
+
+TinkerPop 3.x drivers used a single serializer configuration (for example, `serializer` in the Java driver or
+`message_serializer` in Python) that controlled both how a `RequestMessage` was serialized on the way to the server
+and how responses were deserialized on the way back. TinkerPop 4 splits these concerns. Requests are now always
+serialized as JSON (`application/json`) and that behavior is not configurable. The remaining serializer configuration
+is named `responseSerializer` (cased idiomatically per language) and controls only the `Accept` header and response
+deserialization.
+
+Applications that require a different request body encoding, such as GraphBinary for a server that expects it, can
+register a request interceptor that serializes the `RequestMessage` and replaces the body and `Content-Type` header.
+See the RequestInterceptor section for each GLV in the
+link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Gremlin Drivers and Variants]
+reference.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3250[TINKERPOP-3250]
+
==== Expanding Dynamic Arguments to Additional Steps
Prior to 4.0, comparing a traverser's value against a dynamically computed reference required several coordinated
@@ -948,18 +942,6 @@ unwrap(toInt(29)); // 29
unwrap('hello'); // 'hello'
----
-==== Subgraph Support in GLVs
-
-All GLVs now support the `subgraph()` step. Previously, calling `subgraph()` from a GLV produced an unknown-type error
-because the variant could not interpret the `Graph` payload that the server returned. Applications can now extract a
-portion of a remote graph as part of a normal traversal and inspect its vertices and edges directly from the client,
-without having to re-issue queries to reconstruct the result. See: <<subgraph-step>>.
-
-In the GLVs, the result is a detached snapshot of the captured vertices and edges, not a traversable `Graph` instance.
-It cannot be passed to `traversal().with(...)`, and mutating its collections has no effect on the source graph. To
-re-query elements against the original graph, extract their ids and call `g.V(id)` or `g.E(id)` against the original
-`GraphTraversalSource`.
-
==== Provider Defined Types
Graph providers may now expose custom types as Provider Defined Types (PDT) (replacing the old `CustomTypeSerializer`
(tinkerpop) 04/06: Document hasLabel() label matching semantics
Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit def6f26306ac3482fca8c8df3bd0a5edd8377182
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Thu Jul 16 13:33:55 2026 -0400
Document hasLabel() label matching semantics
hasLabel() tests each label an element carries, so a negation predicate
retains multi-label vertices that carry the label.
Assisted-by: Claude Code:claude-opus-4-8
---
docs/src/dev/provider/gremlin-semantics.asciidoc | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc
index 3674136d64..6fd002bf71 100644
--- a/docs/src/dev/provider/gremlin-semantics.asciidoc
+++ b/docs/src/dev/provider/gremlin-semantics.asciidoc
@@ -2009,6 +2009,13 @@ None
* 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.
(tinkerpop) 05/06: Remove Java implementation references from multi-label semantics
Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit f1abcc4bf8a1342ab688690e30d631e32b9344df
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Thu Jul 16 13:34:36 2026 -0400
Remove Java implementation references from multi-label semantics
Replaces Java exception classes with error categories, LabelCardinality
with prose, and Java collection types with GType names. Also widens the
errors table description column.
Assisted-by: Claude Code:claude-opus-4-8
---
docs/src/dev/provider/gremlin-semantics.asciidoc | 64 ++++++++++++------------
1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc
index 6fd002bf71..ddb0b95b9e 100644
--- a/docs/src/dev/provider/gremlin-semantics.asciidoc
+++ b/docs/src/dev/provider/gremlin-semantics.asciidoc
@@ -125,13 +125,13 @@ categories rather than to Java exception class names, since the categories are l
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%",options="header"]
+[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. |`IllegalStateException` |`"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"`
+|`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]]
@@ -586,7 +586,7 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addvertex-step[reference
*Description:* Adds one or more labels to an element.
-*Syntax:* `addLabel(String label, String... moreLabels)` | `addLabel(Traversal<?, ?> labelTraversal, Traversal<?, ?>... moreLabelTraversals)`
+*Syntax:* `addLabel(label: STRING, moreLabels: STRING...)` | `addLabel(labelTraversal: Traversal<any, STRING>, moreLabelTraversals: Traversal<any, STRING>...)`
[width="100%",options="header"]
|=========================================================
@@ -603,20 +603,18 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addvertex-step[reference
*Considerations:*
-Label add and remove operations require a `LabelCardinality` that permits changing the set of labels on an element
-(`ONE_OR_MORE` or `ZERO_OR_MORE`). When `LabelCardinality` is `ONE`, the vertex's label is fixed at creation and
-cannot be added to or removed.
+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 `Collection<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.
+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's `LabelCardinality` does not permit label modification, an `IllegalStateException` will be thrown.
-* If any label argument is null or empty, an `IllegalArgumentException` will be thrown.
-* If more than one traversal is provided and any of them produces a `Collection`, an `IllegalArgumentException` will
-be thrown.
+* 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]
@@ -625,7 +623,7 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addlabel-step[reference]
*Description:* Removes one or more specific labels from an element, or removes all labels.
-*Syntax:* `dropLabel(String label, String... moreLabels)` | `dropLabel(Traversal<?, String> labelTraversal, Traversal<?, String>... moreLabelTraversals)` | `dropLabels()`
+*Syntax:* `dropLabel(label: STRING, moreLabels: STRING...)` | `dropLabel(labelTraversal: Traversal<any, STRING>, moreLabelTraversals: Traversal<any, STRING>...)` | `dropLabels()`
[width="100%",options="header"]
|=========================================================
@@ -642,20 +640,20 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#addlabel-step[reference]
*Considerations:*
-Label add and remove operations require a `LabelCardinality` that permits changing the set of labels on an element
-(`ONE_OR_MORE` or `ZERO_OR_MORE`). Dropping a label that does not exist
-on the element is a no-op. `dropLabels()` removes all labels (only valid with `ZERO_OR_MORE`). The step returns the
+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 `Collection<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.
+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's `LabelCardinality` does not permit label modification, an `IllegalStateException` will be thrown.
-* If `dropLabel()` or `dropLabels()` would violate the minimum label count, an `IllegalStateException` will be thrown.
-* If more than one traversal is provided and any of them produces a `Collection`, an `IllegalArgumentException` will
-be thrown.
+* 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]
@@ -1685,9 +1683,10 @@ property entries specified by propertyKeys. If the list is empty, then all prope
*Considerations:*
The label format in the map is controlled by source-level `with("multilabel")` and `with("singlelabel")` options, not
-by the graph's `LabelCardinality`. 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 `VerificationException`. Note that is a
+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.
@@ -2098,7 +2097,7 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#label-step[reference]
*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 with zero labels (`ZERO_OR_MORE` mode), no traversers are emitted.
+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]
@@ -3222,9 +3221,10 @@ keys, the resulting map entry contains a `LIST` of the values. An `Edge` and a `
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 graph's `LabelCardinality`. 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 `VerificationException`. Note that is a
+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.
(tinkerpop) 06/06: Correct type and error guidance in the tinker-doc skill
Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 27f2006dc03bf4f511a31d3ef89bcefa25653358
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Thu Jul 16 13:38:28 2026 -0400
Correct type and error guidance in the tinker-doc skill
Drops the Type Error category which does not exist, and documents the
GType forms for collections and generics that the semantics doc uses.
Assisted-by: Claude Code:claude-opus-4-8
---
.skills/tinker-doc/references/books-and-voice.md | 39 ++++++++++++++++++------
1 file changed, 30 insertions(+), 9 deletions(-)
diff --git a/.skills/tinker-doc/references/books-and-voice.md b/.skills/tinker-doc/references/books-and-voice.md
index a0baa14b8f..3548c0994b 100644
--- a/.skills/tinker-doc/references/books-and-voice.md
+++ b/.skills/tinker-doc/references/books-and-voice.md
@@ -245,15 +245,36 @@ The semantics document is language-agnostic: it specifies Gremlin behavior for
every GLV. Do not name Java-specific types, exception classes, syntax, or library
functions in normative prose. The reference implementation is linked from each
step's `See:` block, and that is the appropriate place for any Java-flavored
-specifics. For exceptions, name the TinkerPop error category (`Argument Error`,
-`State Error`, `Type Error`, `Arithmetic Error`, `Unsupported Operation`) rather
-than a Java exception class. For types, use the `GType` enum names (`STRING`,
-`INT`, `LONG`, `BIGINT`, `BIGDECIMAL`, `FLOAT`, `DOUBLE`, `BOOLEAN`, `UUID`,
-`DATETIME`, `DURATION`, `BINARY`, `CHAR`, `NULL`, `NUMBER`, `LIST`, `SET`, `MAP`,
-plus graph types). Type definitions live in `== Types`; per-type Equality,
-Comparability, and Orderability behavior lives in the Comparability chapter. Do
-not duplicate definitions across the two. The `Java reference exception` column
-of the Errors table is the one intentional Java reference in the document.
+specifics. For exceptions, name the TinkerPop error category rather than a Java
+exception class. The categories are defined by the Errors table near the top of
+the document, which is the authority on which ones exist — do not use a category
+that is not a row in that table.
+
+For types, use the `GType` enum names (`STRING`, `INT`, `LONG`, `BIGINT`,
+`BIGDECIMAL`, `FLOAT`, `DOUBLE`, `BOOLEAN`, `UUID`, `DATETIME`, `DURATION`,
+`BINARY`, `CHAR`, `NULL`, `NUMBER`, `LIST`, `SET`, `MAP`, plus graph types), not
+the Java class names that back them. This applies to collections and generics as
+much as to scalars: write `SET<STRING>`, `LIST<STRING>`, `MAP<STRING, any>`, and
+`Traversal<any, STRING>` — never `Set<String>`, `Collection<String>`, or
+`Traversal<?, ?>`. Use `any` for an unconstrained type parameter.
+
+Note that `GType` has no `Collection` supertype, so there is nothing to translate
+`Collection<String>` directly into. `LIST` and `SET` are distinct types and
+neither subsumes the other, so a step accepting either takes "a `LIST` or `SET` of
+`STRING`". Check the step's source rather than assuming, since narrowing the
+contract to one of them is a real error.
+
+Java-specific naming also leaks in through configuration and enum names, not just
+types and exceptions. `LabelCardinality` and its constants (`ONE`, `ONE_OR_MORE`,
+`ZERO_OR_MORE`) are a Java-side concept that never appears in the grammar, so the
+capability is described in prose instead ("a graph that permits the set of labels
+on an element to change"). If a name is not in `Gremlin.g4` and not a `GType`, it
+probably does not belong in normative prose.
+
+Type definitions live in `== Types`; per-type Equality, Comparability, and
+Orderability behavior lives in the Comparability chapter. Do not duplicate
+definitions across the two. The `Java reference exception` column of the Errors
+table is the one intentional Java reference in the document.
The document has two parts. The conceptual sections near the top (equality,
comparability, orderability, equivalence, type promotion) specify the
(tinkerpop) 02/06: Document hasLabel() label matching and fix singlelabel docs
Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 5cc84a043d29fc7f6467c2e2d04067643e7ac3cd
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Thu Jul 16 12:30:55 2026 -0400
Document hasLabel() label matching and fix singlelabel docs
hasLabel() tests each label an element carries, so a negation predicate
retains multi-label vertices that carry the label. Also corrects the
claim that with("singlelabel") can override a multilabel source, which
raises a VerificationException instead.
Assisted-by: Claude Code:claude-opus-4-8
---
docs/src/reference/the-traversal.asciidoc | 45 +++++++++++++++++++++----------
1 file changed, 31 insertions(+), 14 deletions(-)
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 104db83ca6..065952a3f3 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -1985,23 +1985,25 @@ gml.V().has('name','tux').valueMap(true) <4>
<3> A persistent source avoids repeating `with("multilabel")` on each traversal.
<4> `valueMap(true)` also respects the `"multilabel"` configuration.
-When a source is configured with `with("multilabel")`, the `with("singlelabel")` option can be used per-traversal to
-force single-string label output:
+The companion `with("singlelabel")` option requests the opposite format, returning the label as a single string.
+Since the two options call for conflicting output, they are mutually exclusive and configuring both on the same
+traversal source raises a `VerificationException`:
[source, groovy]
----
-gml = g.with("multilabel")
-gml.V().has('name','marko').elementMap()
// results in: with("multilabel") and with("singlelabel") are mutually exclusive
// and cannot both be configured on the same traversal source.
-gml.with("singlelabel").V().has('name','marko').elementMap()
+g.with("multilabel").with("singlelabel").V().has('name','tux').elementMap()
----
-Since `"multilabel"` and `"singlelabel"` request opposite output formats, configuring both on the same traversal
-source is rejected. Attempting to combine them, for example by calling `g.with("multilabel").with("singlelabel")`
-will raise a `VerificationException`. The label format is controlled entirely by these source options and is independent of the
-graph's `LabelCardinality` setting, so a graph configured for `ZERO_OR_MORE` labels will still return a single
-string per element unless `with("multilabel")` is applied.
+Whether a traversal source that specifies neither option produces single-string or set-valued labels is a provider
+choice. The reference implementation defaults to a single string, which leaves `with("singlelabel")` redundant
+against TinkerGraph. The option exists for providers whose default is multi-label output and which need a way to
+request the single-string form.
+
+The label format is controlled entirely by these source options and is independent of the graph's `LabelCardinality`
+setting, so a graph configured for `ZERO_OR_MORE` labels will still return a single string per element unless
+`with("multilabel")` is applied.
*Additional References*
@@ -2363,6 +2365,21 @@ g.V().hasLabel('bird').hasLabel('aquatic').values('name') <2>
<2> Chaining `hasLabel()` calls requires both labels to be present, matching only `tux`, which has both "bird" and
"aquatic".
+More generally, `hasLabel()` applies its test to each label an element carries and retains the element when at least
+one label satisfies that test. For elements with a single label the distinction never surfaces, but it shapes the
+result of a negation predicate applied to a multi-label vertex:
+
+[gremlin-groovy,theZoo]
+----
+g.V().hasLabel(neq('bird')).values('name') <1>
+g.V().not(hasLabel('bird')).values('name') <2>
+----
+
+<1> `tux` carries the "bird" label and is still retained, because its remaining labels each satisfy `neq('bird')`.
+This follows from testing a negation against a set of labels rather than a single value.
+<2> Wrapping `hasLabel()` in <<not-step,`not()`>> excludes every vertex carrying the label, which is the pattern to
+reach for when the intent is to filter such vertices out.
+
NOTE: When a `Traversal` is provided directly as the value argument (not inside a `P`), it is internally wrapped in
`P.eq(traversal)`. Child traversals must be read-only. Mutating steps like `addV()` or `property()` are rejected with
an `IllegalArgumentException`. See <<a-note-on-predicates,A Note on Predicates>> for more details on traversal-bearing
@@ -5728,10 +5745,10 @@ g.with("multilabel").V().has('name','tux').valueMap(true) <2>
<1> Without `"multilabel"`, the label entry is a single string.
<2> With `"multilabel"`, all labels are returned as a set.
-When a source is configured with `with("multilabel")`, the `with("singlelabel")` option can be used per-traversal to
-force single-string label output. Since the two options request opposite output formats, configuring both on the
-same traversal source is rejected with a `VerificationException`. As with `elementMap()`, the label format is controlled
-entirely by these source options and is independent of the graph's `LabelCardinality` setting.
+The companion `with("singlelabel")` option requests labels as a single string instead. As with `elementMap()`, the
+two options are mutually exclusive and configuring both on the same traversal source raises a `VerificationException`.
+The label format is controlled entirely by these source options and is independent of the graph's `LabelCardinality`
+setting. See <<elementmap-step,`elementMap()`>> for a fuller description of both options.
*Additional References*
(tinkerpop) 01/06: Register theZoo graph in the docs build
Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 4271c0f0c4e46bb803424252d55599aa5e5492a0
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Thu Jul 16 12:30:42 2026 -0400
Register theZoo graph in the docs build
Reference doc examples using [gremlin-groovy,theZoo] were executing
against an empty TinkerGraph and rendering with no output, since the
graph was never added to GRAPH_INIT.
Assisted-by: Claude Code:claude-opus-4-8
---
.../java/org/apache/tinkerpop/gremlin/docs/GremlinTreeprocessor.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/tinkerpop-docs/src/main/java/org/apache/tinkerpop/gremlin/docs/GremlinTreeprocessor.java b/docs/tinkerpop-docs/src/main/java/org/apache/tinkerpop/gremlin/docs/GremlinTreeprocessor.java
index 8bc60dae87..46e42b734c 100644
--- a/docs/tinkerpop-docs/src/main/java/org/apache/tinkerpop/gremlin/docs/GremlinTreeprocessor.java
+++ b/docs/tinkerpop-docs/src/main/java/org/apache/tinkerpop/gremlin/docs/GremlinTreeprocessor.java
@@ -65,6 +65,7 @@ public class GremlinTreeprocessor extends Treeprocessor {
m.put("theCrew", "graph = TinkerFactory.createTheCrew()");
m.put("grateful", "graph = TinkerFactory.createGratefulDead()");
m.put("sink", "graph = TinkerFactory.createKitchenSink()");
+ m.put("theZoo", "graph = TinkerFactory.createTheZoo()");
GRAPH_INIT = Collections.unmodifiableMap(m);
}