# HugeGraph 1.8.0 Gremlin 迁移与兼容性指南

> 详细说明 HugeGraph 1.8.0 从 TinkerPop 3.5 升级到 3.8 带来的核心语法兼容性变化、常用改写方案、配置调整及全量细则速查表。

---

LLMS 索引： [llms.txt](/cn/llms.txt)

---

HugeGraph 1.8.0 将底层图遍历与查询框架从 Apache TinkerPop 3.5.1 升级至 **TinkerPop 3.8.1**，运行环境统一要求 **Java 17**（服务端与构建），并将脚本引擎升级至 **Groovy 4.0.25**。

此次升级引入了部分语言层面的破坏性变更（Breaking Changes）。下文优先列出**最常用且行为发生变化的核心语法与改写方案**，以便快速排查和迁移；次常用选项及底层协议等边缘细节整理在文末的[全量细则速查表](#全量细则速查表)。

> [!NOTE]
> - 本指南适用于从 1.7.0（或更早版本）升级至 1.8.0 的用户。
> - 1.8.0 依然保留了 **Groovy 脚本引擎**支持，既有的多行脚本、变量定义及 Java 闭包等大多数逻辑仍可正常运行。

---

## 最常用的 5 大语法与行为变化

以下 5 项改动在已有业务查询中最为高频。前 3 项若不改写，在 HugeGraph 1.8 中会直接抛出异常；后 2 项不报错，但会静默改变返回结果（自动剔除缺失属性的元素或放行未匹配分支），需重点排查。

### 1. `has(key, traversal)` 重载删除（高危：查询执行报错）

* **影响说明**：
  在 TinkerPop 3.8.0 中，`has(String, Traversal)` 和 `has(T, Traversal)` 重载被彻底移除。
  在 Groovy 脚本引擎下，旧写法会退化命中 `has(String, Object)` 重载。在 HugeGraph 1.8 中，由于图引擎将 `has()` 谓词下推至后端存储检索，并通过 `TraversalUtil.validPropertyValue()` 对属性值进行强类型校验，**会直接抛出类型不匹配异常**：
  - 属性过滤如 `g.V().has('age', __.is(gt(30)))` 抛出：
    `IllegalArgumentException: Invalid data type of query value '[IsStep(gt(30))]', expect INT for 'age', actual got class org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal`
  - 标签过滤如 `g.V().has(T.label, __.is('person'))` 抛出：
    `HugeException: The label type must be number or string, but got 'class org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal'`
  *(注：仅在纯内存 TinkerGraph 上会静默返回空数组 `[]`，而在 HugeGraph 1.8 上会严格报错)*。
* **典型旧写法**：
  ```groovy
  // 错误！在 HugeGraph 1.8 下执行将抛出 Invalid data type 异常
  g.V().has('age', __.is(gt(30)))
  g.V().has(T.label, __.is('person'))
  ```
* **推荐改写方案**：
  ```groovy
  // 简单标量/谓词判断，直接传值或 P 谓词
  g.V().has('age', gt(30))
  g.V().hasLabel('person')
  g.V().hasId('v-1')

  // 若需要把属性值传给复杂子遍历计算后再过滤，改用 filter()
  g.V().filter(__.values('age').is(gt(30)))
  ```

---

### 2. `store()` 彻底删除 & `aggregate()` Scope 重载移除

* **影响说明**：
  - 旧版中用于惰性边走边收集副作用的 `store('x')` 步骤已被完全删除，在 3.8.1 中直接报错 `MissingMethodException: No signature of method: ...store()`。
  - `aggregate(Scope, 'x')` 的带 Scope 重载也被删除（包括 `aggregate(local, 'x')` 和 `aggregate(global, 'x')`）。
* **典型旧写法**：
  ```groovy
  // 错误！store 步骤已被移除
  g.V().store('x').by('age').cap('x')

  // 错误！aggregate 的 scope 重载已被移除
  g.V().aggregate(local, 'x')
  g.V().aggregate(global, 'x')
  ```
* **推荐改写方案**：
  ```groovy
  // 1. 全局贪心收集（原 global 聚合）：直接使用 aggregate('x')
  g.V().aggregate('x').by('age').cap('x')

  // 2. 局部/惰性逐个对象收集（原 store 或 local 聚合）：使用 local(__.aggregate('x'))
  g.V().local(__.aggregate('x').by('age')).cap('x')
  ```

---

### 3. 丢弃输入步骤 `none()` 更名为 `discard()`

* **影响说明**：
  在 TinkerPop 3.5.1 中，`none()` 为 traversal 步骤，用于丢弃流中输入（如 `g.V().hasLabel('person').none()` 返回 `[]`）。
  3.8.1 中该操作更名为 **`discard()`**。
  同时 3.8 引入了新的集合谓词步骤 `none(P)`（用于检查集合是否没有元素满足条件）。若在 3.8.1 中直接调用无参 `none()`，会立即抛出异常：
  `IllegalArgumentException: Input predicate to none step can't be null.`
* **推荐改写方案**：
  ```groovy
  // 旧写法（3.5.1 下执行返回 []）：
  g.V().hasLabel('person').none()

  // 新写法：统一使用 discard()
  g.V().hasLabel('person').discard()

  // 在 choose() 或分支中丢弃未匹配元素与属性缺失元素：
  g.V().choose(__.values('age'))
    .option(gt(30), __.values('name'))
    .option(Pick.none, __.discard())
    .option(Pick.unproductive, __.discard())
  ```

---

### 4. `by()` 属性缺失行为变严（可能导致排序丢点、投影丢字段、路径丢失）

* **影响说明**：
  TinkerPop 3.6+ 统一了 `by()` 调制器的缺失值处理（去除了原先部分默认补 null 的策略）。在 3.8.1 中，当 `by()` 依赖的属性不存在时：
  1. **排序 `order().by('age')`**：缺少 `age` 属性的顶点**直接被过滤掉**（旧版可能保留在开头或末尾）。
  2. **投影 `project('name', 'age').by('name').by('age')`**：若某个顶点缺少 `age`，返回的 Map 中**将直接不包含 `age` 键**（旧版会返回 `age: null`）。
  3. **路径 `path().by('age')`**：路径上若有任一节点缺少该属性，**整条路径将被直接丢弃**。
* **推荐改写方案**：
  如果业务要求在属性缺失时仍保留元素并填入默认值（如 `null` 或默认占位值），需在 `by()` 中使用 `coalesce()` 显式保底：
  ```groovy
  // 保证缺 age 时依然保留该字段，且值为 null
  g.V().project('name', 'age')
    .by('name')
    .by(__.coalesce(__.values('age'), __.constant(null)))

  // 排序时为缺失属性指定统一后置的默认值（需保持数据类型一致）
  g.V().order().by(__.coalesce(__.values('age'), __.constant(-1)), desc)
  ```

---

### 5. 分支条件 `choose()` 语义收敛

* **影响说明**：
  - **缺失属性处理**：当选择子遍历依赖的属性缺失时，TinkerPop 3.5.1 会直接抛出异常：
    `IllegalArgumentException: The provided traverser does not map to a value: v[3]->[PropertiesStep([age],value)]`。
    在 3.8.1 中，缺少对应属性的元素不再报错，而是默认原样放行穿透。
  - **单分支执行**：当多个 `option()` 条件同时命中时，旧版可能会把多个匹配分支全部执行；新版**只执行第一个命中的分支**。
  - **默认放行机制**：如果没有任何一个 `option()` 匹配，新版**默认将输入元素原样向下游放行**（3.5.1 则会过滤掉）。
* **推荐改写方案**：
  - 如需在多个条件同时命中时全部执行，请使用 `union()` 配合 `filter()`。
  - 如需在无匹配或属性缺失时将元素过滤抛弃，需显式声明 `Pick.none` 与 `Pick.unproductive` 分支：
  ```groovy
  // 显式丢弃无匹配项以及缺少 age 属性的项
  g.V().choose(__.values('age'))
    .option(gt(30), __.values('name'))
    .option(Pick.none, __.discard())
    .option(Pick.unproductive, __.discard())
  ```

---

## 1.8 新增步骤与内置函数（TinkerPop 3.8）

TinkerPop 3.8 引入了原生日期操作函数与步骤，这些能力在 3.5.1（HugeGraph 1.7.0）中并不存在：

### 日期步骤（`datetime()`、`dateAdd()`、`dateDiff()`）

- **`datetime()`**：构造日期时间对象或取得服务器当前时间。在 3.8 中返回 **`java.time.OffsetDateTime`**（如 `datetime('2026-10-19T00:00:00Z')`）。
- **`dateAdd()`**：对日期进行加减运算。
- **`dateDiff()`**：计算两个日期的差值，返回差值的**毫秒数**（例如相隔 1 天的结果为 `86400000` ms）。
- **代码示例**：
  ```groovy
  // 获取当前时间 OffsetDateTime
  g.inject(datetime())

  // 计算日期毫秒差并转换为秒
  g.inject(datetime('2026-10-19T00:00:00Z')).dateDiff(datetime('2026-10-18T00:00:00Z'))
    .math('_ / 1000').asNumber(GType.LONG)
  ```

---

## 服务端配置与客户端连接兼容

### 1. `conf/gremlin-server.yaml` 序列化器配置

在 TinkerPop 3.8 中，序列化器从 `gremlin-driver`（`org.apache.tinkerpop.gremlin.driver.ser.*`）包迁移到了 `gremlin-util`（`org.apache.tinkerpop.gremlin.util.ser.*`）包。

> [!IMPORTANT]
> **请勿直接复制 1.7.0 的 `gremlin-server.yaml` 并仅替换类名！**
> 推荐直接使用 HugeGraph 1.8.0 发行包自带的 `conf/gremlin-server.yaml`，再将自定义端口、认证等配置合并进去。
> HugeGraph 需要专用的 Builder 与 Registry：GraphBinary 必须配置 `builder: org.apache.hugegraph.io.HugeGraphTypeSerializerRegistryBuilder`
> 才能序列化 HugeGraph 的 `Id`、Schema、`Blob` 与枚举类型（否则会抛出 `IOException: Serializer for type ... not found`）；
> 无类型 GraphSON V1 需要添加 `org.apache.hugegraph.io.HugeGraphSONV1MessageIoRegistry`。

| 旧配置类名 (`driver.ser.*`) | 1.8.0 对应类名 (`util.ser.*`) | 所需 Builder / Registry | 说明 |
|---|---|---|---|
| `GraphBinaryMessageSerializerV1` | `org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1` | `builder: org.apache.hugegraph.io.HugeGraphTypeSerializerRegistryBuilder`<br>`ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | 推荐，默认高效二进制协议 |
| `GraphSONMessageSerializerV1d0` | `org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV1` | `ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry, org.apache.hugegraph.io.HugeGraphSONV1MessageIoRegistry]` | 保持无类型 GraphSON V1 |
| `GraphSONMessageSerializerV2d0` | `org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV2` | `ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | 标准 Typed GraphSON V2 |
| `GraphSONMessageSerializerV3d0` | `org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3` | `ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | 标准 Typed GraphSON V3 |
| *(1.8 新增)* | `org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV2` | `ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | 无类型 GraphSON V2 (`types=false`) |
| *(1.8 新增)* | `org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV3` | `ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]` | 无类型 GraphSON V3 (`types=false`) |
{.td-table--wide}

