办公文档
编写、优化和调试 Neo4j Cypher 25…
在 Neo4j 2025.x/2026.x 上生成、优化、校验 Cypher 25 查询(MATCH/MERGE/CREATE/WITH/CALL/UNWIND/SEARCH、子查询、批量写入、LOAD CSV)时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
编写、优化和调试 Neo4j Cyphe特定
解决思路
- 每个顶层查询首 token 写 `CYPHER 25`;不要在 `UNION` 之后或子查询内部重复。
- 先做 schema 检查再写查询:若项目中有 `<db-name>-schema.json` 直接读取并注明文件名与 `schema_retrieved_at`,跳过实时检查;若 schema 已在上下文中直接使用;若缺失则运行 `CALL db.schema.visualization()`、`SHOW INDEXES`、`SHOW CONSTRAINTS`、`SHOW PROCEDURES` 获取。
- 属性类型优先用 `CALL apoc.meta.schema()`;无 APOC 且库 ≤ 10 万节点/关系时才用 `CALL db.schema.nodeTypeProperties()` 与 `CALL db.schema.relTypeProperties()`。
- 返回查询前校验:标签存在、关系类型与方向正确、属性属于该标签、索引状态为 ONLINE。
- schema 未知且无工具时,在代码块外给出不可执行的草图 `(<SOURCE_LABEL> {<KEY>: $value})-[:<REL_TYPE>]->(<TARGET_LABEL>)`,绝不填入猜测的真实名称。
- `MERGE` 只用在有约束的键上;关系 `MERGE` 只用于已绑定的端点;额外属性写在 `ON CREATE SET` / `ON MATCH SET` 中。
- 禁止无标签的 `MATCH (n)`,除非已绑定或后接 `WHERE n:$($label)`。
- 所有探索性读取默认加 `LIMIT 25`;在高基数操作(变长遍历、扇出 MATCH、笛卡尔积)前用 `WITH n LIMIT` 提前截断。
- 注释只用 `//`,`--` 是 SQL 语法无效。
- `REPEATABLE ELEMENTS` / `DIFFERENT RELATIONSHIPS` 写在 `MATCH` 之后,不要放在模式末尾。
- `SHOW` 命令先 `YIELD` 再 `WHERE`,可与通用 Cypher 子句(含 `UNION`/`RETURN`)组合;`SHOW DATABASES` 仍需系统库(用 `USE system`);在 `system` 库上 `CALL` 后先 `YIELD` 再 `WHERE`。
- 内联节点谓词 `(:Label WHERE p=x)` 仅在 `MATCH` 中有效。
- `WHERE` 不能直接跟在裸 `UNWIND` 后,改用 `WITH x WHERE`。
- `(a)-[:R]-(b)` 无向匹配会双向重复计数,除非方向未知否则使用有向。
- 删除有关系的节点用 `DETACH DELETE`,普通 `DELETE` 会报错。
- 命名风格:节点标签 PascalCase `:Person`,关系类型 SCREAMING_SNAKE_CASE `:KNOWS`,属性/变量 camelCase `firstName`,子句大写 `MATCH`,布尔/null 小写,字符串用单引号(含 `'` 时用双引号)。
- `WITH` 会丢弃未列出的变量,需要保留时显式列出或用 `WITH *`。
- 子查询速查:`EXISTS { (a)-[:R]->(b) }` 布尔检查;`COUNT { ... WHERE ... }` 计数;`COLLECT { MATCH ... RETURN b.name }` 收集列表(必须完整 MATCH+RETURN);`CALL (p) { ... }` 相关子查询显式导入;`OPTIONAL CALL (p) { ... }` 可空子查询;`CALL { WITH x ... }` 已废弃,改用 `CALL (x) { ... }`。
- 批量写入用 `CALL (row) { ... } IN TRANSACTIONS OF 1000 ROWS ON ERROR CONTINUE REPORT STATUS AS s`,输入流必须在子查询外,且只能自动提交,不要包在 `beginTransaction()` 中;`PERIODIC COMMIT` 已废弃。
- 导入关系时用 `IN CONCURRENT TRANSACTIONS OF 1000 ROWS DISJOINT BY (line.movieId, line.personId)` 避免死锁;`DISJOINT BY AUTO` 自动推断锁键,`DISJOINT BY NONE` 关闭;可用 `EXPLAIN`/`PROFILE` 在 `TransactionForeach` 上查看 `DISJOINT BY (...)`。
- QPE 写法:`MATCH SHORTEST 1 (a:Person {name:'Alice'})(()-[:KNOWS]->()){1,}(b:Person {name:'Bob'})`;量词写在组外 `(pattern){N,M}`;组以节点开始和结束;`REPEATABLE ELEMENTS` 需要有限量词 `{m,n}`。
- 需要路径内节点不重复时用 `ACYCLIC`(如 `MATCH p = ACYCLIC (start:Router {name: $from})-[:LINK]-+(end:Router {name: $to})`),它比默认 `DIFFERENT RELATIONSHIPS` 更强。
- 条件写入用 `CALL (move, insertBefore, insertAfter) { WHEN ... THEN { ... } WHEN ... THEN { ... } ELSE { ... } }`,互斥且首个匹配生效(需 2025.06+)。
- 动态关系类型用 `[:$($relType)]` 创建/匹配/合并,表达式必须解析为单个 STRING。
- 字符串插值用 `s"Hello, {p.name}, age {p.age}"`(`S"..."` 与 `s'...'` 等价),每个 `{expr}` 用 `toString()` 转换,`MAP`/`LIST`/`NODE`/`PATH`/`RELATIONSHIP` 会被拒绝,字面大括号用 `\{` `\}` 转义;绝不把不可信值插值进传给 `apoc.cypher.run*()` 的 Cypher 文本,改用 `$parameters`。
- UUID 类型(2026.08+):`CREATE (sess:Session {sessionId: uuid()})`,用 `uuid($uuidString)` 把 8-4-4-4-12 字符串转 UUID,用 `toString()` / `uuid.mostSignificantBits()` 读取;旧驱动可能返回占位 MAP 并警告 `03N95`,否则继续用 `randomUUID()` 字符串 id。
- 空间点:写入 `SET n.coords = point({longitude: $lon, latitude: $lat})`;距离用 `point.distance(a.coords, b.coords)`(需 POINT 索引);先用 `point.withinBBox(...)` 做边界框预过滤再算距离;建索引 `CREATE POINT INDEX name IF NOT EXISTS FOR (n:Place) ON (n.coords)`。
- 聚合分组:`RETURN`/`WITH` 中非聚合表达式是隐式分组键;也可用显式 `GROUP BY actor, genre`(2026.07+);`GROUP BY ()` 表示无分组键(单行),`GROUP BY ALL` 表示所有非聚合返回项都是键。
- `count(n)` 只计非 null,`count(*)` 计所有行;`collect(DISTINCT expr)` 去重;`count()` 比 `size(collect())` 快。
- `ORDER BY`/`WHERE` 中引用比变量或 `var.prop` 更复杂的投影项已废弃(2026.07),应在投影中起别名再按别名排序。
- 避免常见语法陷阱:`ORDER BY n.prop AS x DESC` → `ORDER BY n.prop DESC`;`count(r WHERE r.x=5)` → `sum(CASE WHEN r.x=5 THEN 1 ELSE 0 END)`;`least/greatest` → `CASE WHEN`;`shortestPath(...)` → `SHORTEST 1 (...)`;`id(n)` → `elementId(n)`;`[:REL*1..5]` → `(()-[:REL]->()){1,5}`;`SET n = {k:v}` 部分更新 → `SET n += {k:v}`;`WHERE n.x = null` → `WHERE n.x IS NULL`;`toInteger(null)` → `toIntegerOrNull(null)`;`n.$key` → `n[$key]`;`SET n:$label` → `SET n:$($label)`;`FOREACH ... RETURN` → `UNWIND ... RETURN`。
- 日期时间:`ZONED DATETIME >= date(...)` 会返回 0 行,改用 `datetime(...)` 或 `.year`;ISO 字符串的 `Z` 后缀在 Neo4j 中不是 UTC 时区而是偏移量,需显式转换 `datetime({datetime: datetime('2025-09-10T03:43:00Z'), timezone: 'UTC'})`。
- 默认输出参数化查询,返回具名属性而非整个节点或 `RETURN *`(schema/诊断查询如 `CALL db.schema.visualization()`、`SHOW INDEXES YIELD *`、`EXPLAIN` 例外)。
- 校验流程:任何写入前先 `EXPLAIN`;新读取先用 `LIMIT 1` 测试;写入前先把读部分作为 `RETURN` 验证再替换为 `SET`/`CREATE`/`DELETE`;用 `PROFILE` 测量 db hits 并检查 `AllNodesScan`、`CartesianProduct`、`Eager`。
- 无驱动时可用 Query API v2:`curl -X POST https://<instance>.databases.neo4j.io/db/<database>/query/v2 -u <user>:<password> -H "Content-Type: application/json" -d '{"statement": "EXPLAIN MATCH (n:Person {name: $name}) RETURN n", "parameters": {"name": "Alice"}}'`,本地用 `http://localhost:7474/db/<database>/query/v2`,响应为 `{"data": {"fields": [...], "values": [...]}}`。
- 当 agent 实际执行写入(MCP/cypher-shell/HTTP)而非生成代码时:先 `EXPLAIN` 报告预估影响行数,等用户确认后再执行。
- 版本未知时默认使用 2025.01 安全特性;按版本门控表回退:`SEARCH` 子句(2026.01)→ `CALL db.index.vector.queryNodes(...)`;`ACYCLIC`(2026.03)→ 用 `size(nodes(p)) = size(apoc.coll.toSet(nodes(p)))` 后置过滤;`string.indexOf()` 等(2026.05)→ `apoc.text.*`;`GROUP BY` 子句与 `cardinality()`(2026.07)→ 隐式分组与 `size()`;字符串插值(2026.08)→ `+` 与 `toString()`;`UUID` 类型(2026.08)→ `randomUUID()` 字符串;map 推导式(2026.09)→ `apoc.map.fromPairs(...)`。
- 性能红牌 `AllNodesScan`/`CartesianProduct`/`NodeByLabelScan`/`Eager` 的修复:给 MATCH 节点加具体标签消除读写歧义;先 `WITH collect(u) AS users UNWIND users AS u ...` 再写;或用 `CALL IN TRANSACTIONS` 隔离批次。
- 多标签查询选择性被低估时用标签推断:`CYPHER inferSchemaParts = most_selective_label MATCH ...`。
- 每个 MATCH/MERGE/WHERE 的查找属性都要有索引,且索引只在节点有标签时生效(`MATCH (n {prop: $v})` 不会用索引);`CONTAINS`/`ENDS WITH` 用 TEXT 索引;EXPLAIN 显示扫描时用 `USING INDEX n:Label(prop)` 强制计划;嵌套数据的链式 `OPTIONAL MATCH` 改用 `COLLECT { MATCH ... RETURN }`;动态标签 `$($label)` 会导致 `AllNodesScan`+Filter,尽量用静态标签。
- 失败恢复:0 结果时检查参数类型、逐个移除 WHERE、EXPLAIN 看索引;类型错误用 `toIntegerOrNull()`/`toFloatOrNull()` 并加 `IS NOT NULL` 守卫;变量越界说明未列入 `WITH`;超时先修 AllNodesScan 再加早 `LIMIT` 再用 `CALL IN TRANSACTIONS OF 1000 ROWS`;长查询进度用 `SHOW TRANSACTIONS YIELD currentQuery, status, currentQueryProgress`;Duration 用 `.days`/`.months` 而非 `.inDays`/`.inMonths`;`Cannot merge node using null property value` 说明 MERGE 键为 null,先校验参数;`IndexNotFoundError` 用 `SHOW INDEXES YIELD name, state WHERE state <> 'ONLINE'`。
- 按需加载参考文档:`references/indexes.md`(索引类型、约束、MERGE 锁语义、全文 Lucene 语法)、`references/cypher-syntax.md`(完整语法)、`references/syntax-traps.md`(40+ 陷阱)、`references/performance.md`(反模式、Eager 修复、批处理)、`references/advanced-patterns.md`(REPEATABLE ELEMENTS、allReduce、多段 QPE)、`references/apoc.md`(APOC Core)、`references/graph-type.md`(GRAPH TYPE DDL)。
- 需要权威语义时 WebFetch:子句 `https://neo4j.com/docs/cypher-manual/25/clauses/{clause}/`、函数 `https://neo4j.com/docs/cypher-manual/25/functions/{type}/`、模式 `https://neo4j.com/docs/cypher-manual/25/patterns/`、空间函数 `https://neo4j.com/docs/cypher-manual/25/functions/spatial/`、索引约束 `https://neo4j.com/docs/cypher-manual/25/indexes/`、速查表 `https://neo4j.com/docs/cypher-cheat-sheet/25/all/`。
- 交付前过一遍检查清单:schema 已检查、每个顶层查询有 `CYPHER 25`、使用 `$parameters`、探索性读取有 `LIMIT`、已 `EXPLAIN` 且红牌已解决、写入前读部分已验证、agent 执行时已过写入门、`MERGE` 只在约束键上、无无标签 `MATCH (n)`、schema 操作不在显式事务内。
适用边界 · 注意事项
- 驱动迁移或 API 变更场景,改用 `neo4j-migration-skill`。
- 数据库管理(用户、配置、备份)或服务器运维,改用 `neo4j-cli-tools-skill`。
- 把向量检索与全文或其他排序来源混合的混合检索,改用 `neo4j-vector-index-skill`。