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