### 2. HugeGraph Client 1.5.0 能否平滑使用？
* **结论**：**基础数据 CRUD 与 Schema 操作可以继续使用；Gremlin 查询与鉴权管理需要升级或适配**。
* 服务端在 1.8.0 中增加了向前兼容层，使用 `hugegraph-client:1.5.0` 进行常规顶点/边 CRUD 和 Schema 操作均可正常连通。
* **限制说明**：
  - **Gremlin 查询别名**：Client 1.5.0 的 `gremlin().execute()` 发送旧版图别名（`graph=<graph>` 与 `g=__g_<graph>`），而 1.8.0 服务端仅绑定了 GraphSpace 命名空间的别名（`DEFAULT-<graph>` 与 `__g_DEFAULT-<graph>`），且服务端 `/gremlin` 会原样透传请求体，因此 1.5.0 客户端的 Gremlin 查询会因别名未识别而失败。如需在 1.8.0 下执行 Gremlin 查询，请升级客户端或改用 HTTP 接口显式传递正确的 aliases。
  - **Auth 鉴权路径**：1.8.0 将用户与鉴权体系重构至 GraphSpace 维度（`graphspaces/{graphspace}/auth/users`），Client 1.5.0 的 `graphs/{graph}/auth/...` 路径并不包含在此重定向范围内，鉴权管理操作需升级 Client。
  - **Gremlin 脚本语法**：客户端所发送的 Gremlin 脚本字符串均在服务端的 3.8.1 引擎中执行，必须按上述规则适配 3.8 语法。

