This is an automated email from the ASF dual-hosted git repository. Cole-Greer pushed a commit to branch simplePDT in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 435f10d14a69e0dfc88db618b5c983d181e62966 Author: Cole Greer <[email protected]> AuthorDate: Wed Jun 24 21:17:19 2026 -0700 Document PrimitivePDT: provider guide, gremlin-lang literal, upgrade note, CHANGELOG Documentation for the PrimitivePDT (0xF1 / g:PrimitivePdt) feature. - docs/src/dev/provider/index.asciidoc: new "Primitive Provider Defined Types" section (anchor primitive-provider-defined-types) — when to use a primitive PDT (a single opaque stringified value for types with no native TinkerPop representation, e.g. unsigned integers), per-GLV adapter registration (Java PrimitivePDTAdapter, Python register_primitive, JS registerPrimitive, Go RegisterPrimitiveFuncs, .NET IPrimitivePdtAdapter), and the adapter-over-annotation/attribute precedence. - docs/src/reference/gremlin-variants.asciidoc: document both PDT literal forms — composite PDT("name",[map]) and primitive PDT("name","value"). - docs/src/upgrade/release-4.x.x.asciidoc: PrimitivePDT upgrade note. - CHANGELOG.asciidoc: entry in the current unreleased section. The GraphBinary (0xf1, {type}{value} two fully-qualified Strings) and GraphSON (g:PrimitivePdt, untyped string value) spec sections already matched the implementation and required no changes. tinkerpop-2gy.13 Assisted-by: Kiro:claude-opus-4.8 --- CHANGELOG.asciidoc | 1 + docs/src/dev/provider/index.asciidoc | 111 +++++++++++++++++++++++++++ docs/src/reference/gremlin-variants.asciidoc | 21 +++++ docs/src/upgrade/release-4.x.x.asciidoc | 14 ++++ 4 files changed, 147 insertions(+) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 4971f79217..20b93a608a 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -30,6 +30,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima * Added typed numeric wrappers and `preciseNumbers` connection option to `gremlin-javascript` for explicit control over numeric type serialization and deserialization. * Added `NextN(n)` to `Traversal` in `gremlin-go` for batched result iteration, providing API parity with `next(n)` in the Java, Python, and .NET GLVs, and updated the Go translators in `gremlin-core` and `gremlin-javascript` to emit `NextN(n)` for the batched form. * Added Provider Defined Types (PDT) support — graph providers can define custom types via `@ProviderDefined` annotation that serialize/deserialize seamlessly across all GLVs without driver-side configuration. Replaces TP3 custom type mechanism. +* Added Primitive Provider Defined Types (PrimitivePDT) — a single opaque stringified value variant of PDT for types with no native TinkerPop representation (e.g. unsigned integers). Supported across all GLVs via adapter registration. * Added Gremlator, a single page web application, that translates Gremlin into various programming languages like Javascript and Python. * Added explicit transaction support to all non-Java GLVs (gremlin-python, gremlin-go, gremlin-javascript, gremlin-dotnet). * Changed default transaction close behavior from commit to rollback across all GLVs to align with embedded graph defaults. diff --git a/docs/src/dev/provider/index.asciidoc b/docs/src/dev/provider/index.asciidoc index 45c4cd8a91..6ce6e0bc45 100644 --- a/docs/src/dev/provider/index.asciidoc +++ b/docs/src/dev/provider/index.asciidoc @@ -1577,6 +1577,117 @@ serialization and the registry handles inbound reconstruction. For driver users consuming PDTs, see the <<gremlin-variants,Gremlin Variants>> reference documentation for each language driver. +[[primitive-provider-defined-types]] +==== Primitive Provider Defined Types + +When a custom type is best represented as a single opaque stringified value rather than a map of fields — for example, +an unsigned 32-bit integer, a WKT geometry string, or a provider-specific identifier — use a *Primitive PDT* instead of +a Composite PDT. A Primitive PDT carries only a type name and a string value; TinkerPop never parses or interprets the +value. + +On the wire, a Primitive PDT is serialized as GraphBinary type `0xf1` (two fully-qualified Strings: type and value) or +GraphSON type `g:PrimitivePdt` (with an untyped JSON string value). In gremlin-lang, the literal form is +`PDT("name","value")` — an overload of the composite `PDT("name",[map])` literal where the second argument is a string +instead of a map. + +A type may be registered as composite *or* primitive, not both — attempting to register a type for both forms throws an +error. + +===== Adapter Precedence + +A registered adapter always takes precedence over annotation/attribute-based handling on dehydration. This is consistent +across all GLVs: if an adapter is registered for a type, it controls serialization regardless of whether the class is +also annotated. + +===== Java + +Implement `PrimitivePDTAdapter<T>`: + +[source,java] +---- +public class Uint32Adapter implements PrimitivePDTAdapter<Uint32> { + @Override public String typeName() { return "mygraph:Uint32"; } + @Override public Class<Uint32> targetClass() { return Uint32.class; } + @Override public String toValue(Uint32 obj) { return Long.toUnsignedString(obj.getValue()); } + @Override public Uint32 fromValue(String value) { return new Uint32(Integer.parseUnsignedInt(value)); } +} +---- + +Register via ServiceLoader in the same way as composite adapters — add the fully qualified class name to +`META-INF/services/org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDTAdapter`. + +===== Python + +Use `register_primitive` on the registry: + +[source,python] +---- +from gremlin_python.structure.graph import ProviderDefinedTypeRegistry + +registry = ProviderDefinedTypeRegistry() +registry.register_primitive('mygraph:Uint32', + from_value=lambda s: Uint32(int(s)), + to_value=lambda obj: str(obj.value), + target_class=Uint32) +---- + +===== JavaScript + +Use `registerPrimitive` with a `PrimitivePdtAdapter`: + +[source,javascript] +---- +const { ProviderDefinedTypeRegistry, PrimitivePdtAdapter } = require('gremlin'); + +const registry = new ProviderDefinedTypeRegistry(); +registry.registerPrimitive('mygraph:Uint32', new PrimitivePdtAdapter( + (str) => new Uint32(parseInt(str)), // fromValue + (obj) => obj.value.toString() // toValue +), Uint32); +---- + +===== Go + +Use `RegisterPrimitiveFuncs` or `RegisterPrimitiveFuncsWithType`: + +[source,go] +---- +registry := gremlingo.NewPDTRegistry() +registry.RegisterPrimitiveFuncsWithType("mygraph:Uint32", reflect.TypeOf(Uint32{}), + // hydrate: string -> Go type + func(value string) (interface{}, error) { + v, err := strconv.ParseUint(value, 10, 32) + return Uint32{Value: uint32(v)}, err + }, + // dehydrate: Go type -> string + func(obj interface{}) (string, error) { + return strconv.FormatUint(uint64(obj.(Uint32).Value), 10), nil + }, +) +---- + +===== .NET + +Implement `IPrimitivePdtAdapter<T>`: + +[source,csharp] +---- +public class Uint32Adapter : IPrimitivePdtAdapter<Uint32> +{ + public string TypeName => "mygraph:Uint32"; + public Uint32 FromString(string value) => new Uint32(uint.Parse(value)); + public string ToString(Uint32 obj) => obj.Value.ToString(); +} +---- + +Register on the `ProviderDefinedTypeRegistry` in the same way as composite adapters. + +===== Value Type + +Across all GLVs, unhydrated primitive PDT values are represented as `PrimitiveProviderDefinedType` objects containing a +`name` (or `Name`) and a string `value` (or `Value`). When no adapter is registered for a given type name, the driver +returns this generic value object. + [[gremlin-plugins]] == Gremlin Plugins diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc index 7a8405b292..4e01d73113 100644 --- a/docs/src/reference/gremlin-variants.asciidoc +++ b/docs/src/reference/gremlin-variants.asciidoc @@ -116,6 +116,27 @@ string is valid within the ANTLR grammar: For example, a string value `say "hello"` would be serialized as `"say \"hello\""` in the canonical Gremlin string. +==== PDT Literals + +The gremlin-lang grammar supports Provider Defined Type literals in two forms: + +* Composite: `PDT("typeName",[key:"value",...])` — creates a `ProviderDefinedType` with a map of fields. +* Primitive: `PDT("typeName","value")` — creates a `PrimitiveProviderDefinedType` with an opaque string value. + +Both forms share the `PDT(` prefix. The second argument determines the variant: a map literal produces a composite PDT, +a string literal produces a primitive PDT. + +Examples: + +[source,groovy] +---- +// Composite PDT — a Point with x and y fields +g.inject(PDT("mygraph:Point",["x":1.0,"y":2.0])) + +// Primitive PDT — an unsigned 32-bit integer as a stringified value +g.inject(PDT("mygraph:Uint32","4294967295")) +---- + The following sections describe each language variant and driver that is officially TinkerPop a part of the project, providing more detailed information about usage, configuration and known limitations. diff --git a/docs/src/upgrade/release-4.x.x.asciidoc b/docs/src/upgrade/release-4.x.x.asciidoc index ac8debb0a9..88fe8d51a9 100644 --- a/docs/src/upgrade/release-4.x.x.asciidoc +++ b/docs/src/upgrade/release-4.x.x.asciidoc @@ -545,6 +545,10 @@ effort to the old custom serializer approach but is entirely optional for basic * <<gremlin-dotnet-pdt,Gremlin.Net>> * <<gremlin-go-pdt,Gremlin-Go>> +PDTs come in two flavors: *Composite* (a type name plus a map of fields, for structured types) and *Primitive* (a type +name plus a single opaque string value, for types expressible as a single stringified value). The gremlin-lang grammar +supports both forms via the `PDT("name",[map])` and `PDT("name","value")` literals respectively. + === Upgrading for Providers @@ -565,6 +569,16 @@ benefiting all driver users transparently. See <<provider-defined-types>> for full details on annotation usage, field filtering, nested types, and ServiceLoader registration. +===== Primitive Provider Defined Types + +In addition to composite PDTs (which carry a map of fields), providers can now expose *Primitive PDTs* — types +represented as a single opaque stringified value (GraphBinary type `0xf1`, GraphSON type `g:PrimitivePdt`). This is +ideal for types with no native TinkerPop representation that can be expressed as a single string, such as unsigned +integers or WKT geometry strings. Each GLV provides an adapter interface for primitive PDTs +(`PrimitivePDTAdapter` in Java, `register_primitive` in Python, `registerPrimitive` in JavaScript, +`RegisterPrimitiveFuncs` in Go, `IPrimitivePdtAdapter<T>` in .NET). A type may be registered as composite or +primitive, not both. See <<primitive-provider-defined-types>> for details. + ==== Graph Driver Providers == TinkerPop 4.0.0-beta.2
