imbajin commented on code in PR #541:
URL: https://github.com/apache/hugegraph-doc/pull/541#discussion_r4323397738
##########
content/en/docs/guides/gremlin-migration-3.8.md:
##########
@@ -0,0 +1,218 @@
+---
+title: "HugeGraph 1.8.0 Gremlin Migration & Compatibility Guide"
+linkTitle: "1.8 Gremlin & TinkerPop Migration"
+description: "Comprehensive guide to breaking changes, high-frequency syntax
rewrites, configuration migrations, and complete compatibility references from
TinkerPop 3.5 to 3.8 in HugeGraph 1.8.0."
+weight: 9
+---
+
+HugeGraph 1.8.0 upgrades its underlying graph traversal and query framework
from Apache TinkerPop 3.5.1 to **TinkerPop 3.8.1**, requires **Java 17** for
Server and builds, and updates the script engine to **Groovy 4.0.25**.
+
+While bringing substantial performance optimizations and specification
alignments, this upgrade introduces several language-level breaking changes.
This guide highlights the **most critical and frequently used syntax changes**
with recommended rewrites to ensure seamless migration. Edge cases and
lower-level protocol details are organized in the [Full Compatibility
Reference](#full-compatibility-reference) at the end.
+
+> [!NOTE]
+> - This guide applies to upgrades from version 1.7.0 (or earlier) to 1.8.0.
+> - The **Groovy script engine** remains supported in 1.8.0, allowing existing
multi-line scripts, variable definitions, and Java closures to continue
functioning as expected.
+
+---
+
+## Top 5 High-Frequency Syntax and Behavioral Changes
+
+Among existing query scripts, the following 5 changes are the most common.
**Failure to update these may cause query errors or silently return empty
results.**
+
+### 1. `has(key, traversal)` Overload Removed (High Risk: Silent Empty Results)
+
+* **Impact**:
+ In TinkerPop 3.8.0, the `has(String, Traversal)` and `has(T, Traversal)`
overloads were removed.
+ In the Groovy script engine, legacy code will not throw a compilation error;
instead, it **silently falls back to `has(String, Object)`**, comparing the
anonymous traversal object itself against vertex/edge properties. In practice,
this **silently yields 0 results / empty sets**!
+* **Legacy Query**:
+ ```groovy
+ // Broken in 3.8: will not throw an error, but silently returns 0 results
+ g.V().has('age', __.is(gt(30)))
+ g.V().has(T.label, __.is('person'))
+ ```
+* **Recommended Rewrite**:
+ ```groovy
+ // For simple scalar/predicate checks, pass predicates directly
+ g.V().has('age', gt(30))
+ g.V().hasLabel('person')
+ g.V().hasId('v-1')
+
+ // If passing property values into child traversals for complex filtering,
use filter()
+ g.V().filter(__.values('age').is(gt(30)))
+ ```
+
+---
+
+### 2. `store()` Removed & `aggregate()` Scope Overload Removed
+
+* **Impact**:
+ - The `store('x')` step, previously used for lazy side-effect collection,
has been completely removed (`No signature of method: store()`).
+ - The `aggregate(Scope, 'x')` overload with an explicit `Scope` (both
`local` and `global`) has also been removed.
+* **Legacy Query**:
+ ```groovy
+ // Broken: store step removed
+ g.V().store('x').by('age').cap('x')
+
+ // Broken: aggregate(Scope, ...) overload removed
+ g.V().aggregate(local, 'x')
+ g.V().aggregate(global, 'x')
+ ```
+* **Recommended Rewrite**:
+ ```groovy
+ // 1. Eager global collection (formerly global aggregate): use
aggregate('x') directly
+ g.V().aggregate('x').by('age').cap('x')
+
+ // 2. Per-object / lazy collection (formerly store or local aggregate): wrap
in local()
+ g.V().local(__.aggregate('x').by('age')).cap('x')
+ ```
+
+---
+
+### 3. Step `none()` Renamed to `discard()`
+
+* **Impact**:
+ In TinkerPop 3.5.1, `none()` was a traversal step used to discard all
incoming traversers (e.g., `g.V().hasLabel('person').none()`, which returned
`[]`).
+ In 3.8.1, this step has been renamed to **`discard()`**.
+ Meanwhile, 3.8 introduces a new collection predicate step `none(P)`
(checking if no elements in a collection match predicate P). Invoking
parameterless
+ `none()` in 3.8.1 fails immediately with:
+ `IllegalArgumentException: Input predicate to none step can't be null.`
+* **Recommended Rewrite**:
+ ```groovy
+ // Legacy (runs on 3.5.1, returning []):
+ g.V().hasLabel('person').none()
+
+ // Rewritten for 3.8: use discard() to discard traversers
+ g.V().hasLabel('person').discard()
+
+ // Discarding unhandled inputs in choose() branches:
+ g.V().choose(__.values('age')).option(gt(30),
__.values('name')).option(Pick.none, __.discard())
+ ```
+
+---
+
+### 4. Strict Behavior for Missing Properties in `by()`
+
+* **Impact**:
+ TinkerPop 3.6+ unified missing value handling across `by()` modulators
(removing previous default-null behaviors). In 3.8.1, when a property
referenced by `by()` does not exist:
+ 1. **Ordering `order().by('age')`**: Vertices missing `age` are
**immediately filtered out** (previously they might sort first/last).
+ 2. **Projection `project('name', 'age').by('name').by('age')`**: If `age` is
missing, the returned Map **will not contain the `age` key at all** (previously
returned `age: null`).
+ 3. **Path `path().by('age')`**: If any vertex along the path misses `age`,
the **entire path is dropped**.
+* **Recommended Rewrite**:
+ If your application requires preserving elements with explicit defaults
(e.g., `null`), use `coalesce()` inside `by()`:
+ ```groovy
+ // Retain key 'age' with null when missing
+ g.V().project('name', 'age')
+ .by('name')
+ .by(__.coalesce(__.values('age'), __.constant(null)))
+
+ // Provide a typed default for sorting missing fields
+ g.V().order().by(__.coalesce(__.values('age'), __.constant(-1)), desc)
+ ```
+
+---
+
+### 5. `choose()` Semantics Alignment
+
+* **Impact**:
+ - **Missing Property Handling**: When a choice child traversal evaluated an
absent property on an element, TinkerPop 3.5.1 threw:
+ `IllegalArgumentException: The provided traverser does not map to a value:
v[3]->[PropertiesStep([age],value)]`.
+ In 3.8.1, elements missing the target property pass through unchanged
without error.
+ - **Single Branch Execution**: When multiple `option()` conditions match
simultaneously, 3.8.1 **executes only the first matching branch** (legacy
versions might execute all matching branches).
+ - **Default Pass-Through**: When no `option()` condition matches, 3.8.1
**passes the input element through unchanged** by default (whereas 3.5.1
filtered it out).
+* **Recommended Rewrite**:
+ - To execute multiple matching branches, rewrite using `union()` combined
with `filter()`.
+ - To filter out elements when no option matches or property is missing,
explicitly supply `Pick.none` and `Pick.unproductive` branches:
+ ```groovy
+ // Discard unmatched and missing-property inputs
+ g.V().choose(__.values('age'))
+ .option(gt(30), __.values('name'))
+ .option(Pick.none, __.discard())
+ .option(Pick.unproductive, __.discard())
+ ```
+
+---
+
+## New Steps & Functions in 1.8 (TinkerPop 3.8)
+
+TinkerPop 3.8 introduces native date manipulation functions and steps that
were not present in 3.5.1 (HugeGraph 1.7.0):
+
+### Date Steps (`datetime()`, `dateAdd()`, `dateDiff()`)
+
+- **`datetime()`**: Creates a date-time object or retrieves server time.
+ In 3.8, it returns **`java.time.OffsetDateTime`** (e.g.,
`datetime('2026-10-19T00:00:00Z')`).
+- **`dateAdd()`**: Performs date arithmetic, adding a duration to a date.
+- **`dateDiff()`**: Computes the difference between two date-time objects,
returning the difference in **milliseconds**
+ (e.g., a 1-day difference yields `86400000` ms).
+- **Example**:
+ ```groovy
+ // Current time as OffsetDateTime
+ g.inject(datetime())
+
+ // Compute difference in milliseconds, convert to seconds
+
g.inject(datetime('2026-10-19T00:00:00Z')).dateDiff(datetime('2026-10-18T00:00:00Z'))
+ .math('_ / 1000').asNumber(GType.LONG)
+ ```
+
+---
+
+## Server Configuration and Client Compatibility
+
+### 1. Serializer Configuration in `conf/gremlin-server.yaml`
+
+In TinkerPop 3.8, serializer classes moved from `gremlin-driver`
(`org.apache.tinkerpop.gremlin.driver.ser.*`)
+to `gremlin-util` (`org.apache.tinkerpop.gremlin.util.ser.*`).
+
+> [!IMPORTANT]
+> **Do not simply copy an old `gremlin-server.yaml` from 1.7.0 and edit class
names.**
+> Always start from the `conf/gremlin-server.yaml` bundled with HugeGraph
1.8.0 and carry over your customizations (ports, host, authenticator).
+> HugeGraph requires custom serializer builders and registries: GraphBinary
requires `builder:
org.apache.hugegraph.io.HugeGraphTypeSerializerRegistryBuilder`
+> to serialize HugeGraph `Id`, schema, `Blob`, and enum values (otherwise
failing with `IOException: Serializer for type ... not found`);
+> untyped GraphSON V1 requires
`org.apache.hugegraph.io.HugeGraphSONV1MessageIoRegistry`.
+
+| Legacy Serializer (`driver.ser.*`) | 1.8.0 Serializer (`util.ser.*`) |
Required Registries / Builder | Notes |
+|---|---|---|---|
+| `GraphBinaryMessageSerializerV1` |
`org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1` |
`builder:
org.apache.hugegraph.io.HugeGraphTypeSerializerRegistryBuilder`<br>`ioRegistries:
[org.apache.hugegraph.io.HugeGraphIoRegistry]` | Recommended default binary
serializer |
+| `GraphSONMessageSerializerV1d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV1` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry,
org.apache.hugegraph.io.HugeGraphSONV1MessageIoRegistry]` | Untyped GraphSON V1
|
+| `GraphSONMessageSerializerV2d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV2` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | Untyped
GraphSON V2 |
+| `GraphSONMessageSerializerV3d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV3` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | Untyped
GraphSON V3 |
+{.td-table--wide}
+
+### 2. Can HugeGraph Client 1.5.0 Continue to Work?
+* **Verdict**: **Yes**.
+* HugeGraph Server 1.8.0 provides backward-compatible adapters (graph alias
mapping and legacy auth path redirection). Applications using
`hugegraph-client:1.5.0` can continue standard CRUD, schema management, and
REST operations without forced SDK upgrades.
Review Comment:
⚠️ Important: This compatibility claim says Server 1.8 redirects the legacy
auth path, but Client 1.5 uses `graphs/{graph}/auth/...` while the server
`PathFilter` only prefixes the default graphspace and `UserAPI` is mounted at
`graphspaces/{graphspace}/auth/users`; the rewritten request still has no
matching route. Please narrow the claim to verified operations or
document/provide the legacy auth adapter, and mirror the correction in Chinese.
##########
content/en/docs/guides/gremlin-migration-3.8.md:
##########
@@ -0,0 +1,218 @@
+---
+title: "HugeGraph 1.8.0 Gremlin Migration & Compatibility Guide"
+linkTitle: "1.8 Gremlin & TinkerPop Migration"
+description: "Comprehensive guide to breaking changes, high-frequency syntax
rewrites, configuration migrations, and complete compatibility references from
TinkerPop 3.5 to 3.8 in HugeGraph 1.8.0."
+weight: 9
+---
+
+HugeGraph 1.8.0 upgrades its underlying graph traversal and query framework
from Apache TinkerPop 3.5.1 to **TinkerPop 3.8.1**, requires **Java 17** for
Server and builds, and updates the script engine to **Groovy 4.0.25**.
+
+While bringing substantial performance optimizations and specification
alignments, this upgrade introduces several language-level breaking changes.
This guide highlights the **most critical and frequently used syntax changes**
with recommended rewrites to ensure seamless migration. Edge cases and
lower-level protocol details are organized in the [Full Compatibility
Reference](#full-compatibility-reference) at the end.
+
+> [!NOTE]
+> - This guide applies to upgrades from version 1.7.0 (or earlier) to 1.8.0.
+> - The **Groovy script engine** remains supported in 1.8.0, allowing existing
multi-line scripts, variable definitions, and Java closures to continue
functioning as expected.
+
+---
+
+## Top 5 High-Frequency Syntax and Behavioral Changes
+
+Among existing query scripts, the following 5 changes are the most common.
**Failure to update these may cause query errors or silently return empty
results.**
+
+### 1. `has(key, traversal)` Overload Removed (High Risk: Silent Empty Results)
+
+* **Impact**:
+ In TinkerPop 3.8.0, the `has(String, Traversal)` and `has(T, Traversal)`
overloads were removed.
+ In the Groovy script engine, legacy code will not throw a compilation error;
instead, it **silently falls back to `has(String, Object)`**, comparing the
anonymous traversal object itself against vertex/edge properties. In practice,
this **silently yields 0 results / empty sets**!
+* **Legacy Query**:
+ ```groovy
+ // Broken in 3.8: will not throw an error, but silently returns 0 results
+ g.V().has('age', __.is(gt(30)))
+ g.V().has(T.label, __.is('person'))
+ ```
+* **Recommended Rewrite**:
+ ```groovy
+ // For simple scalar/predicate checks, pass predicates directly
+ g.V().has('age', gt(30))
+ g.V().hasLabel('person')
+ g.V().hasId('v-1')
+
+ // If passing property values into child traversals for complex filtering,
use filter()
+ g.V().filter(__.values('age').is(gt(30)))
+ ```
+
+---
+
+### 2. `store()` Removed & `aggregate()` Scope Overload Removed
+
+* **Impact**:
+ - The `store('x')` step, previously used for lazy side-effect collection,
has been completely removed (`No signature of method: store()`).
+ - The `aggregate(Scope, 'x')` overload with an explicit `Scope` (both
`local` and `global`) has also been removed.
+* **Legacy Query**:
+ ```groovy
+ // Broken: store step removed
+ g.V().store('x').by('age').cap('x')
+
+ // Broken: aggregate(Scope, ...) overload removed
+ g.V().aggregate(local, 'x')
+ g.V().aggregate(global, 'x')
+ ```
+* **Recommended Rewrite**:
+ ```groovy
+ // 1. Eager global collection (formerly global aggregate): use
aggregate('x') directly
+ g.V().aggregate('x').by('age').cap('x')
+
+ // 2. Per-object / lazy collection (formerly store or local aggregate): wrap
in local()
+ g.V().local(__.aggregate('x').by('age')).cap('x')
+ ```
+
+---
+
+### 3. Step `none()` Renamed to `discard()`
+
+* **Impact**:
+ In TinkerPop 3.5.1, `none()` was a traversal step used to discard all
incoming traversers (e.g., `g.V().hasLabel('person').none()`, which returned
`[]`).
+ In 3.8.1, this step has been renamed to **`discard()`**.
+ Meanwhile, 3.8 introduces a new collection predicate step `none(P)`
(checking if no elements in a collection match predicate P). Invoking
parameterless
+ `none()` in 3.8.1 fails immediately with:
+ `IllegalArgumentException: Input predicate to none step can't be null.`
+* **Recommended Rewrite**:
+ ```groovy
+ // Legacy (runs on 3.5.1, returning []):
+ g.V().hasLabel('person').none()
+
+ // Rewritten for 3.8: use discard() to discard traversers
+ g.V().hasLabel('person').discard()
+
+ // Discarding unhandled inputs in choose() branches:
+ g.V().choose(__.values('age')).option(gt(30),
__.values('name')).option(Pick.none, __.discard())
+ ```
+
+---
+
+### 4. Strict Behavior for Missing Properties in `by()`
+
+* **Impact**:
+ TinkerPop 3.6+ unified missing value handling across `by()` modulators
(removing previous default-null behaviors). In 3.8.1, when a property
referenced by `by()` does not exist:
+ 1. **Ordering `order().by('age')`**: Vertices missing `age` are
**immediately filtered out** (previously they might sort first/last).
+ 2. **Projection `project('name', 'age').by('name').by('age')`**: If `age` is
missing, the returned Map **will not contain the `age` key at all** (previously
returned `age: null`).
+ 3. **Path `path().by('age')`**: If any vertex along the path misses `age`,
the **entire path is dropped**.
+* **Recommended Rewrite**:
+ If your application requires preserving elements with explicit defaults
(e.g., `null`), use `coalesce()` inside `by()`:
+ ```groovy
+ // Retain key 'age' with null when missing
+ g.V().project('name', 'age')
+ .by('name')
+ .by(__.coalesce(__.values('age'), __.constant(null)))
+
+ // Provide a typed default for sorting missing fields
+ g.V().order().by(__.coalesce(__.values('age'), __.constant(-1)), desc)
+ ```
+
+---
+
+### 5. `choose()` Semantics Alignment
+
+* **Impact**:
+ - **Missing Property Handling**: When a choice child traversal evaluated an
absent property on an element, TinkerPop 3.5.1 threw:
+ `IllegalArgumentException: The provided traverser does not map to a value:
v[3]->[PropertiesStep([age],value)]`.
+ In 3.8.1, elements missing the target property pass through unchanged
without error.
+ - **Single Branch Execution**: When multiple `option()` conditions match
simultaneously, 3.8.1 **executes only the first matching branch** (legacy
versions might execute all matching branches).
+ - **Default Pass-Through**: When no `option()` condition matches, 3.8.1
**passes the input element through unchanged** by default (whereas 3.5.1
filtered it out).
+* **Recommended Rewrite**:
+ - To execute multiple matching branches, rewrite using `union()` combined
with `filter()`.
+ - To filter out elements when no option matches or property is missing,
explicitly supply `Pick.none` and `Pick.unproductive` branches:
+ ```groovy
+ // Discard unmatched and missing-property inputs
+ g.V().choose(__.values('age'))
+ .option(gt(30), __.values('name'))
+ .option(Pick.none, __.discard())
+ .option(Pick.unproductive, __.discard())
+ ```
+
+---
+
+## New Steps & Functions in 1.8 (TinkerPop 3.8)
+
+TinkerPop 3.8 introduces native date manipulation functions and steps that
were not present in 3.5.1 (HugeGraph 1.7.0):
+
+### Date Steps (`datetime()`, `dateAdd()`, `dateDiff()`)
+
+- **`datetime()`**: Creates a date-time object or retrieves server time.
+ In 3.8, it returns **`java.time.OffsetDateTime`** (e.g.,
`datetime('2026-10-19T00:00:00Z')`).
+- **`dateAdd()`**: Performs date arithmetic, adding a duration to a date.
+- **`dateDiff()`**: Computes the difference between two date-time objects,
returning the difference in **milliseconds**
+ (e.g., a 1-day difference yields `86400000` ms).
+- **Example**:
+ ```groovy
+ // Current time as OffsetDateTime
+ g.inject(datetime())
+
+ // Compute difference in milliseconds, convert to seconds
+
g.inject(datetime('2026-10-19T00:00:00Z')).dateDiff(datetime('2026-10-18T00:00:00Z'))
+ .math('_ / 1000').asNumber(GType.LONG)
+ ```
+
+---
+
+## Server Configuration and Client Compatibility
+
+### 1. Serializer Configuration in `conf/gremlin-server.yaml`
+
+In TinkerPop 3.8, serializer classes moved from `gremlin-driver`
(`org.apache.tinkerpop.gremlin.driver.ser.*`)
+to `gremlin-util` (`org.apache.tinkerpop.gremlin.util.ser.*`).
+
+> [!IMPORTANT]
+> **Do not simply copy an old `gremlin-server.yaml` from 1.7.0 and edit class
names.**
+> Always start from the `conf/gremlin-server.yaml` bundled with HugeGraph
1.8.0 and carry over your customizations (ports, host, authenticator).
+> HugeGraph requires custom serializer builders and registries: GraphBinary
requires `builder:
org.apache.hugegraph.io.HugeGraphTypeSerializerRegistryBuilder`
+> to serialize HugeGraph `Id`, schema, `Blob`, and enum values (otherwise
failing with `IOException: Serializer for type ... not found`);
+> untyped GraphSON V1 requires
`org.apache.hugegraph.io.HugeGraphSONV1MessageIoRegistry`.
+
+| Legacy Serializer (`driver.ser.*`) | 1.8.0 Serializer (`util.ser.*`) |
Required Registries / Builder | Notes |
+|---|---|---|---|
+| `GraphBinaryMessageSerializerV1` |
`org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1` |
`builder:
org.apache.hugegraph.io.HugeGraphTypeSerializerRegistryBuilder`<br>`ioRegistries:
[org.apache.hugegraph.io.HugeGraphIoRegistry]` | Recommended default binary
serializer |
+| `GraphSONMessageSerializerV1d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV1` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry,
org.apache.hugegraph.io.HugeGraphSONV1MessageIoRegistry]` | Untyped GraphSON V1
|
+| `GraphSONMessageSerializerV2d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV2` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | Untyped
GraphSON V2 |
+| `GraphSONMessageSerializerV3d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV3` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | Untyped
GraphSON V3 |
+{.td-table--wide}
+
+### 2. Can HugeGraph Client 1.5.0 Continue to Work?
+* **Verdict**: **Yes**.
+* HugeGraph Server 1.8.0 provides backward-compatible adapters (graph alias
mapping and legacy auth path redirection). Applications using
`hugegraph-client:1.5.0` can continue standard CRUD, schema management, and
REST operations without forced SDK upgrades.
+* **Important Note**: Although Client 1.5.0 connects successfully, **all
submitted Gremlin script strings are executed on the 3.8.1 server engine**.
Therefore, Gremlin queries constructed in your code must comply with the 3.8
rules described above.
+
+---
+
+## Full Compatibility Reference
+
+### Appendix 1: Writes & Upsert (`mergeV` / `mergeE`)
+`mergeV()` and `mergeE()` are new steps in TinkerPop 3.8 (absent in 3.5.1) for
graph upserts:
+- **M01**: `onCreate` property maps strictly inherit the search match map.
Conflicting keys cannot overwrite matched properties.
+- **M02**: `mergeE` no longer implicitly binds incoming/outgoing vertices from
upstream traversers; specify `Direction.OUT` and `Direction.IN` in the map or
use explicit options.
+- **M03**: Conflicting `onCreate` / `onMatch` maps are validated during
construction rather than deferred to runtime execution.
Review Comment:
⚠️ Important: TinkerPop's construction-time validation applies to a static
`onCreate` map that overrides the merge search criteria; it does not validate
conflicts between `onCreate` and `onMatch` maps. Please narrow M03 to the
`onCreate` versus search-criteria case and mirror the correction in Chinese.
##########
content/en/docs/guides/gremlin-migration-3.8.md:
##########
@@ -0,0 +1,218 @@
+---
+title: "HugeGraph 1.8.0 Gremlin Migration & Compatibility Guide"
+linkTitle: "1.8 Gremlin & TinkerPop Migration"
+description: "Comprehensive guide to breaking changes, high-frequency syntax
rewrites, configuration migrations, and complete compatibility references from
TinkerPop 3.5 to 3.8 in HugeGraph 1.8.0."
+weight: 9
+---
+
+HugeGraph 1.8.0 upgrades its underlying graph traversal and query framework
from Apache TinkerPop 3.5.1 to **TinkerPop 3.8.1**, requires **Java 17** for
Server and builds, and updates the script engine to **Groovy 4.0.25**.
+
+While bringing substantial performance optimizations and specification
alignments, this upgrade introduces several language-level breaking changes.
This guide highlights the **most critical and frequently used syntax changes**
with recommended rewrites to ensure seamless migration. Edge cases and
lower-level protocol details are organized in the [Full Compatibility
Reference](#full-compatibility-reference) at the end.
+
+> [!NOTE]
+> - This guide applies to upgrades from version 1.7.0 (or earlier) to 1.8.0.
+> - The **Groovy script engine** remains supported in 1.8.0, allowing existing
multi-line scripts, variable definitions, and Java closures to continue
functioning as expected.
+
+---
+
+## Top 5 High-Frequency Syntax and Behavioral Changes
+
+Among existing query scripts, the following 5 changes are the most common.
**Failure to update these may cause query errors or silently return empty
results.**
+
+### 1. `has(key, traversal)` Overload Removed (High Risk: Silent Empty Results)
+
+* **Impact**:
+ In TinkerPop 3.8.0, the `has(String, Traversal)` and `has(T, Traversal)`
overloads were removed.
+ In the Groovy script engine, legacy code will not throw a compilation error;
instead, it **silently falls back to `has(String, Object)`**, comparing the
anonymous traversal object itself against vertex/edge properties. In practice,
this **silently yields 0 results / empty sets**!
+* **Legacy Query**:
+ ```groovy
+ // Broken in 3.8: will not throw an error, but silently returns 0 results
+ g.V().has('age', __.is(gt(30)))
+ g.V().has(T.label, __.is('person'))
+ ```
+* **Recommended Rewrite**:
+ ```groovy
+ // For simple scalar/predicate checks, pass predicates directly
+ g.V().has('age', gt(30))
+ g.V().hasLabel('person')
+ g.V().hasId('v-1')
+
+ // If passing property values into child traversals for complex filtering,
use filter()
+ g.V().filter(__.values('age').is(gt(30)))
+ ```
+
+---
+
+### 2. `store()` Removed & `aggregate()` Scope Overload Removed
+
+* **Impact**:
+ - The `store('x')` step, previously used for lazy side-effect collection,
has been completely removed (`No signature of method: store()`).
+ - The `aggregate(Scope, 'x')` overload with an explicit `Scope` (both
`local` and `global`) has also been removed.
+* **Legacy Query**:
+ ```groovy
+ // Broken: store step removed
+ g.V().store('x').by('age').cap('x')
+
+ // Broken: aggregate(Scope, ...) overload removed
+ g.V().aggregate(local, 'x')
+ g.V().aggregate(global, 'x')
+ ```
+* **Recommended Rewrite**:
+ ```groovy
+ // 1. Eager global collection (formerly global aggregate): use
aggregate('x') directly
+ g.V().aggregate('x').by('age').cap('x')
+
+ // 2. Per-object / lazy collection (formerly store or local aggregate): wrap
in local()
+ g.V().local(__.aggregate('x').by('age')).cap('x')
+ ```
+
+---
+
+### 3. Step `none()` Renamed to `discard()`
+
+* **Impact**:
+ In TinkerPop 3.5.1, `none()` was a traversal step used to discard all
incoming traversers (e.g., `g.V().hasLabel('person').none()`, which returned
`[]`).
+ In 3.8.1, this step has been renamed to **`discard()`**.
+ Meanwhile, 3.8 introduces a new collection predicate step `none(P)`
(checking if no elements in a collection match predicate P). Invoking
parameterless
+ `none()` in 3.8.1 fails immediately with:
+ `IllegalArgumentException: Input predicate to none step can't be null.`
+* **Recommended Rewrite**:
+ ```groovy
+ // Legacy (runs on 3.5.1, returning []):
+ g.V().hasLabel('person').none()
+
+ // Rewritten for 3.8: use discard() to discard traversers
+ g.V().hasLabel('person').discard()
+
+ // Discarding unhandled inputs in choose() branches:
+ g.V().choose(__.values('age')).option(gt(30),
__.values('name')).option(Pick.none, __.discard())
+ ```
+
+---
+
+### 4. Strict Behavior for Missing Properties in `by()`
+
+* **Impact**:
+ TinkerPop 3.6+ unified missing value handling across `by()` modulators
(removing previous default-null behaviors). In 3.8.1, when a property
referenced by `by()` does not exist:
+ 1. **Ordering `order().by('age')`**: Vertices missing `age` are
**immediately filtered out** (previously they might sort first/last).
+ 2. **Projection `project('name', 'age').by('name').by('age')`**: If `age` is
missing, the returned Map **will not contain the `age` key at all** (previously
returned `age: null`).
+ 3. **Path `path().by('age')`**: If any vertex along the path misses `age`,
the **entire path is dropped**.
+* **Recommended Rewrite**:
+ If your application requires preserving elements with explicit defaults
(e.g., `null`), use `coalesce()` inside `by()`:
+ ```groovy
+ // Retain key 'age' with null when missing
+ g.V().project('name', 'age')
+ .by('name')
+ .by(__.coalesce(__.values('age'), __.constant(null)))
+
+ // Provide a typed default for sorting missing fields
+ g.V().order().by(__.coalesce(__.values('age'), __.constant(-1)), desc)
+ ```
+
+---
+
+### 5. `choose()` Semantics Alignment
+
+* **Impact**:
+ - **Missing Property Handling**: When a choice child traversal evaluated an
absent property on an element, TinkerPop 3.5.1 threw:
+ `IllegalArgumentException: The provided traverser does not map to a value:
v[3]->[PropertiesStep([age],value)]`.
+ In 3.8.1, elements missing the target property pass through unchanged
without error.
+ - **Single Branch Execution**: When multiple `option()` conditions match
simultaneously, 3.8.1 **executes only the first matching branch** (legacy
versions might execute all matching branches).
+ - **Default Pass-Through**: When no `option()` condition matches, 3.8.1
**passes the input element through unchanged** by default (whereas 3.5.1
filtered it out).
+* **Recommended Rewrite**:
+ - To execute multiple matching branches, rewrite using `union()` combined
with `filter()`.
+ - To filter out elements when no option matches or property is missing,
explicitly supply `Pick.none` and `Pick.unproductive` branches:
+ ```groovy
+ // Discard unmatched and missing-property inputs
+ g.V().choose(__.values('age'))
+ .option(gt(30), __.values('name'))
+ .option(Pick.none, __.discard())
+ .option(Pick.unproductive, __.discard())
+ ```
+
+---
+
+## New Steps & Functions in 1.8 (TinkerPop 3.8)
+
+TinkerPop 3.8 introduces native date manipulation functions and steps that
were not present in 3.5.1 (HugeGraph 1.7.0):
+
+### Date Steps (`datetime()`, `dateAdd()`, `dateDiff()`)
+
+- **`datetime()`**: Creates a date-time object or retrieves server time.
+ In 3.8, it returns **`java.time.OffsetDateTime`** (e.g.,
`datetime('2026-10-19T00:00:00Z')`).
+- **`dateAdd()`**: Performs date arithmetic, adding a duration to a date.
+- **`dateDiff()`**: Computes the difference between two date-time objects,
returning the difference in **milliseconds**
+ (e.g., a 1-day difference yields `86400000` ms).
+- **Example**:
+ ```groovy
+ // Current time as OffsetDateTime
+ g.inject(datetime())
+
+ // Compute difference in milliseconds, convert to seconds
+
g.inject(datetime('2026-10-19T00:00:00Z')).dateDiff(datetime('2026-10-18T00:00:00Z'))
+ .math('_ / 1000').asNumber(GType.LONG)
+ ```
+
+---
+
+## Server Configuration and Client Compatibility
+
+### 1. Serializer Configuration in `conf/gremlin-server.yaml`
+
+In TinkerPop 3.8, serializer classes moved from `gremlin-driver`
(`org.apache.tinkerpop.gremlin.driver.ser.*`)
+to `gremlin-util` (`org.apache.tinkerpop.gremlin.util.ser.*`).
+
+> [!IMPORTANT]
+> **Do not simply copy an old `gremlin-server.yaml` from 1.7.0 and edit class
names.**
+> Always start from the `conf/gremlin-server.yaml` bundled with HugeGraph
1.8.0 and carry over your customizations (ports, host, authenticator).
+> HugeGraph requires custom serializer builders and registries: GraphBinary
requires `builder:
org.apache.hugegraph.io.HugeGraphTypeSerializerRegistryBuilder`
+> to serialize HugeGraph `Id`, schema, `Blob`, and enum values (otherwise
failing with `IOException: Serializer for type ... not found`);
+> untyped GraphSON V1 requires
`org.apache.hugegraph.io.HugeGraphSONV1MessageIoRegistry`.
+
+| Legacy Serializer (`driver.ser.*`) | 1.8.0 Serializer (`util.ser.*`) |
Required Registries / Builder | Notes |
+|---|---|---|---|
+| `GraphBinaryMessageSerializerV1` |
`org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1` |
`builder:
org.apache.hugegraph.io.HugeGraphTypeSerializerRegistryBuilder`<br>`ioRegistries:
[org.apache.hugegraph.io.HugeGraphIoRegistry]` | Recommended default binary
serializer |
+| `GraphSONMessageSerializerV1d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV1` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry,
org.apache.hugegraph.io.HugeGraphSONV1MessageIoRegistry]` | Untyped GraphSON V1
|
+| `GraphSONMessageSerializerV2d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV2` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | Untyped
GraphSON V2 |
+| `GraphSONMessageSerializerV3d0` |
`org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV3` |
`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | Untyped
GraphSON V3 |
+{.td-table--wide}
+
+### 2. Can HugeGraph Client 1.5.0 Continue to Work?
+* **Verdict**: **Yes**.
+* HugeGraph Server 1.8.0 provides backward-compatible adapters (graph alias
mapping and legacy auth path redirection). Applications using
`hugegraph-client:1.5.0` can continue standard CRUD, schema management, and
REST operations without forced SDK upgrades.
+* **Important Note**: Although Client 1.5.0 connects successfully, **all
submitted Gremlin script strings are executed on the 3.8.1 server engine**.
Therefore, Gremlin queries constructed in your code must comply with the 3.8
rules described above.
+
+---
+
+## Full Compatibility Reference
+
+### Appendix 1: Writes & Upsert (`mergeV` / `mergeE`)
+`mergeV()` and `mergeE()` are new steps in TinkerPop 3.8 (absent in 3.5.1) for
graph upserts:
Review Comment:
🧹 Minor: TinkerPop introduced `mergeV()` and `mergeE()` in 3.6.0, with
further semantics changes in 3.6.2; they are not new in TinkerPop 3.8. Since
this guide compares HugeGraph's 3.5.1 and 3.8.1 stacks, please describe them as
newly available in HugeGraph 1.8.0 or in this upgrade, and mirror that wording
in Chinese.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]