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

xiazcy 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 53208dfa5e CTR add per-request language to gremlin-go and correct 
driver docs
53208dfa5e is described below

commit 53208dfa5eadd9e8f6d194523ecf8ab57fd33f47
Author: Yang Xia <[email protected]>
AuthorDate: Thu Oct 22 16:28:48 2026 -0700

    CTR add per-request language to gremlin-go and correct driver docs
---
 CHANGELOG.asciidoc                                 |  1 +
 docs/src/dev/provider/index.asciidoc               | 11 +++++---
 docs/src/reference/gremlin-variants.asciidoc       | 16 ++++++------
 .../apache/tinkerpop/gremlin/driver/Settings.java  |  2 +-
 gremlin-go/driver/client.go                        |  1 +
 gremlin-go/driver/client_test.go                   | 30 ++++++++++++++++++++++
 gremlin-go/driver/request.go                       |  6 ++++-
 gremlin-go/driver/requestOptions.go                |  9 +++++++
 gremlin-go/driver/request_test.go                  |  6 +++++
 .../lib/process/graph-traversal.ts                 |  7 ++---
 10 files changed, 72 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 4808c2e2f1..b65fd44022 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -28,6 +28,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Added a `Char` type to `gremlin-go` to represent the Gremlin `Character` 
type in `GremlinLang`.
 * Restricted GraphSON and Gryo `Class` deserialization to classes registered 
with `ClassRegistry.register()`, `registerStrategy()` or `registerStrategies()`.
 * Fixed `gremlin-go` to report a malformed or truncated GraphBinary response 
as a deserialization error rather than a bare decoder message.
+* Added a per-request `language` option to the `gremlin-go` 
`RequestOptionsBuilder`, and honored `With("language", ...)` on traversals.
 * Made `TinkerGraph` an interface and renamed the in-memory implementation to 
`TinkerMemoryGraph`; `TinkerGraph.open()` and `gremlin.graph=...TinkerGraph` 
behave as before.
 * Renamed `TinkerTransactionGraph` to `TinkerStorageGraph`.
 * Fixed a `NullPointerException` when rolling back a transaction on a 
`TinkerStorageGraph` that has a vertex index but no edge index.
diff --git a/docs/src/dev/provider/index.asciidoc 
b/docs/src/dev/provider/index.asciidoc
index 01c533e522..1289c2a290 100644
--- a/docs/src/dev/provider/index.asciidoc
+++ b/docs/src/dev/provider/index.asciidoc
@@ -1196,8 +1196,9 @@ supports types like GraphBinary.
 ==== HTTP Request
 
 To formulate a request to Gremlin Server, a `RequestMessage` needs to be 
constructed.  The `RequestMessage` is a
-generalized representation of a request. This message can be serialized in any 
fashion that is supported by Gremlin
-Server, which by default is GraphBinary. An HTTP request that contains a 
`RequestMessage` has the following form:
+generalized representation of a request. Drivers send it as JSON 
(`application/json`), which Gremlin Server always
+accepts. It may instead be serialized with any serializer configured on 
Gremlin Server, in which case the
+`Content-Type` header names that serializer. An HTTP request that contains a 
`RequestMessage` has the following form:
 
 [source,text]
 ----
@@ -1212,8 +1213,10 @@ Gremlin-Hints: <hints>
   "parameters": string,
   "g": string,
   "language" : string,
+  "batchSize": number,
   "materializeProperties": string,
-  "bulkResults": boolean
+  "bulkResults": boolean,
+  "transactionId": string
 }
 ----
 
@@ -1224,7 +1227,7 @@ An actual, complete request might look like the following:
 POST /gremlin HTTP/1.1
 content-length: 61
 host: 127.0.0.1
-content-type: application/vnd.gremlin-v4.0+json
+content-type: application/json
 accept-encoding: deflate
 accept: application/vnd.graphbinary-v4.0
 user-agent: NotAvailable Gremlin-Java.4.0.0 11.0.25 Windows_11.10.0 amd64