---

## 全量细则速查表

除上述高频变化外，TinkerPop 3.5.1 → 3.8.1 还包含若干深层优化与边缘语义调整。如遇特定场景异常，请查阅下表对应项：

### 附录 1：写入与 Upsert（`mergeV` / `mergeE`）
在 HugeGraph 1.8.0（自 TinkerPop 3.6.0 起引入）中可使用标准的 `mergeV()` 与 `mergeE()` 执行图数据 Upsert 操作：
- **M01**: `onCreate` 的属性 Map 严格继承主匹配 Map，不允许用冲突值覆盖主匹配条件（如匹配条件是 `name: 'A'`，`onCreate` 不能设 `name: 'B'`）。
- **M02**: `mergeE` 不再隐式拿上游顶点填补端点，必须在 Map 中指明 `(Direction.OUT)` 与 `(Direction.IN)`，或使用 `option(Merge.outV, ...)` 显式绑定。
- **M03**: 静态 `onCreate` Map 与 match 检索匹配条件发生键值冲突时，在构造阶段就会被严格校验，而非等到运行时才报错。
- **M04**: `onMatch` 子遍历在 3.8 中接收的是当前已匹配到的 Element 对象，而非最开始输入的 Map。若需引用外部输入参数，请在 merge 前通过 `.as('input')` 保存引用，内部通过 `select('input')` 取值。

