diff --git a/content/cn/docs/language/_index.md b/content/cn/docs/language/_index.md index 16dc29be79..46307b379d 100644 --- a/content/cn/docs/language/_index.md +++ b/content/cn/docs/language/_index.md @@ -4,4 +4,4 @@ linkTitle: "查询语言" weight: 7 --- -HugeGraph 支持 Gremlin 和 Cypher。这里主要介绍 Gremlin 用法;Cypher HTTP 接口见 [Cypher API](/cn/docs/clients/restful-api/cypher/)。 +HugeGraph 支持 Gremlin 和 Cypher。本节涵盖两种语言;Cypher HTTP 接口见 [Cypher API](/cn/docs/clients/restful-api/cypher/)。 diff --git a/content/cn/docs/language/cypher-compatibility.md b/content/cn/docs/language/cypher-compatibility.md new file mode 100644 index 0000000000..f84690910d --- /dev/null +++ b/content/cn/docs/language/cypher-compatibility.md @@ -0,0 +1,142 @@ +--- +title: "Cypher 兼容性" +linkTitle: "Cypher 兼容性" +weight: 3 +--- + +### 范围 + +**下文验证结果描述的是 `master`,不能直接套用于已发布版本。** HugeGraph 1.7.0 在 +`/graphspaces/{graphspace}/graphs/{graph}/cypher` 上只提供两种请求形式:`GET ?cypher=` +和 `POST application/json`(请求体为原始 Cypher 文本)。1.7.0 之前的版本在 `/graphs/{graph}/cypher` 下 +提供同样的两种形式。已发布版本不接受绑定参数,引用 `$param` 的语句 +不会报错,缺失的绑定会被内联为 `null`(基于它的谓词匹配不到行,而 `RETURN $value AS value` 仍会返回一行)——参见[已知限制](/cn/docs/language/hugegraph-cypher/#已知限制)。 + +本页记录当前 Cypher 开发改动实际验证过的用例,不代表完整支持 openCypher 或 Neo4j,也不代表所有已发布 +版本的 HugeGraph 都具备相同行为。测试代码基于当前 Apache `master` 的 +[`5039e5b`](https://github.com/apache/hugegraph/commit/5039e5b67d26ea305b8509d3a414877749baaebb), +该提交声明 HugeGraph `1.8.0`,使用 Java 17 和 TinkerPop `3.8.1`,且 Cypher 改动 +([ASF PR #3289](https://github.com/apache/hugegraph/pull/3289))已合入。运行栈使用 Java `17.0.20.1`(Temurin)、 +TinkerPop `3.8.1`、`org.opencypher.gremlin:translation:1.0.4` 和 RocksDB。合入前在 +[`af3c686`](https://github.com/apache/hugegraph/commit/af3c6867f4bfa3f95e63ad472c12a88c63529c1b) +上的验证仍见下文。 + +接口路径为 `/graphspaces/{graphspace}/graphs/{graph}/cypher`。一般用法见 +[HugeGraph Cypher 指南](/cn/docs/language/hugegraph-cypher/)。 + +### 请求形式与参数 + +| 请求 | 是否存在于已发布版本 | 测试栈上已验证行为 | 测试方法 | +|---|---|---|---| +| `GET ?cypher=` | 是 | 保留原有查询参数形式 | `testGet` | +| `POST application/json`,请求体为原始 Cypher 文本 | 是 | 保留旧的原始文本请求形式 | `testPost` | +| `POST text/plain`,任意请求体 | 否 | 返回 HTTP 415 | `testRejectPlainTextPost` | +| `POST application/json`,请求体为 JSON 对象 | 否 — 由 [#3289](https://github.com/apache/hugegraph/pull/3289) 引入 | 查询语句与参数分开传入 | `testParameters` | + +因此下面的 JSON 对象形式**在任何已发布的 HugeGraph 版本中都不可用**,它描述已合入 `master`(1.8.0 开发线)的改动。 +在已发布版本上,引用 `$param` 的语句会把缺失的绑定内联为 `null`,是否返回行取决于表达式(`WHERE n.name = $name` 匹配不到行,`RETURN $value AS value` 会返回一行 `null`)——参见 +[已知限制](/cn/docs/language/hugegraph-cypher/#已知限制)。 + +```json +{ + "cypher": "MATCH (n:cypher_person) WHERE n.name = $name RETURN n.name", + "parameters": {"name": "marko"} +} +``` + +JSON 数组、字符串、数字、布尔值或 null 请求体会返回 HTTP 400。 +格式错误的 JSON 对象和尾随内容也会产生请求错误,不会回退为原始 Cypher 文本。 + +JSON 对象中的 `cypher` 必须是非空白字符串;`parameters` 若提供,必须是对象。省略 `parameters` 表示空参数表。 +语句引用了未提供的参数时会报执行错误;显式传入 `null` 是有效值。在测试栈上,该执行错误适用于所有请求形式, +包括 `GET ?cypher=` 和原始文本 `POST`:[#3289](https://github.com/apache/hugegraph/pull/3289) 总是附带 +参数表并拒绝未解析的 `$param` 引用,取代了上文描述的已发布版本“缺失绑定按 `null` 处理”行为。测试覆盖字符串、数字、布尔值、引号和换行, +参数名 `id` 与 `label`。当前实现的 `parameters` 最多接受 16 个顶层条目,该上限固定。 +该上限只统计顶层 `parameters` 对象中的键。Cypher 不支持在 Gremlin Server 的处理器配置中设置 +`maxParameters`。参数值会作为绑定值传递,不会改写查询文本。 + +一个 Map 只计为一个顶层参数。可以将相关值放入一个 Map,并显式引用各字段;顶点标签和必需属性必须匹配图中已有的 Schema: + +```json +{ + "cypher": "CREATE (n:cypher_person {name:$props.name, age:$props.age, city:$props.city}) RETURN n.name", + "parameters": {"props": {"name": "new-person", "age": 20, "city": "Beijing"}} +} +``` + +实际 REST 验证确认,一个包含 17 个条目的 Map 可以通过显式属性引用完成写入,并通过原生 REST 核对全部 17 个持久化值。 +返回包含 17 个条目的 Map 也成功,而 17 个顶层绑定参数会产生执行错误。请使用上述显式属性引用; +固定版本的转译库不会通过 CREATE 的简写形式赋予绑定 Map 中的属性。 + +转译库将精确字符串 `" cypher.null"`(开头有两个空格)保留为 null 标记,因此绑定值会拒绝该字符串, +包括列表和嵌套 Map 中的值。显式 null 仍受支持。语句字面量及存储属性等于该字符串的情况仍受转译库限制, +本次改动未验证这些值能够保真。 + +同时检查 HTTP 状态和响应体的 `status.code`:执行错误可能仍返回 HTTP 200,但 `status.code` 为 400,结果数据为 null。 + +### 已验证行为 + +API 测试夹具使用强类型 Schema,并为 `city` 建立 `SECONDARY` 索引、为 `age` 建立 `RANGE` 索引。验收结论仅限于下列用例: + +| 范围 | 测试用例 | 测试方法 | +|---|---|---| +| 查询 | 标签扫描、相等和范围条件、布尔组合、有向一跳和两跳关系、空结果、计算字符串的正则全字符串匹配 | `testGet`、`testExactReadsAndPredicates`、`testRelationQuery`、`testComputedRegexExecutesExtensionPredicate`、`testComputedRegexRequiresWholeStringMatch` | +| 结果 | 别名、标量和节点值、嵌套值、关系 ID、路径结构、null 与缺失属性 | `testReturnNodeIdAsPrimitiveValue`、`testReturnNodeDoesNotLeakInternalIdTypes`、`testReturnNestedIdDoesNotLeakInternalIdTypes`、`testReturnRelationIdDoesNotLeakInternalIdTypes`、`testReturnPathShape`、`testNullAndMissingProperty` | +| 聚合和分页 | `DISTINCT`、`count`/`sum`/`min`/`max`/`avg`、`ORDER BY`、`SKIP`、`LIMIT` | `testDuplicatesDistinctAndPagination`、`testAggregates` | +| 写入 | 创建顶点和边、修改属性、删除边和顶点;通过原生 REST 读取检查状态 | `testCreate`、`testCreateSetAndDeleteWithNativeReadback` | +| 失败写入 | 拒绝双顶点 `CREATE` 后,连续 32 次查询和原生读取均未发现残留 | `testFailedWriteDoesNotLeakIntoLaterRequests` | +| 错误与路由 | 非法语法和请求结构、内容类型、参数键与保留的 null 标记、Schema 值、认证及图路由 | `testInvalidRequests`、`testRejectPlainTextPost`、`testRejectInvalidQuery`、`testRejectInvalidBindingShapeAndKeys`、`testRejectTranslatorNullMarker`、`testRejectTranslatorNullSentinelInBindings`、`testAuthenticationAndGraphRouting`、`testSpecifiedGraphRouting` | + +合入后在 `5039e5b` 上的补充 REST 验证对全新 RocksDB 图重新执行了文档示例:查询、带排序和 LIMIT 的投影、聚合、 +单顶点 `CREATE`、`SET`/`REMOVE`、边 `DELETE`、`DETACH DELETE`,以及上表全部请求形式约定均与文档一致, +包括三种请求形式下缺失绑定的执行错误。双顶点简写 `CREATE (a)-[:knows]->(b)` 在同一运行中于 `person` 标签使用 +`AUTOMATIC` id 策略的图上验证通过;在 `PRIMARY_KEY` 标签上,同一语句会被拒绝并返回执行错误(提示必须设置主键), +而同一标签上的单顶点 `CREATE` 成功。在 `PRIMARY_KEY` Schema 上,请将两条 `CREATE` 语句分开执行,或对该形式使用 +`AUTOMATIC` id 策略。 + +指南中有两个示例依赖 Schema。范围谓词查询(`WHERE n.age > 30`)需要上文夹具 Schema 中声明的 `age` 上的 `RANGE` 索引; +`scripts/example.groovy` 的 Schema 只为 `person` 建了 `SECONDARY` 索引(`city`,以及 `age` 与 `city` 的组合), +因此在那里对 `age` 做范围比较会被拒绝并报“未建索引”错误,直到建立 `RANGE` 索引。创建、更新和删除示例随后改名为 +`alice`、`bob` 和 `carol`,因为 `scripts/example.groovy` 已预置 `marko`、`vadas`、`josh` 和 `peter`,而在 `PRIMARY_KEY` 标签上 +用预置名字执行 `CREATE` 会替换该顶点;改名只改动数据取值,没有作为单独的验证重新运行。 + +独立代码改动位于 [ASF PR #3289](https://github.com/apache/hugegraph/pull/3289),该 PR 已于 2026-10-08 合入 Apache `master`,合入提交为 +[`5039e5b`](https://github.com/apache/hugegraph/commit/5039e5b67d26ea305b8509d3a414877749baaebb);上文的验证记录直接描述该合入后的提交, +合入前验证的基线为 +[`af3c686`](https://github.com/apache/hugegraph/commit/af3c6867f4bfa3f95e63ad472c12a88c63529c1b)。 +原始开发改动仍关联 [PR #238](https://github.com/hugegraph/hugegraph/pull/238)。 +在固定源码提交 +[`8d06ad3`](https://github.com/apache/hugegraph/blob/8d06ad3b18fbd18807cf545184d07e6fec0c55b4/docs/cypher-compatibility.md) 上,`CypherApiTest` 通过 23/23, +`CypherClientTest` 通过 7/7,`CypherOpProcessorTest` 通过 8/8,均无跳过。新增的谓词、执行上下文和请求资源所有权 +回归测试均通过;`AbstractRestClientTest` 的九项测试也全部通过,包括 ASCII 请求体、UTF-16 Map 请求体及 gzip。 +Login 测试通过 3/3;相关 Gremlin 测试通过 10 项,保留一项因后端不共享而不适用的 `testClearAndInit` 跳过。 +EditorConfig 格式检查、仓库根目录 clean compile 和完整 install 均通过。在实际服务测试前,分发包内的 API JAR 与编译目标 JAR 一致。 + +该固定提交中的兼容性说明包含验证记录和逐方法映射。测试源码: +[`CypherApiTest`](https://github.com/apache/hugegraph/blob/8d06ad3b18fbd18807cf545184d07e6fec0c55b4/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/CypherApiTest.java)、 +[`CypherClientTest`](https://github.com/apache/hugegraph/blob/8d06ad3b18fbd18807cf545184d07e6fec0c55b4/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/cypher/CypherClientTest.java) +和 +[`CypherOpProcessorTest`](https://github.com/apache/hugegraph/blob/8d06ad3b18fbd18807cf545184d07e6fec0c55b4/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessorTest.java)。 +此前在 +[`e62c961`](https://github.com/apache/hugegraph/commit/e62c961e00221569d4f955abbadf60faee45b283) +基线上的验证仍保留在 +[`fd8f4a6`](https://github.com/apache/hugegraph/blob/fd8f4a626b9e899663d3114035e2967e421b3e55/docs/cypher-compatibility.md)。 +此前变基前的验证记录仍保留在 +[`c3b2f3e`](https://github.com/hugegraph/hugegraph/blob/c3b2f3e3b9ff1de0495260d6eec0b16816d7f095/docs/cypher-compatibility.md)。 + +### 基线故障与当前修复 + +在此前的基线 +[`27a7c9b`](https://github.com/apache/hugegraph/commit/27a7c9b42274d6d4f95eabed6d2051d393ae0eaf) +中,失败的双顶点 `CREATE` 在两次观察中都留下了延迟可见的残留。第一次运行中,即时原生读取未发现前缀, +但后续聚合/排序查询暴露了前缀,原生全量列表也确认了它。第二次复现时,即时原生读取和之后 7 次 count/原生读取均未见前缀; +第 8 次 count 后才出现。原始基线证据保留在 +[基线报告](https://github.com/apache/hugegraph-doc/pull/499#issuecomment-5826172378)。 + +当前开发改动通过了 `testFailedWriteDoesNotLeakIntoLaterRequests`:针对该夹具,原生读取和后续 32 轮查询/原生读取均确认无残留。 +这只验证了已报告的失败场景及当前测试栈,不能据此推断所有回滚路径、跨请求事务语义、所有语句或后端的写原子性。 + +### 本次未验证 + +本次测试范围之外的高级语法和行为仍未验证,包括 `MERGE`、`OPTIONAL MATCH`、`WITH`、`UNWIND`、`UNION`、变长路径、 +广泛的函数覆盖、过程调用、Cypher Schema DDL、`EXPLAIN`/`PROFILE`、HStore、Bolt、跨请求事务和性能。 diff --git a/content/cn/docs/language/hugegraph-cypher.md b/content/cn/docs/language/hugegraph-cypher.md new file mode 100644 index 0000000000..389c8d67b2 --- /dev/null +++ b/content/cn/docs/language/hugegraph-cypher.md @@ -0,0 +1,180 @@ +--- +title: "HugeGraph Cypher" +linkTitle: "Cypher 查询语言" +weight: 2 +--- + +### 概述 + +HugeGraph 支持 [Cypher](https://opencypher.org/) —— 最初由 Neo4j 创建、并由 [openCypher](https://github.com/opencypher/openCypher) 标准化的声明式图查询语言。Gremlin 是命令式的遍历语言,而 Cypher +只需描述想要的图模式(`MATCH`、`WHERE`、`RETURN`),由引擎决定如何执行。 + +HugeGraph 通过 [cypher-for-gremlin](https://github.com/opencypher/cypher-for-gremlin) 转译器将 Cypher 语句翻译为 Gremlin,再在 TinkerPop 技术栈上执行。因此 HugeGraph 的 Cypher +能力覆盖转译器所实现的 openCypher 9 语法面 —— 详见下文[已知限制](#已知限制)。 + +### 何时用 Cypher、何时用 Gremlin + +| 适合用 Cypher | 适合用 Gremlin | +|---|---| +| 正在从 Neo4j 迁移,或已熟悉 Cypher | 需要 HugeGraph 的完整功能面 | +| 需要声明式模式匹配(`MATCH (a)-[:KNOWS]->(b)`) | 需要精细的遍历控制、自定义步骤或 lambda | +| 查询以读模式和投影为主 | 需要图算法、OLAP 或 Cypher 未暴露的功能 | + +凡是 Cypher 表达不了的,等价的 Gremlin 查询总是可用 —— 下文示例同时给出两种写法。 + +### 调用 Cypher API + +Cypher 语句通过 HugeGraph Server 的 REST 端点提交: + +```http +GET /graphspaces/{graphspace}/graphs/{graph}/cypher?cypher={statement} +POST /graphspaces/{graphspace}/graphs/{graph}/cypher +``` + +该端点始终要求 `Authorization` 头(`Basic` 或 `Bearer`),即使服务端未开启认证 —— 凭据会被转发给 Gremlin Server。`Basic` 凭据必须使用 URL 安全的 Base64 编码(用 `-` 和 `_` 代替 `+` 和 `/`),且密码不能包含 `:`; +无法满足这两个条件的账号请改用 `Bearer`。完整的请求/响应格式见 [Cypher REST API 参考](/cn/docs/clients/restful-api/cypher/)。 + +示例: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + "http://localhost:8080/graphspaces/DEFAULT/graphs/hugegraph/cypher?cypher=MATCH%20(n:person)%20RETURN%20n.name%20LIMIT%2010" +``` + +Java 客户端使用 `CypherManager`(见 [hugegraph-client](/cn/docs/clients/hugegraph-client/)): + +```java +ResultSet resultSet = hugeClient.cypher().execute("MATCH (n:person) RETURN n.name LIMIT 10"); +``` + +### 基础示例 + +以下示例假设图中存在 `person` 顶点(`name`、`age`、`city` 属性)和 `knows` 边,并假设标签 +声明了 `person` 上的 `nullableKeys("age", "city")` 以及 `knows` 上的可空属性,并且 `person.age` 上有 `RANGE` 索引——例如在 +`scripts/example.groovy` 的 Schema 上补充这些可空键,再加上 +`schema.indexLabel("personByAge").onV("person").by("age").range().ifNotExist().create()`。`WHERE` 中的属性谓词必须有索引覆盖: +`scripts/example.groovy` 只为 `person.city` 以及 `(age, city)` 建了 `SECONDARY` 索引,因此在 `RANGE` 索引存在之前, +`n.age > 30` 这类范围比较会被拒绝并报“未建索引”错误。 +若先加载 `scripts/example.groovy`,请注意它已经预置了 `person` 顶点 `marko`、`vadas`、`josh` 和 `peter`; +由于 `person` 标签使用 `PRIMARY_KEY` id 策略,用这些名字执行 `CREATE` 会替换预置顶点, +因此下面的创建、更新和删除示例使用示例数据中不存在的名字(`alice`、`bob`、`carol`),或请在空图上执行。若 Schema 未声明可空键(已发布的默认 Schema 把 +`person` 的 `age`、`city` 标记为非空),下面的创建与更新示例会触发非空属性校验失败:请在每条语句中 +为所有非空属性赋值,或先以 `nullableKeys` 声明标签。若 `person` 标签使用 `PRIMARY_KEY` id 策略(`scripts/example.groovy` 即如此),双顶点简写 +`CREATE (a)-[:knows]->(b)` 会被拒绝并返回执行错误(提示必须设置主键;在已合入 +[#3289](https://github.com/apache/hugegraph/pull/3289) 的 `master` 上验证),因此下面的边示例先分别创建顶点再匹配它们; +该简写在 `AUTOMATIC` id 策略下可用。 + +#### 查询 — MATCH / WHERE / RETURN + +```cypher +// 所有名为 "marko" 的 person +MATCH (n:person) WHERE n.name = 'marko' RETURN n +``` + +```cypher +// 投影 + 排序 + 限制条数 +MATCH (n:person) +WHERE n.age > 30 +RETURN n.name AS name, n.age AS age +ORDER BY age DESC +LIMIT 10 +``` + +```cypher +// 关系模式 +MATCH (a:person)-[:knows]->(b:person) +WHERE a.name = 'marko' +RETURN b.name AS friend +``` + +#### 创建 — CREATE + +```cypher +// 创建顶点 +CREATE (n:person {name: 'alice', age: 32, city: 'beijing'}) +``` + +```cypher +// 先创建两个顶点,再创建它们之间的边(每条语句单独作为一次请求发送) +CREATE (:person {name: 'bob'}) +CREATE (:person {name: 'carol'}) +MATCH (a:person), (b:person) +WHERE a.name = 'bob' AND b.name = 'carol' +CREATE (a)-[:knows]->(b) +``` + +#### 更新 — SET / REMOVE + +```cypher +MATCH (n:person) WHERE n.name = 'alice' +SET n.age = 33 +REMOVE n.city +``` + +#### 删除 — DELETE / DETACH DELETE + +```cypher +// 删除边 +MATCH (a:person)-[r:knows]->(b:person) +WHERE a.name = 'bob' AND b.name = 'carol' +DELETE r +``` + +```cypher +// 删除顶点及其所有边 +MATCH (n:person) WHERE n.name = 'alice' +DETACH DELETE n +``` + +#### 聚合 + +```cypher +// 总数 +MATCH (n:person) RETURN count(n) AS total +``` + +```cypher +// 按城市分组计数 +MATCH (n:person) RETURN n.city AS city, count(*) AS cnt ORDER BY cnt DESC +``` + +### 等价的 Gremlin 写法 + +每条 Cypher 语句在内部都会被翻译为 Gremlin。当某个 Cypher 特性不受支持时,可直接编写遍历: + +| Cypher | 等价 Gremlin | +|---|---| +| `MATCH (n:person) WHERE n.name='marko' RETURN n` | `g.V().hasLabel('person').has('name','marko')` | +| `MATCH (a)-[:knows]->(b) RETURN b` | `g.V().out('knows')` | +| `CREATE (n:person {name:'x'})` | `g.addV('person').property('name','x')` | +| `MATCH (n) DETACH DELETE n` | `g.V().drop()` | +| `MATCH (n:person) RETURN count(n)` | `g.V().hasLabel('person').count()` | + +提示:`EXPLAIN MATCH (n:person) RETURN n` 会以 `EXPLAIN` 选项解析,翻译后的 Gremlin 预期出现在 `result.data[0].translation` 中 —— 这是转译层行为,尚未在服务器上端到端验证(见 +[兼容性说明](/cn/docs/language/cypher-compatibility/))。它仍是起草上表等价写法的最快方式。`PROFILE` 不受支持。 + +### 已知限制 + +HugeGraph 的 Cypher 能力受转译层约束,该层基于 openCypher 9 时代的工具链。已知缺口: + +- **已发布版本不支持参数化查询** —— API 只接受原始语句字符串。使用 `$param` 的语句不会报错:缺失的绑定会被内联为 `null`,结果取决于表达式:`WHERE n.name = $name` 这类谓词匹配不到行,`RETURN $value AS value` 仍会返回一行 `null`。 + 缺失绑定的写入并不能可靠地存入 null 值:视语句而定,`SET n.prop = $missing` 会移除已有属性,`CREATE (n:label {prop: $missing})` 会在文本属性中存入转译库的 null 标记字符串 `" cypher.null"`,而对非文本属性执行同样的 + `CREATE`,或对非空属性执行 `SET`,则会报错。请在客户端先对值做转义/净化再拼接到语句中。在合入 [#3289](https://github.com/apache/hugegraph/pull/3289) 后的 +测试栈上情况改变:缺失的绑定对所有请求形式都是执行错误,包括 `GET ?cypher=` 与原始文本 +`POST`——服务器总是附带参数表并拒绝未解析的 `$param` 引用。JSON 绑定参数(`{"cypher": ..., "parameters": {...}}`)已通过合入 `master` 的 +[ASF PR #3289](https://github.com/apache/hugegraph/pull/3289) 引入,承接原 [PR #238](https://github.com/hugegraph/hugegraph/pull/238);已验证的请求契约见 +[开发版兼容性记录](/cn/docs/language/cypher-compatibility/)。 +- **子句覆盖不完整** —— 部分 openCypher 结构无法翻译(例如 map projection、部分 `datetime()` 函数)。不支持的结构会在翻译阶段报错。`MERGE ... ON CREATE SET`、`NOT ... IN` 等结构可以翻译,但尚未在 HugeGraph 上端到端验证。 + 开发改动验证了计算字符串上的两个 `=~` 全字符串匹配用例,更广泛的正则覆盖仍未验证 —— 见[兼容性说明](/cn/docs/language/cypher-compatibility/)。 +- **不支持 `CALL` 过程** —— HugeGraph 的图算法(最短路径、k-out、personalrank 等)未暴露为 Cypher 过程;请改用 [traverser REST API](/cn/docs/clients/restful-api/traverser/) 或 Gremlin。 +- **每次请求仅一条语句** —— 不支持多语句脚本,请每次调用发送一条语句。 + +当语句翻译失败时,响应的 `status.message` 会包含转译器错误信息。遇到缺口时,等价的 Gremlin 写法是受支持的回退方案。 + +### 延伸阅读 + +- [Cypher REST API 参考](/cn/docs/clients/restful-api/cypher/) +- [Gremlin 查询语言](/cn/docs/language/hugegraph-gremlin/) +- [openCypher 项目](https://opencypher.org/) —— 语言规范与资源 + +转译库历史 TCK 通过比例不等于 HugeGraph 端到端兼容率;本页没有给出经测量的 HugeGraph Cypher 总体兼容百分比。 diff --git a/content/cn/docs/language/migrate-from-neo4j.md b/content/cn/docs/language/migrate-from-neo4j.md new file mode 100644 index 0000000000..4a25948f5f --- /dev/null +++ b/content/cn/docs/language/migrate-from-neo4j.md @@ -0,0 +1,88 @@ +--- +title: "从 Neo4j 迁移" +linkTitle: "从 Neo4j 迁移" +weight: 4 +--- + +### 本页面向谁 + +正在评估 HugeGraph 的 Neo4j 用户。HugeGraph 的 Cypher 覆盖面由 +[cypher-for-gremlin](https://github.com/opencypher/cypher-for-gremlin) 转译器决定, +与 Neo4j 并非 1:1 等价 —— 已发布版本的行为见 +[已知限制](/cn/docs/language/hugegraph-cypher/#已知限制),开发树验证记录见 +[Cypher 兼容性说明](/cn/docs/language/cypher-compatibility/), +用法见 [HugeGraph Cypher](/cn/docs/language/hugegraph-cypher/)。 + +### 可以直接带过来的部分 + +| Neo4j 概念 | HugeGraph 对应 | +|---|---| +| 属性图模型(节点/关系/属性) | 相同 —— 顶点标签 / 边标签 / 属性 | +| 基础 Cypher:`MATCH`/`WHERE`/`RETURN`/`CREATE`/`SET`/`DELETE` | 直接可用(REST `/cypher` 端点或 Java 客户端 `CypherManager`),但须遵守下文的 Schema 与索引规则 | +| 简单聚合(`count`/`sum`/`min`/`max`/`avg`) | 直接可用 | +| 依赖图遍历的查询需求 | Gremlin 遍历或 [traverser REST API](/cn/docs/clients/restful-api/traverser/)(k-out、最短路、personalrank 等) | + +### 需要改变习惯的部分 + +| 在 Neo4j 里 | 在 HugeGraph 里 | +|---|---| +| `CREATE INDEX` / `CREATE CONSTRAINT` | 通过 [IndexLabel REST API](/cn/docs/clients/restful-api/indexlabel/) 建索引/约束;Cypher 不承载 DDL | +| 隐式 schema(属性随意添加) | **强 schema**:先定义 VertexLabel/EdgeLabel/PropertyKey,再写数据 | +| `WHERE` 中的属性谓词无需索引 | `WHERE` 中针对属性的谓词必须有索引覆盖:请先创建 `SECONDARY` 或 `RANGE` 的 IndexLabel,`>`、`<` 等范围比较需要 `RANGE`。没有覆盖索引时,请求会返回 `status.code` 400 和“未建索引”错误 | +| 参数化查询 `$param` | 已发布版本仅发送原始语句 —— 缺失的绑定会被内联为 `null` 而不是报错(基于它的谓词匹配不到行,写入也不能可靠地存入 `null`:`SET` 会移除属性,`CREATE` 可能存入标记字符串 `" cypher.null"` 或报错),请在客户端校验取值;JSON 绑定参数已通过 [ASF PR #3289](https://github.com/apache/hugegraph/pull/3289) 合入 `master`,固定最多 16 个顶层绑定参数 | +| `CALL dbms.*` / `CALL algo.*` 过程 | 对应功能分散在 [traverser](/cn/docs/clients/restful-api/traverser/)、[task](/cn/docs/clients/restful-api/task/) 等 REST API | +| 驱动:Neo4j Bolt driver | [hugegraph-client](/cn/docs/clients/hugegraph-client/)(`CypherManager`)或任意 HTTP 客户端 | +| 事务边界在 Cypher 语句内 | 每次请求一个语句;图到图的数据迁移使用 [Apache SeaTunnel 3.0.0](https://seatunnel.apache.org/download/)(首选 —— 见下文),基于文件的批量导入使用 [HugeGraph Loader](/cn/docs/quickstart/toolchain/hugegraph-loader/) | + +截至 2026-10-09,最新已发布版本为 1.7.0;JSON 绑定请求已进入 `master`(1.8.0 开发线),不能据此认为 1.7.0 已支持。 + +### 建议的迁移路径 + +1. **盘点查询**:把应用里的 Cypher 按 + [已知限制](/cn/docs/language/hugegraph-cypher/#已知限制)(描述已发布版本行为)分成 + "直接可用 / 需改写为 Gremlin / 需换 REST API" 三类; + [兼容性说明](/cn/docs/language/cypher-compatibility/) 仅记录开发树上的验证结果。 +2. **先建 schema**:用 schema API 建好 VertexLabel/EdgeLabel/PropertyKey 和索引 —— + HugeGraph 在写入前要求 schema 存在。 +3. **导入数据**:首选路径是 [Apache SeaTunnel 3.0.0](https://seatunnel.apache.org/download/) + (2026-09-29 发布),直接运行 **Neo4j Source → HugeGraph Sink** —— 无需中间文件导出。 + 小数据集用 REST 也没问题;基于文件的大批量导入仍可使用 + [HugeGraph Loader](https://github.com/apache/hugegraph-toolchain/tree/master/hugegraph-loader)。 + HugeGraph 侧的环境准备、`mappings` 配置与顶点/边导入示例见 + [通过 SeaTunnel Sink 导入图数据](/cn/docs/quickstart/toolchain/import/hugegraph-seatunnel-connector/)。 + + [SeaTunnel 3.0.0 连接器文档](https://seatunnel.apache.org/docs/3.0.0/introduction/about/) 说明了可依赖的能力: + - [Neo4j Source](https://seatunnel.apache.org/docs/3.0.0/connectors/source/Neo4j/) 以显式字段 + schema 读取 Cypher 查询结果;3.0.0 新增通过 `tables_configs` 的多表读取。 + - [HugeGraph Sink](https://seatunnel.apache.org/docs/3.0.0/connectors/sink/HugeGraph/) 在 3.0.0 + 中重构,支持多映射 —— 顶点/边请使用推荐的 `mappings` 配置,并遵循其 schema 创建行为。 + - [HugeGraph Source](https://seatunnel.apache.org/docs/3.0.0/connectors/source/HugeGraph/) 为 + 3.0.0 新增(schema 自动发现、多标签/并行读取),使 HugeGraph 的导出与迁移工作流成为可能。 + + 将 Neo4j 属性映射为 HugeGraph PropertyKey,并在导入前先定好顶点 ID 策略。Sink 对除 `AUTOMATIC` + 之外的每种顶点策略都要求 `idFields`,边映射还需要分别设置 `sourceConfig.idFields` 与 + `targetConfig.idFields`,因此 Neo4j 查询必须返回承载 ID 的字段: + - `idStrategy = "PRIMARY_KEY"`:在 `idFields` 中放入 Neo4j 业务键,边的端点也通过这些键字段表达。 + - `idStrategy = "CUSTOMIZE_STRING"`:仅选择它并不能保留 Neo4j 节点 id。请让 Neo4j 查询把要保留的 id + 作为单独一列返回(用 `AS node_id` 等别名),在 Source 的 `schema.fields` 中声明该字段(必须与查询返回的字段一致), + 把顶点的 `idFields` 设为该字段,并在 `sourceConfig.idFields` 与 `targetConfig.idFields` 中映射对应的边端点字段。 + + 第 2 步预创建的标签必须使用同一策略——自动创建不会把已有的 `PRIMARY_KEY` 标签改为 `CUSTOMIZE_STRING`。 + Sink 的 `mappings` 配置可以创建缺失的 PropertyKey、VertexLabel 和 EdgeLabel 定义,因此使用它时第 2 步中的这三类 + 定义可省略;但它不会创建 IndexLabel,查询所依赖的索引仍须单独创建(见上文“需要改变习惯的部分”表)。 + 先导入顶点、后导入边。以上能力均有文档记载,但这里没有完整跑通 + 一次 Neo4j → HugeGraph 迁移 —— 在把示例当作已验证之前,请先用你自己的数据验证。 +4. **灰度切换**:Cypher 能表达的查询保持不动, translator 不支持的改写为 + Gremlin(等价写法见 [对照表](/cn/docs/language/hugegraph-cypher/#等价的-gremlin-写法))。 + +### 已知限制速查 + +- 已发布版本不支持参数绑定(缺失的绑定会被内联为 `null`,见上表)、 + 多语句脚本与 `CALL` 过程。 +- 转译器最后一次发布是 2019-11(1.0.4),其自身 TCK 报告为 + 879 通过 / 79 失败(约 91.8%)—— 未覆盖的语法会在翻译期报错。 +- 不受支持的查询结构通常可以改写为等价的 Gremlin 遍历 —— 见 + [HugeGraph Cypher 的对照表](/cn/docs/language/hugegraph-cypher/#等价的-gremlin-写法); + schema DDL 与 `CALL` 过程请改用 REST API。 + +转译库历史 TCK 通过比例不等于 HugeGraph 端到端兼容率;本页没有给出经测量的 HugeGraph Cypher 总体兼容百分比。 diff --git a/content/en/docs/language/_index.md b/content/en/docs/language/_index.md index 7362b4f706..c4f38a9572 100644 --- a/content/en/docs/language/_index.md +++ b/content/en/docs/language/_index.md @@ -4,4 +4,4 @@ linkTitle: "Query Languages" weight: 7 --- -HugeGraph supports Gremlin and Cypher. This section mainly covers Gremlin; see the [Cypher API](/docs/clients/restful-api/cypher/) for the Cypher HTTP interface. +HugeGraph supports Gremlin and Cypher. This section covers both languages; see the [Cypher API](/docs/clients/restful-api/cypher/) for the Cypher HTTP interface. diff --git a/content/en/docs/language/cypher-compatibility.md b/content/en/docs/language/cypher-compatibility.md new file mode 100644 index 0000000000..5d90a38a5e --- /dev/null +++ b/content/en/docs/language/cypher-compatibility.md @@ -0,0 +1,165 @@ +--- +title: "Cypher Compatibility" +linkTitle: "Cypher Compatibility" +weight: 3 +--- + +### Scope + +**The verification results below describe `master`, not a released HugeGraph version.** HugeGraph 1.7.0 exposes exactly two +request forms on `/graphspaces/{graphspace}/graphs/{graph}/cypher`: `GET ?cypher=` +and `POST application/json` with a raw Cypher body. Releases before 1.7.0 expose the same two forms under +`/graphs/{graph}/cypher`. Released versions accept no bound parameters, and a +statement that references `$param` runs with the missing binding inlined as `null` rather than failing (a predicate on it matches no rows, +while `RETURN $value AS value` still returns a row) — see +[Known limitations](/docs/language/hugegraph-cypher/#known-limitations). + +This page records the cases verified for the current Cypher development change. It does not claim full +openCypher or Neo4j compatibility, or compatibility across every released HugeGraph version. The tested +working tree is current Apache `master` at +[`5039e5b`](https://github.com/apache/hugegraph/commit/5039e5b67d26ea305b8509d3a414877749baaebb), +which declares HugeGraph `1.8.0`, Java 17, and TinkerPop `3.8.1`, with the Cypher change +([#3289](https://github.com/apache/hugegraph/pull/3289)) merged. The runtime uses Java `17.0.20.1` (Temurin), +TinkerPop `3.8.1`, `org.opencypher.gremlin:translation:1.0.4`, and RocksDB. The pre-merge verification on +[`af3c686`](https://github.com/apache/hugegraph/commit/af3c6867f4bfa3f95e63ad472c12a88c63529c1b) remains +referenced below. + +The endpoint is `/graphspaces/{graphspace}/graphs/{graph}/cypher`. General usage is covered in the +[HugeGraph Cypher guide](/docs/language/hugegraph-cypher/). + +### Request forms and parameters + +| Request | In a released version | Verified contract on the tested tree | Test method | +|---|---|---|---| +| `GET ?cypher=` | Yes | Existing query-string form | `testGet` | +| `POST application/json` with raw Cypher text | Yes | Legacy raw-body form remains available | `testPost` | +| `POST text/plain` with any body | No | Rejected with HTTP 415 | `testRejectPlainTextPost` | +| `POST application/json` with a JSON object | No — introduced by [#3289](https://github.com/apache/hugegraph/pull/3289) | Query and bindings are sent separately | `testParameters` | + +The JSON object form below is therefore **not available in any released HugeGraph version**; it describes the merged change on `master` (the 1.8.0 development +line). On released versions a statement using `$param` runs with the missing binding inlined as `null`; whether it returns rows +depends on the expression (`WHERE n.name = $name` matches nothing, `RETURN $value AS value` returns a row holding `null`) — +see [Known limitations](/docs/language/hugegraph-cypher/#known-limitations). + +```json +{ + "cypher": "MATCH (n:cypher_person) WHERE n.name = $name RETURN n.name", + "parameters": {"name": "marko"} +} +``` + +POST bodies containing JSON arrays, strings, numbers, booleans, or null are rejected with HTTP 400. +Malformed JSON objects and trailing tokens also produce request errors; they do not fall back to raw Cypher. + +For the JSON-object form, `cypher` must be a nonblank string and `parameters`, if present, must be an object. +Omitting `parameters` means an empty map. A referenced but missing binding is an execution error; an explicit +null value is valid. On the tested tree this execution error applies to every request form, including +`GET ?cypher=` and raw-body `POST`: [#3289](https://github.com/apache/hugegraph/pull/3289) always attaches the +parameters map and rejects unresolved `$param` references, replacing the released null-binding behaviour +described above. Tests cover string, number, and boolean values, quotes and newlines, the binding names +`id` and `label`. The current implementation accepts at most 16 top-level entries in `parameters`; this +limit is fixed. Only keys in the top-level `parameters` object count toward it. Cypher does not support a +`maxParameters` setting in Gremlin Server's processor configuration. Values remain bindings and do not +alter query text. + +A Map counts as one top-level parameter. Group related values into one Map and reference its fields +explicitly; the vertex label and required properties must match the graph's existing schema: + +```json +{ + "cypher": "CREATE (n:cypher_person {name:$props.name, age:$props.age, city:$props.city}) RETURN n.name", + "parameters": {"props": {"name": "new-person", "age": 20, "city": "Beijing"}} +} +``` + +Live REST verification confirmed that one Map containing 17 entries works with explicit property +references, with all 17 persisted values checked through native REST. A 17-entry returned Map also +succeeds, while 17 top-level bindings produce an execution error. Use the explicit property references +shown above; the pinned translator does not assign a bound Map's fields through the shorthand CREATE form. + +The translator reserves the exact string `" cypher.null"` (two leading spaces) as its null marker. That +string is rejected as a binding value, including inside lists and nested maps. Explicit null remains +supported. Literal query values and stored properties equal to that string remain a translator limitation; +this change does not establish value preservation for those cases. + +Check both the HTTP status and the response body's `status.code`: an execution failure can retain HTTP 200 +while returning `status.code` 400 and null result data. + +### Verified behavior + +The API fixture uses a strong schema, with a `SECONDARY` index on `city` and a `RANGE` index on `age`. +Acceptance results are limited to these test cases: + +| Area | Tested cases | Test methods | +|---|---|---| +| Reads | Label scans; equality and range predicates; boolean combinations; directed one- and two-hop patterns; empty results; whole-string regex matching on computed strings | `testGet`, `testExactReadsAndPredicates`, `testRelationQuery`, `testComputedRegexExecutesExtensionPredicate`, `testComputedRegexRequiresWholeStringMatch` | +| Results | Aliases, scalar and node values, nested values, relationship ids, path shape, null and missing properties | `testReturnNodeIdAsPrimitiveValue`, `testReturnNodeDoesNotLeakInternalIdTypes`, `testReturnNestedIdDoesNotLeakInternalIdTypes`, `testReturnRelationIdDoesNotLeakInternalIdTypes`, `testReturnPathShape`, `testNullAndMissingProperty` | +| Aggregation and pagination | `DISTINCT`, `count`/`sum`/`min`/`max`/`avg`, `ORDER BY`, `SKIP`, and `LIMIT` | `testDuplicatesDistinctAndPagination`, `testAggregates` | +| Writes | Vertex and edge `CREATE`, property `SET`, edge and vertex `DELETE`; state checked through native REST reads | `testCreate`, `testCreateSetAndDeleteWithNativeReadback` | +| Failed write | A rejected two-vertex `CREATE` left no residue during 32 subsequent query and native-read checks | `testFailedWriteDoesNotLeakIntoLaterRequests` | +| Errors and routing | Invalid syntax and request shape, content type, binding keys and the reserved null marker, schema values, authentication, and graph routing | `testInvalidRequests`, `testRejectPlainTextPost`, `testRejectInvalidQuery`, `testRejectInvalidBindingShapeAndKeys`, `testRejectTranslatorNullMarker`, `testRejectTranslatorNullSentinelInBindings`, `testAuthenticationAndGraphRouting`, `testSpecifiedGraphRouting` | + +An additional post-merge REST run on `5039e5b` re-executed the documented guide examples against a fresh RocksDB graph: +reads, projection with ordering and limit, aggregation, single-vertex `CREATE`, `SET`/`REMOVE`, edge `DELETE`, +`DETACH DELETE`, and every request-form contract above held, including the missing-binding execution error on all +three forms. The two-vertex shorthand `CREATE (a)-[:knows]->(b)` was verified in the same run on a graph whose +`person` label uses the `AUTOMATIC` id strategy; on a `PRIMARY_KEY` label the same statement is rejected with an +execution error reporting that the primary keys must be set, while single-vertex `CREATE` on that label succeeds. +On `PRIMARY_KEY` schemas, issue the two `CREATE` statements separately or use the `AUTOMATIC` id strategy for this form. + +Two guide examples depend on the schema. The range-predicate read (`WHERE n.age > 30`) needs the `RANGE` index on `age` +declared in the fixture schema above; the `scripts/example.groovy` schema has only `SECONDARY` indexes on `person` +(`city`, and `age` with `city`), so a range comparison on `age` there is rejected with a "not indexed" error until a +`RANGE` index exists. The create, update and delete examples were renamed afterwards to `alice`, `bob` and `carol` because +`scripts/example.groovy` already seeds `marko`, `vadas`, `josh` and `peter`, and on a `PRIMARY_KEY` label a `CREATE` with a seeded +name replaces that vertex; the rename changes only data values and was not run as a separate verification. + +The standalone code change is in [ASF PR #3289](https://github.com/apache/hugegraph/pull/3289), +which was merged into Apache `master` on 2026-10-08 as +[`5039e5b`](https://github.com/apache/hugegraph/commit/5039e5b67d26ea305b8509d3a414877749baaebb); +the verification record above describes that merged tree directly. The pre-merge baseline verified before the +merge is +[`af3c686`](https://github.com/apache/hugegraph/commit/af3c6867f4bfa3f95e63ad472c12a88c63529c1b). +The original development +change remains linked in [PR #238](https://github.com/hugegraph/hugegraph/pull/238). At source commit +[`8d06ad3`](https://github.com/apache/hugegraph/blob/8d06ad3b18fbd18807cf545184d07e6fec0c55b4/docs/cypher-compatibility.md), `CypherApiTest` passed 23/23, +`CypherClientTest` 7/7, and `CypherOpProcessorTest` 8/8, with no skips. The additional predicate, +execution-context, and request-ownership regressions passed, as did all nine `AbstractRestClientTest` +cases, including ASCII and UTF-16 map bodies with gzip. Login tests passed 3/3; related Gremlin tests +passed 10 cases with one existing, inapplicable `testClearAndInit` skip for a non-shared backend. +EditorConfig formatting, the repository-root clean compile, and the full install passed. The packaged +API JAR matched the compiled target JAR before the live test run. + +The compatibility note at that fixed commit contains the verification record and method-level mapping. +Test sources: +[`CypherApiTest`](https://github.com/apache/hugegraph/blob/8d06ad3b18fbd18807cf545184d07e6fec0c55b4/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/CypherApiTest.java), +[`CypherClientTest`](https://github.com/apache/hugegraph/blob/8d06ad3b18fbd18807cf545184d07e6fec0c55b4/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/cypher/CypherClientTest.java), +and +[`CypherOpProcessorTest`](https://github.com/apache/hugegraph/blob/8d06ad3b18fbd18807cf545184d07e6fec0c55b4/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessorTest.java). +The earlier verification on +[`e62c961`](https://github.com/apache/hugegraph/commit/e62c961e00221569d4f955abbadf60faee45b283) +remains at +[`fd8f4a6`](https://github.com/apache/hugegraph/blob/fd8f4a626b9e899663d3114035e2967e421b3e55/docs/cypher-compatibility.md). +The earlier, pre-rebase verification remains at +[`c3b2f3e`](https://github.com/hugegraph/hugegraph/blob/c3b2f3e3b9ff1de0495260d6eec0b16816d7f095/docs/cypher-compatibility.md). + +### Baseline failure and current fix + +At the earlier baseline +[`27a7c9b`](https://github.com/apache/hugegraph/commit/27a7c9b42274d6d4f95eabed6d2051d393ae0eaf), +a failed two-vertex `CREATE` left delayed residue in two observations. +In the first run, an immediate native read missed the prefix, but later aggregate/sort reads exposed it and +a full native listing confirmed it. In the second reproduction, immediate native reads and seven later +count/native-read checks showed no prefix; it appeared after the eighth count. The original baseline evidence remains in +[the baseline report](https://github.com/apache/hugegraph-doc/pull/499#issuecomment-5826172378). + +The current development change passes `testFailedWriteDoesNotLeakIntoLaterRequests`: native reads and 32 +later query/native-read checks confirm that this fixture leaves no residue. This verifies the reported +failure case on the tested stack; it does not establish every rollback path, cross-request transaction +semantics, or full write atomicity for all statements and backends. + +### Not verified by this change + +Advanced constructs and behaviors outside the test cases above remain unverified. This includes `MERGE`, +`OPTIONAL MATCH`, `WITH`, `UNWIND`, `UNION`, variable-length paths, broad function coverage, procedures, +Cypher schema DDL, `EXPLAIN`/`PROFILE`, HStore, Bolt, cross-request transactions, and performance. diff --git a/content/en/docs/language/hugegraph-cypher.md b/content/en/docs/language/hugegraph-cypher.md new file mode 100644 index 0000000000..8bd5a245bf --- /dev/null +++ b/content/en/docs/language/hugegraph-cypher.md @@ -0,0 +1,195 @@ +--- +title: "HugeGraph Cypher" +linkTitle: "Cypher Query Language" +weight: 2 +--- + +### Overview + +HugeGraph supports [Cypher](https://opencypher.org/), the declarative graph query language originally created by Neo4j and standardized as +[openCypher](https://github.com/opencypher/openCypher). While Gremlin is an imperative traversal language, Cypher lets you describe the graph pattern you want +(`MATCH`, `WHERE`, `RETURN`) and lets the engine decide how to execute it. + +HugeGraph executes Cypher by translating the statement to Gremlin through the [cypher-for-gremlin](https://github.com/opencypher/cypher-for-gremlin) transpiler, +then running the resulting traversal on the TinkerPop stack. This means Cypher support in HugeGraph covers the openCypher 9 surface that the transpiler +implements — see [Known limitations](#known-limitations) below. + +### When to use Cypher vs Gremlin + +| Use Cypher when | Use Gremlin when | +|---|---| +| You are migrating from Neo4j or already know Cypher | You need the full HugeGraph feature surface | +| You want declarative pattern matching (`MATCH (a)-[:KNOWS]->(b)`) | You need fine-grained traversal control, custom steps, or lambdas | +| Your queries are read-mostly patterns and projections | You need graph algorithms, OLAP, or features Cypher does not expose | + +For anything Cypher cannot express, the equivalent Gremlin query always works — examples below show both. + +### Calling the Cypher API + +Cypher statements are submitted to the HugeGraph Server through the REST endpoint: + +```http +GET /graphspaces/{graphspace}/graphs/{graph}/cypher?cypher={statement} +POST /graphspaces/{graphspace}/graphs/{graph}/cypher +``` + +The endpoint always requires an `Authorization` header (`Basic` or `Bearer`), even when server authentication is disabled — the credentials are forwarded to the +Gremlin Server. `Basic` credentials must be encoded with URL-safe Base64 (`-` and `_` instead of `+` and `/`), and the password must not contain `:`; use +`Bearer` for accounts that cannot meet either condition. See the [Cypher REST API reference](/docs/clients/restful-api/cypher/) for the full request/response +format. + +Example: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + "http://localhost:8080/graphspaces/DEFAULT/graphs/hugegraph/cypher?cypher=MATCH%20(n:person)%20RETURN%20n.name%20LIMIT%2010" +``` + +From the Java client, use `CypherManager` (see [hugegraph-client](/docs/clients/hugegraph-client/)): + +```java +ResultSet resultSet = hugeClient.cypher().execute("MATCH (n:person) RETURN n.name LIMIT 10"); +``` + +### Basic examples + +The examples below assume a graph with `person` vertices (`name`, `age`, `city` properties) and `knows` edges. +They assume the labels declare `nullableKeys("age", "city")` on `person` and nullable properties on `knows`, and that +`person.age` has a `RANGE` index — for example the schema in `scripts/example.groovy` modified with those nullable keys +plus `schema.indexLabel("personByAge").onV("person").by("age").range().ifNotExist().create()`. A property predicate in +`WHERE` must be covered by an index: `scripts/example.groovy` indexes `person.city` and `(age, city)` with `SECONDARY` indexes only, +so a range comparison such as `n.age > 30` is rejected with a "not indexed" error until the `RANGE` index exists. +If you load `scripts/example.groovy` first, note that it already seeds the `person` vertices `marko`, `vadas`, `josh` and `peter`; +because the `person` label uses the `PRIMARY_KEY` id strategy, a `CREATE` with one of those names replaces the seeded vertex, +so the create, update and delete examples below use names that the sample data does not contain (`alice`, `bob`, `carol`), +or run them on an empty graph. On a schema without the nullable keys +(the released default marks `age` and `city` non-null on `person`), the create and update examples below fail +the non-null property check: set every non-null property in each statement, or declare the labels with +`nullableKeys` first. If the `person` label uses the `PRIMARY_KEY` id strategy (as it does in `scripts/example.groovy`), the two-vertex +shorthand `CREATE (a)-[:knows]->(b)` is rejected with an execution error reporting that the primary keys must be set +(verified on `master` with [#3289](https://github.com/apache/hugegraph/pull/3289) merged), so the edge example below creates the vertices +first and then matches them. The shorthand works with the `AUTOMATIC` id strategy. + +#### Read — MATCH / WHERE / RETURN + +```cypher +// All persons named "marko" +MATCH (n:person) WHERE n.name = 'marko' RETURN n +``` + +```cypher +// Projection with ordering and limit +MATCH (n:person) +WHERE n.age > 30 +RETURN n.name AS name, n.age AS age +ORDER BY age DESC +LIMIT 10 +``` + +```cypher +// Relationship pattern +MATCH (a:person)-[:knows]->(b:person) +WHERE a.name = 'marko' +RETURN b.name AS friend +``` + +#### Create — CREATE + +```cypher +// Create a vertex +CREATE (n:person {name: 'alice', age: 32, city: 'beijing'}) +``` + +```cypher +// Create two vertices, then an edge between them (send each statement as its own request) +CREATE (:person {name: 'bob'}) +CREATE (:person {name: 'carol'}) +MATCH (a:person), (b:person) +WHERE a.name = 'bob' AND b.name = 'carol' +CREATE (a)-[:knows]->(b) +``` + +#### Update — SET / REMOVE + +```cypher +MATCH (n:person) WHERE n.name = 'alice' +SET n.age = 33 +REMOVE n.city +``` + +#### Delete — DELETE / DETACH DELETE + +```cypher +// Delete an edge +MATCH (a:person)-[r:knows]->(b:person) +WHERE a.name = 'bob' AND b.name = 'carol' +DELETE r +``` + +```cypher +// Delete a vertex and all its edges +MATCH (n:person) WHERE n.name = 'alice' +DETACH DELETE n +``` + +#### Aggregation + +```cypher +// Total count +MATCH (n:person) RETURN count(n) AS total +``` + +```cypher +// Grouped count +MATCH (n:person) RETURN n.city AS city, count(*) AS cnt ORDER BY cnt DESC +``` + +### Gremlin equivalents + +Every Cypher statement is translated to Gremlin internally. When a Cypher feature is unsupported, write the traversal directly: + +| Cypher | Gremlin equivalent | +|---|---| +| `MATCH (n:person) WHERE n.name='marko' RETURN n` | `g.V().hasLabel('person').has('name','marko')` | +| `MATCH (a)-[:knows]->(b) RETURN b` | `g.V().out('knows')` | +| `CREATE (n:person {name:'x'})` | `g.addV('person').property('name','x')` | +| `MATCH (n) DETACH DELETE n` | `g.V().drop()` | +| `MATCH (n:person) RETURN count(n)` | `g.V().hasLabel('person').count()` | + +Tip: `EXPLAIN MATCH (n:person) RETURN n` is parsed with the `EXPLAIN` option, and the translated Gremlin is expected in `result.data[0].translation` — +translator-level behavior, not yet verified end-to-end on a server (see the [compatibility notes](/docs/language/cypher-compatibility/)). It remains the +quickest way to draft the equivalent traversal for the table above. `PROFILE` is not supported. + +### Known limitations + +Cypher support in HugeGraph is bounded by the transpiler layer, which is based on openCypher 9 era tooling. Known gaps: + +- **No parameterized queries in released versions** — the API accepts a raw statement string only. A statement using `$param` still runs: a missing binding is + inlined as `null` rather than failing, and the result depends on the expression: a predicate such as `WHERE n.name = $name` matches no rows, + `RETURN $value AS value` still returns a row holding `null`. A write with a missing binding does not reliably store a null value: depending on the statement, + `SET n.prop = $missing` removes the existing property, `CREATE (n:label {prop: $missing})` stores the translator's null marker string `" cypher.null"` in a + text property, and the same `CREATE` on a non-text property, or `SET` on a non-nullable property, fails with an error. Sanitize/escape values on the client + side before interpolating them into the statement. On the tested tree with [#3289](https://github.com/apache/hugegraph/pull/3289) merged, this changes: a + missing binding is an execution error for every request form, including `GET ?cypher=` and raw-body `POST` — the server always attaches a parameters map and + rejects unresolved `$param` references. JSON-bound parameters (`{"cypher": ..., "parameters": {...}}`) are available on `master` after the merged + [ASF PR #3289](https://github.com/apache/hugegraph/pull/3289), carried over from [PR #238](https://github.com/hugegraph/hugegraph/pull/238); see the + [development compatibility record](/docs/language/cypher-compatibility/) for the tested request contract. +- **Partial clause coverage** — some openCypher constructs are not translated (for example map projections and certain `datetime()` functions). Unsupported + constructs fail at translation time with an error from the transpiler. Constructs such as `MERGE ... ON CREATE SET` and `NOT ... IN` do translate, but they + are not verified end-to-end on HugeGraph. The development change verifies two whole-string `=~` matching cases on computed strings; broader regex coverage + remains unverified — see the [compatibility notes](/docs/language/cypher-compatibility/). +- **No `CALL` procedures** — HugeGraph graph algorithms (shortest path, k-out, personalrank, etc.) are not exposed as Cypher procedures; use the + [traverser REST APIs](/docs/clients/restful-api/traverser/) or Gremlin instead. +- **Single statement per request** — multi-statement scripts are not supported; send one statement per call. + +When a statement fails translation, the response `status.message` contains the transpiler error. If you hit a gap, the Gremlin equivalent is the supported +fallback. + +### Further reading + +- [Cypher REST API reference](/docs/clients/restful-api/cypher/) +- [Gremlin Query Language](/docs/language/hugegraph-gremlin/) +- [openCypher project](https://opencypher.org/) — language specification and resources + +The translator’s historical TCK percentage is not a HugeGraph end-to-end compatibility rate; no measured overall HugeGraph Cypher percentage is established +here. diff --git a/content/en/docs/language/migrate-from-neo4j.md b/content/en/docs/language/migrate-from-neo4j.md new file mode 100644 index 0000000000..9a2378bc96 --- /dev/null +++ b/content/en/docs/language/migrate-from-neo4j.md @@ -0,0 +1,111 @@ +--- +title: "Migrating from Neo4j" +linkTitle: "Migrating from Neo4j" +weight: 4 +--- + +### Who this page is for + +Neo4j users evaluating HugeGraph. HugeGraph's Cypher coverage is defined by +the [`cypher-for-gremlin`](https://github.com/opencypher/cypher-for-gremlin) +translator and is not 1:1 with Neo4j — see +[Known limitations](/docs/language/hugegraph-cypher/#known-limitations) for what +released HugeGraph versions do, and the +[Cypher compatibility notes](/docs/language/cypher-compatibility/) for the +development-tree verification record and +[HugeGraph Cypher](/docs/language/hugegraph-cypher/) for usage. + +### What carries over directly + +| Neo4j concept | HugeGraph equivalent | +|---|---| +| Property graph model (nodes / relationships / properties) | Same — vertex labels / edge labels / properties | +| Core Cypher: `MATCH`/`WHERE`/`RETURN`/`CREATE`/`SET`/`DELETE` | Works as-is (REST `/cypher` endpoint or Java client `CypherManager`), subject to the schema and index rules below | +| Simple aggregations (`count`/`sum`/`min`/`max`/`avg`) | Works as-is | +| Traversal-heavy queries | Gremlin traversals or the [traverser REST APIs](/docs/clients/restful-api/traverser/) (k-out, shortest path, personalrank, ...) | + +### Habits you need to change + +| In Neo4j | In HugeGraph | +|---|---| +| `CREATE INDEX` / `CREATE CONSTRAINT` | Create indexes/constraints through the [IndexLabel REST API](/docs/clients/restful-api/indexlabel/); Cypher carries no DDL | +| Implicit schema (add properties freely) | **Strong schema**: define VertexLabel / EdgeLabel / PropertyKey before writing data | +| Property predicates in `WHERE` need no index | A `WHERE` predicate on a property must be covered by an index: create a `SECONDARY` or `RANGE` IndexLabel first, and use `RANGE` for comparisons such as `>` or `<`. Without a covering index the request returns `status.code` 400 with a "not indexed" error | +| Parameterized queries `$param` | Released versions send the raw statement only — a missing binding is inlined as `null` instead of failing (a predicate on it matches no rows, and a write does not reliably store `null`: `SET` removes the property, `CREATE` can store the marker string `" cypher.null"` or fail), so validate values client-side; JSON-bound parameters are available on `master` after [ASF PR #3289](https://github.com/apache/hugegraph/pull/3289), with a fixed limit of 16 top-level bindings | +| `CALL dbms.*` / `CALL algo.*` procedures | Equivalent capabilities live in the [traverser](/docs/clients/restful-api/traverser/), [task](/docs/clients/restful-api/task/), and other REST APIs | +| Driver: Neo4j Bolt driver | [hugegraph-client](/docs/clients/hugegraph-client/) (`CypherManager`) or any HTTP client | +| Transaction boundaries inside a Cypher statement | One statement per request; graph-to-graph data migration uses [Apache SeaTunnel 3.0.0](https://seatunnel.apache.org/download/) (preferred — see below), file-based bulk loads use the [HugeGraph Loader](/docs/quickstart/toolchain/hugegraph-loader/) | + +The latest published release is 1.7.0 as of 2026-10-09; the merged JSON-bound request form is a +`master` feature, not a promise about that release. See the compatibility notes for its validated contract. + +### Suggested migration path + +1. **Inventory your queries**: classify the Cypher in your application against + the [Known limitations](/docs/language/hugegraph-cypher/#known-limitations) + (which describe released behaviour) into + "works as-is / rewrite as Gremlin / switch to a REST API"; the + [compatibility notes](/docs/language/cypher-compatibility/) record only the + development-tree contract. +2. **Create the schema first**: define VertexLabel / EdgeLabel / PropertyKey + and indexes through the schema API — HugeGraph requires the schema to + exist before data is written. +3. **Import data**: the preferred path is + [Apache SeaTunnel 3.0.0](https://seatunnel.apache.org/download/) (released + 2026-09-29) running **Neo4j Source → HugeGraph Sink** directly — no + intermediate file export. REST remains fine for small datasets; the + [HugeGraph Loader](https://github.com/apache/hugegraph-toolchain/tree/master/hugegraph-loader) + stays an alternative for file-based imports. HugeGraph-side setup + (environment, `mappings`, vertex/edge import examples) is covered in the + [Import Graph Data with SeaTunnel Sink guide](/docs/quickstart/toolchain/import/hugegraph-seatunnel-connector/). + + The [SeaTunnel 3.0.0 connector docs](https://seatunnel.apache.org/docs/3.0.0/introduction/about/) document + what to rely on: + - [Neo4j Source](https://seatunnel.apache.org/docs/3.0.0/connectors/source/Neo4j/) reads Cypher + query results with explicit field schemas; 3.0.0 adds multi-table reads via `tables_configs`. + - [HugeGraph Sink](https://seatunnel.apache.org/docs/3.0.0/connectors/sink/HugeGraph/) is + refactored in 3.0.0 with multi-mapping support — use the recommended `mappings` configuration + for vertices/edges and follow its schema-creation behavior. + - [HugeGraph Source](https://seatunnel.apache.org/docs/3.0.0/connectors/source/HugeGraph/) is + new in 3.0.0 (schema auto-discovery, multi-label/parallel reads), enabling HugeGraph export + and migration workflows. + + Map Neo4j properties to HugeGraph PropertyKeys and pick the vertex ID strategy + before importing. The Sink requires `idFields` for every vertex strategy other than + `AUTOMATIC`, and edge mappings need separate `sourceConfig.idFields` and + `targetConfig.idFields`, so the Neo4j query must return the field that carries the ID: + - `idStrategy = "PRIMARY_KEY"`: put a Neo4j business key in `idFields` and express edge + endpoints through those same key fields. + - `idStrategy = "CUSTOMIZE_STRING"`: this alone does not keep the Neo4j node id. Make the + Neo4j query return the id you want to keep as its own column (aliased, for example `AS node_id`), + declare that field in the Source `schema.fields` (it must match the fields the query + returns), set the vertex `idFields` to it, and map the matching edge endpoint fields + in `sourceConfig.idFields` and `targetConfig.idFields`. + + Labels you pre-created in step 2 must use that same strategy — automatic creation does not + convert an existing `PRIMARY_KEY` label into `CUSTOMIZE_STRING`. The Sink `mappings` + configuration can create the missing PropertyKey, VertexLabel and EdgeLabel definitions, so + those three parts of step 2 are optional when you use it; it does not create IndexLabels, so + create any index your queries rely on (see the habits table above) separately. Load vertices + before edges. These capabilities are + documented, but a complete Neo4j → HugeGraph migration has not been run end-to-end here — + validate examples against your own data before treating them as tested. +4. **Switch incrementally**: keep the queries Cypher can express, and rewrite + translator-unsupported constructs as Gremlin — the + [equivalents table](/docs/language/hugegraph-cypher/#gremlin-equivalents) + shows the mapping. + +### Known limitations at a glance + +- Released versions support neither parameter binding (a missing binding + is inlined as `null` — see the habits table), multi-statement + scripts, nor `CALL` procedures. +- The translator's last release is 2019-11 (1.0.4); its own TCK self-report is + 879 pass / 79 fail (~91.8%) — uncovered syntax fails at translation time. +- Unsupported query constructs can typically be rewritten as Gremlin + traversals — see the + [equivalents table](/docs/language/hugegraph-cypher/#gremlin-equivalents); + schema DDL and `CALL` procedures map to REST APIs instead. + +The translator’s historical TCK percentage is not a HugeGraph end-to-end compatibility rate; no measured overall HugeGraph Cypher percentage is established +here. diff --git a/data/docs_nav.json b/data/docs_nav.json index d4b4125433..57694b7d59 100644 --- a/data/docs_nav.json +++ b/data/docs_nav.json @@ -218,6 +218,15 @@ "children": [ { "page": "/docs/language/hugegraph-gremlin" + }, + { + "page": "/docs/language/hugegraph-cypher" + }, + { + "page": "/docs/language/cypher-compatibility" + }, + { + "page": "/docs/language/migrate-from-neo4j" } ] }, @@ -674,6 +683,21 @@ "page": "/docs/language/hugegraph-gremlin", "url": "/docs/language/hugegraph-gremlin/", "children": [] + }, + { + "page": "/docs/language/hugegraph-cypher", + "url": "/docs/language/hugegraph-cypher/", + "children": [] + }, + { + "page": "/docs/language/cypher-compatibility", + "url": "/docs/language/cypher-compatibility/", + "children": [] + }, + { + "page": "/docs/language/migrate-from-neo4j", + "url": "/docs/language/migrate-from-neo4j/", + "children": [] } ] }, @@ -1335,6 +1359,21 @@ "/docs/cla/": [ "@group/reference", "/docs/cla/" + ], + "/docs/language/hugegraph-cypher/": [ + "@group/develop", + "/docs/language/", + "/docs/language/hugegraph-cypher/" + ], + "/docs/language/cypher-compatibility/": [ + "@group/develop", + "/docs/language/", + "/docs/language/cypher-compatibility/" + ], + "/docs/language/migrate-from-neo4j/": [ + "@group/develop", + "/docs/language/", + "/docs/language/migrate-from-neo4j/" ] }, "children_by_url": { @@ -1413,7 +1452,10 @@ "/docs/quickstart/client/hugegraph-client-go" ], "/docs/language/": [ - "/docs/language/hugegraph-gremlin" + "/docs/language/hugegraph-gremlin", + "/docs/language/hugegraph-cypher", + "/docs/language/cypher-compatibility", + "/docs/language/migrate-from-neo4j" ], "/docs/config/": [ "/docs/config/config-guide", diff --git a/data/version_routes.json b/data/version_routes.json index 7f6e062577..a010ac87d4 100644 --- a/data/version_routes.json +++ b/data/version_routes.json @@ -505,6 +505,20 @@ "1.3": "cn/docs/language/", "1.0": "cn/docs/language/" }, + "cn:language/cypher-compatibility": { + "latest": "cn/docs/language/cypher-compatibility/", + "1.7": null, + "1.5": null, + "1.3": null, + "1.0": null + }, + "cn:language/hugegraph-cypher": { + "latest": "cn/docs/language/hugegraph-cypher/", + "1.7": null, + "1.5": null, + "1.3": null, + "1.0": null + }, "cn:language/hugegraph-example": { "latest": "cn/docs/language/hugegraph-example/", "1.7": "cn/docs/language/hugegraph-example/", @@ -519,6 +533,13 @@ "1.3": "cn/docs/language/hugegraph-gremlin/", "1.0": "cn/docs/language/hugegraph-gremlin/" }, + "cn:language/migrate-from-neo4j": { + "latest": "cn/docs/language/migrate-from-neo4j/", + "1.7": null, + "1.5": null, + "1.3": null, + "1.0": null + }, "cn:performance": { "latest": "cn/docs/performance/", "1.7": "cn/docs/performance/", @@ -1219,6 +1240,20 @@ "1.3": "docs/language/", "1.0": "docs/language/" }, + "en:language/cypher-compatibility": { + "latest": "docs/language/cypher-compatibility/", + "1.7": null, + "1.5": null, + "1.3": null, + "1.0": null + }, + "en:language/hugegraph-cypher": { + "latest": "docs/language/hugegraph-cypher/", + "1.7": null, + "1.5": null, + "1.3": null, + "1.0": null + }, "en:language/hugegraph-example": { "latest": "docs/language/hugegraph-example/", "1.7": "docs/language/hugegraph-example/", @@ -1233,6 +1268,13 @@ "1.3": "docs/language/hugegraph-gremlin/", "1.0": "docs/language/hugegraph-gremlin/" }, + "en:language/migrate-from-neo4j": { + "latest": "docs/language/migrate-from-neo4j/", + "1.7": null, + "1.5": null, + "1.3": null, + "1.0": null + }, "en:performance": { "latest": "docs/performance/", "1.7": "docs/performance/", diff --git a/scripts/versioning.py b/scripts/versioning.py index 2e37d3a435..1d4f0aed2f 100644 --- a/scripts/versioning.py +++ b/scripts/versioning.py @@ -133,36 +133,36 @@ DOCS_NAV_EXPECTED_STATS = { "latest": { "groups": 5, - "pages": 92, + "pages": 95, "removed": 4, "scopedLinks": 0, - "treeSha256": "15de4de385857f877b46083de8e65d2887769d811687c4fe7ade3069401fc929", + "treeSha256": "fd869e8710b268dc59e7f7202cf8ce4bec4061cb7f80af9d9a135070deed72af", }, "1.7": { "groups": 5, "pages": 85, - "removed": 5, + "removed": 8, "scopedLinks": 10, "treeSha256": "c87538b82b0e3506eef59417411686ddc188e0f33d0e3a1cda687de8d6f88747", }, "1.5": { "groups": 5, "pages": 77, - "removed": 13, + "removed": 16, "scopedLinks": 10, "treeSha256": "ac29ab8f0e7020496a3a1afe480687043d751e32c93ffb848e2210e3500cd483", }, "1.3": { "groups": 5, "pages": 68, - "removed": 22, + "removed": 25, "scopedLinks": 10, "treeSha256": "e8c9b0a64dac6dd3e47827ec5072697eb7d3ed79659e0b5c4a9c5df8e1408d38", }, "1.0": { "groups": 5, "pages": 59, - "removed": 21, + "removed": 24, "scopedLinks": 10, "treeSha256": "bd45db511d9b6728e1523bd3c5ebd9d18912ee85aeabf79c77bcca7e511f977a", },