diff --git a/docs/src/reference/gremlin-variants.asciidoc 
b/docs/src/reference/gremlin-variants.asciidoc
index 0db8d59d41..714f0e7b7d 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -242,8 +242,8 @@ Some connection options can also be set on individual 
requests made through the
 results, err := g.With("timeoutMillis", 500).V().Out("knows").ToList()
 ----
 
-The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults` and
-`timeoutMillis`.
+The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`, `language`,
+`materializeProperties` and `timeoutMillis`.
 
 NOTE: When submitting traversals through `DriverRemoteConnection`, 
`bulkResults` defaults to `true` per-request
 to optimize result transfer. For direct `Client.Submit()` calls, set the 
connection-level `BulkResults` option to
@@ -531,7 +531,7 @@ options := new(RequestOptionsBuilder).
 resultSet, err := client.SubmitWithOptions("g.V(x).count()", options)
 ----
 
-The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`,
+The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`, `language`,
 `timeoutMillis` and `materializeProperties`.
 `RequestOptions` may also contain a map of query `parameters` to be applied to 
the supplied
 traversal string.
@@ -1080,7 +1080,7 @@ GraphTraversalSource g = traversal().with(conf);
 List<Vertex> vertices = g.with(Tokens.TIMEOUT_MILLIS, 
500L).V().out("knows").toList()
 ----
 
-The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`,
+The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`, `language`,
 `materializeProperties` and `timeoutMillis`. Use of `Tokens` to reference 
these options is preferred.
 
 NOTE: When submitting traversals through `DriverRemoteConnection`, 
`bulkResults` defaults to `true` per-request
@@ -2360,7 +2360,7 @@ this feature is to set a per-request override to the 
`timeoutMillis` so that it
 const result = await client.submit("g.V().repeat(both()).times(100)", null, { 
timeoutMillis: 5000 })
 ----
 
-The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`,
+The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`, `language`,
 `materializeProperties` and `timeoutMillis`.
 
 IMPORTANT: The preferred method for setting a per-request timeout for scripts 
is demonstrated above, but a script may
@@ -2764,7 +2764,7 @@ For instance to set request timeout to 500 milliseconds:
 var l = g.With(Tokens.ArgsTimeoutMillis, 
500).V().Out("knows").Count().ToList();
 ----
 
-The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`,
+The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`, `language`,
 `materializeProperties`, and `timeoutMillis`. These options are available as 
constants on the `Gremlin.Net.Driver.Tokens`
 class.
 
@@ -3039,7 +3039,7 @@ feature is to set a per-request override to the 
`timeoutMillis` so that it only
 
include::../../../gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs[tags=submittingScriptsWithTimeout]
 ----
 
-The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`, `materializeProperties`
+The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`, `language`, `materializeProperties`
 and `timeoutMillis`. These options are available as constants on the 
`Gremlin.Net.Driver.Tokens` class.
 
 [[gremlin-dotnet-streaming]]
@@ -3837,7 +3837,7 @@ this feature is to set a per-request override to the 
`timeoutMillis` so that it
 result_set = client.submit('g.V().repeat(both()).times(100)', 
request_options={'timeoutMillis': 5000})
 ----
 