### 附录 2：遍历内部高级步骤与集合行为
- **B07 (`local()` 拆 bulk)**：带 bulk 计数的 Traverser 经过 `local()` 子遍历时会被展开为单个对象处理。若需统计频次，建议使用 `groupCount()`。
- **B09 (`range(local)` 集合解包)**：对列表截取单元素（如 `limit(local, 1)`），新版始终返回 Collection 包装（如 `[1]`），若业务必须获取标量，请在后续追加 `.unfold()`。
- **B11 (`sum()` 返回类型)**：整数流的 `sum()` 结果可能直接返回 Integer 而不是 Long（若无溢出），建议 Java 端统一使用 `Number` 接收。
- **B14 (`split()` 步骤)**：HugeGraph 1.8 新增步骤。空字符串分隔符将拆分为单字符列表（如 `'abc'` 拆分为 `['a','b','c']`）。
- **B15 (`asString()` 步骤)**：HugeGraph 1.8 新增步骤。`asString()` 接收到 null 时会抛出 `IllegalArgumentException`，如需兼容 null 请配合 `choose()` 显式处理。

### 附录 3：多语言客户端差异（GLV）
- **Python**：步骤名避免与 Python 关键字冲突，全面使用下划线后缀（如 `filter_()`、`id_()`、`range_()`、`max_()` 等），推荐使用 snake_case（如 `has_label`）。
- **JavaScript**：返回的集合从 Array 转为真正的 ECMAScript `Set`（通过 `.size`、`.has()` 操作）；移除废弃的 `connectOnStartup` 选项，需使用 `await connection.open()`。
- **.NET**：byte 类型严格按有符号整数（sbyte）序列化。
- **Go**：BigDecimal/BigInt 解析对齐，必须始终检查 `Next()` 的 error 返回。

### 附录 4：底层协议与序列化边界
- **C03 (OLTP 元素属性携带)**：远程查询返回的 Vertex/Edge 默认携带属性，不再仅是轻量引用的 id/label。若只关注特定业务字段，建议显式通过 `project()` 或 `valueMap()` 投影，避免网络负载膨胀。
- **C06 (GraphSON 载荷安全限制)**：新增数字长度（默认 10,000）、字符串长度（默认 20,000,000）与嵌套深度（默认 1,000）反序列化上限，避免深层 JSON 导致 OOM。

### 附录 5：HugeGraph 适配与能力边界
- **元属性保持不支持（Unchanged）**：HugeGraph 严格遵循自有 Schema 体系，历来不支持 TinkerPop 的属性元数据（属性上的属性）。
  向顶点属性写入 metadata 始终会抛出 `VertexProperty.Exceptions.metaPropertiesNotSupported()` 或 `NotSupportException("nested property")`，1.8.0 行为与历史版本完全一致。
- **图计算限制**：`supportsComputer()` 仍为 `false`，依赖 `GraphComputer` 的步骤（如 `pageRank()`、`peerPressure()`）不能在 OLTP 引擎上直接执行，建议使用 HugeGraph 的专门算法 API 或 Vermeer 组件。
- **统一列表特性**：`supportsUniformListValues()` 现返回 `false`；图中的 List 属性依然由 HugeGraph 内部 Schema (`valueList()`) 提供完整支持。

---

反链：

- [Release-1.8.0](/cn/docs/changelog/hugegraph-1.8.0-release-notes/)
- [图查询语言](/cn/docs/language/)
- [安装/构建 HugeGraph Server](/cn/docs/quickstart/hugegraph/hugegraph-server/)