-The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`,
+The following options are allowed on a per-request basis in this fashion: 
`batchSize`, `bulkResults`, `language`,
 `materializeProperties` and `timeoutMillis`.
 
 IMPORTANT: The preferred method for setting a per-request timeout for scripts 
is demonstrated above, but a script may
diff --git 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
index 9209209b97..9b6f963ef9 100644
--- 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
+++ 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
@@ -317,7 +317,7 @@ public final class Settings {
 
         /**
          * The amount of time in milliseconds to wait for a new connection 
before timing out where the default value
-         * is 3000.
+         * is 16000.
          */
         public int maxWaitForConnection = Connection.MAX_WAIT_FOR_CONNECTION;
 
diff --git a/gremlin-go/driver/client.go b/gremlin-go/driver/client.go
index 61144aeb00..edf84a2e2e 100644
--- a/gremlin-go/driver/client.go
+++ b/gremlin-go/driver/client.go
@@ -328,6 +328,7 @@ func applyOptionsConfig(builder *RequestOptionsBuilder, 
config map[string]interf
                "batchSize":             "SetBatchSize",
                "materializeProperties": "SetMaterializeProperties",
                "bulkResults":           "SetBulkResults",
+               "language":              "SetLanguage",
        }
 
        for key, value := range config {
diff --git a/gremlin-go/driver/client_test.go b/gremlin-go/driver/client_test.go
index 73aff14a88..9a911a3774 100644
--- a/gremlin-go/driver/client_test.go
+++ b/gremlin-go/driver/client_test.go
@@ -21,6 +21,7 @@ package gremlingo
 
 import (
        "crypto/tls"
+       "errors"
        "testing"
 
        "github.com/stretchr/testify/assert"
@@ -388,3 +389,32 @@ func TestCompositePDTIntegration(t *testing.T) {
                assert.Equal(t, int32(4), p2.Fields["y"])
        })
 }
+
+// captureRequest returns a client whose interceptor records the outgoing 
RequestMessage and then aborts the
+// request, so request-shaping can be asserted without a server.
+func captureRequest(t *testing.T) (*Client, *RequestMessage) {
+       captured := &RequestMessage{}
+       client, err := NewClient("http://localhost:1/gremlin";, func(settings 
*ClientSettings) {
+               settings.Interceptors = []RequestInterceptor{func(req 
*HttpRequest) error {
+                       *captured = *req.Body.(*RequestMessage)
+                       return errors.New("captured")
+               }}
+       })
+       require.NoError(t, err)
+       t.Cleanup(client.Close)
+       return client, captured
+}
+
+func TestRequestOptionsLanguage(t *testing.T) {
+       t.Run("With(language) reaches the request", func(t *testing.T) {
+               builder := applyOptionsConfig(new(RequestOptionsBuilder),
+                       map[string]interface{}{"language": "gremlin-groovy"})
+               assert.Equal(t, "gremlin-groovy", MakeStringRequest("g.V()", 
"g", builder.Create()).Fields["language"])
+       })
+
+       t.Run("SubmitWithOptions sends the per-request language", func(t 
*testing.T) {
+               client, captured := captureRequest(t)
+               _, _ = client.SubmitWithOptions("g.V()", 
new(RequestOptionsBuilder).SetLanguage("gremlin-groovy").Create())
+               assert.Equal(t, "gremlin-groovy", captured.Fields["language"])
+       })
+}
diff --git a/gremlin-go/driver/request.go b/gremlin-go/driver/request.go
index 73dbc6d507..12f0ba05fa 100644
--- a/gremlin-go/driver/request.go
+++ b/gremlin-go/driver/request.go
@@ -47,8 +47,12 @@ type RequestMessage struct {
 //     bytes, _ := httpReq.SerializeBody()
 //     // Send bytes over gRPC, HTTP/2, etc.
 func MakeStringRequest(stringGremlin string, traversalSource string, 
requestOptions RequestOptions) (req RequestMessage) {
+       language := "gremlin-lang"
+       if requestOptions.language != "" {
+               language = requestOptions.language
+       }
        newFields := map[string]interface{}{
-               "language": "gremlin-lang",
+               "language": language,
                "g":        traversalSource,
        }
 
diff --git a/gremlin-go/driver/requestOptions.go 
b/gremlin-go/driver/requestOptions.go
index 47a43740fc..e177bfe568 100644
--- a/gremlin-go/driver/requestOptions.go
+++ b/gremlin-go/driver/requestOptions.go
@@ -26,6 +26,7 @@ type RequestOptions struct {
        materializeProperties string
        bulkResults           *bool
        transactionId         string
+       language              string
 }
 
 type RequestOptionsBuilder struct {
@@ -36,6 +37,7 @@ type RequestOptionsBuilder struct {
        materializeProperties string
        bulkResults           *bool
        transactionId         string
+       language              string
 }
 
 func (builder *RequestOptionsBuilder) SetTimeoutMillis(timeoutMillis int) 
*RequestOptionsBuilder {
@@ -79,6 +81,12 @@ func (builder *RequestOptionsBuilder) 
SetTransactionId(transactionId string) *Re
        return builder
 }
 
+// SetLanguage sets the language (ScriptEngine) used to process the request. 
Defaults to "gremlin-lang" when unset.
+func (builder *RequestOptionsBuilder) SetLanguage(language string) 
*RequestOptionsBuilder {
+       builder.language = language
+       return builder
+}
+
 func (builder *RequestOptionsBuilder) AddParameter(key string, parameter 
interface{}) *RequestOptionsBuilder {
        if builder.parametersString != "" {
                panic("cannot mix AddParameter() with SetParametersString()")
@@ -98,6 +106,7 @@ func (builder *RequestOptionsBuilder) Create() 
RequestOptions {
        requestOptions.materializeProperties = builder.materializeProperties
        requestOptions.bulkResults = builder.bulkResults
        requestOptions.transactionId = builder.transactionId
+       requestOptions.language = builder.language
 
        // convert map parameters to string at creation time, matching Java's 
RequestOptions.Builder.create()
        if builder.parametersString != "" {
diff --git a/gremlin-go/driver/request_test.go 
b/gremlin-go/driver/request_test.go
index 46645943b0..95c97ec3f1 100644
--- a/gremlin-go/driver/request_test.go
+++ b/gremlin-go/driver/request_test.go
@@ -46,6 +46,12 @@ func TestRequest(t *testing.T) {
                assert.Equal(t, 123, r.Fields["batchSize"])
        })
 
+       t.Run("Test makeStringRequest() with custom language", func(t 
*testing.T) {
+               r := MakeStringRequest("g.V()", "g",
+                       
new(RequestOptionsBuilder).SetLanguage("gremlin-groovy").Create())
+               assert.Equal(t, "gremlin-groovy", r.Fields["language"])
+       })
+
        t.Run("Test makeStringRequest() with bulkResults", func(t *testing.T) {
                r := MakeStringRequest("g.V()", "g",
                        
new(RequestOptionsBuilder).SetBulkResults(true).Create())
diff --git a/gremlin-js/gremlin-javascript/lib/process/graph-traversal.ts 
b/gremlin-js/gremlin-javascript/lib/process/graph-traversal.ts
index bfa9c277d7..6ad4e7ec8b 100644
--- a/gremlin-js/gremlin-javascript/lib/process/graph-traversal.ts
+++ b/gremlin-js/gremlin-javascript/lib/process/graph-traversal.ts
@@ -93,9 +93,10 @@ export class GraphTraversalSource {
    * On normal completion of the callback the transaction is committed; on any
    * abnormal exit (a thrown error or a rejected promise) the transaction is
    * rolled back. This is a single-shot operation: there is exactly one attempt
-   * (begin -> run -> commit/rollback) and no retry. A second transaction may 
not
-   * be opened from inside the callback (calling <code>gtx.tx().begin()</code>
-   * throws via the existing double-begin guard).
+   * (begin -> run -> commit/rollback) and no retry. A second transaction 
cannot
+   * be opened from inside the callback: <code>gtx.tx()</code> returns the same
+   * transaction, and calling <code>begin()</code> on it again is idempotent 
and
+   * returns a source bound to that same transaction.
    *
    * The callback may be synchronous or asynchronous; its return value (or the
    * value its promise resolves to) is awaited and returned from

Reply via email to