Neo4j Cypher 命令速查表 - Neo4j 图数据库常用命令大全

Neo4j 相对关系型数据库的核心优势不是"查询更快"这么笼统,而是关系本身可以当查询对象:一个人两跳之内能到达谁、两个节点之间的最短依赖链是什么,这类问题在关系库里要做两三张连接表的自连接,而图库里只是一个带类型的路径模式。这张表按创建节点与关系、查询与匹配、WITH 与子查询、更新与删除、聚合与排序、索引与约束六组铺开,写法从 `CREATE (a)-[:KNOWS]->(b)` 到可变长度 `[:KNOWS*1..3]`、shortestPath、UNWIND、子查询、percentileCont 分位数聚合。什么时候该翻它:写查询前先想清"关系是否有类型"——有类型就该用带类型的关系模式而非无差别匹配,路径上要限定跳数上限避免遍历爆炸。生产环境还有一条硬规矩:唯一约束要在写数据之前建好,否则 MERGE 并不能防止历史重复节点的存在。

数据库·共 40 条命令·最后更新 2026-07-21
neo4j图数据库cypher

典型使用场景

面向用关系建模解决「表里连不出来的查询」的工程师:好友推荐、知识图谱、权限继承、组织层级、风控团伙识别。这类问题的共同特征是需要任意深度的路径遍历,而这正是关系数据库需要递归 CTE、图数据库一行就能表达的地方。本表覆盖节点与关系的创建(CREATE 与 MERGE 的选择)、模式匹配(变长路径 *2、*1..3、shortestPath)、WITH 分段与子查询(UNWIND / CALL {} / UNION)、更新与删除(SET +=、REMOVE、DELETE 与 DETACH DELETE 的区别)、聚合排序(collect、percentileCont、分页),以及索引与约束。必须先掌握的一条规则:没有索引时节点查找会全标签扫描,而 CREATE 与 MERGE 的选择会直接决定后续查询的性能。

创建节点与关系 5

CREATE (n:Person {name: "Alice"})
创建节点
CREATE (a:Person)-[:KNOWS]->(b:Person)
创建关系
CREATE (a:Person {name: "Alice"})-[:KNOWS {since: 2020}]->(b:Person {name: "Bob"})
创建带属性的节点和关系
MERGE (n:Person {name: "Alice"})
存在则匹配,不存在则创建
MERGE (a)-[:KNOWS]->(b) ON CREATE SET a.created = timestamp()
MERGE 带回调

查询与匹配 9

MATCH (n:Person) RETURN n
查询所有 Person 节点
MATCH (n:Person {name: "Alice"}) RETURN n
按属性查询
MATCH (n:Person) WHERE n.age > 18 RETURN n
WHERE 条件过滤
MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a, b
查询关系
MATCH (a:Person)-[:KNOWS*2]->(b:Person) RETURN a, b
查询 2 跳关系
MATCH (a:Person)-[:KNOWS*1..3]->(b:Person) RETURN a, b
查询 1-3 跳关系
MATCH (n) OPTIONAL MATCH (n)-[:KNOWS]->(m) RETURN n, m
可选匹配(无关系返回 null)
MATCH path = (a)-[*]->(b) RETURN path
查询路径
MATCH path = shortestPath((a)-[:KNOWS*]->(b)) RETURN path
最短路径

WITH 与子查询 5

MATCH (n:Person) WITH n WHERE n.age > 18 RETURN n.name
WITH 过滤传递
MATCH (n:Person)-[:KNOWS]->(m) WITH n, count(m) AS friends RETURN n.name, friends
WITH 聚合传递
UNWIND [1, 2, 3] AS x RETURN x
展开数组为行
CALL { MATCH (n:Person) RETURN n ORDER BY n.name LIMIT 5 } RETURN count(*)
CALL 子查询
MATCH (n:Person) RETURN n.name AS name UNION MATCH (n:Company) RETURN n.name AS name
UNION 合并结果

更新与删除 6

MATCH (n:Person {name: "Alice"}) SET n.age = 30
更新属性
MATCH (n:Person {name: "Alice"}) SET n += {age: 30, city: "Beijing"}
批量更新
MATCH (n:Person {name: "Alice"}) REMOVE n.age
删除属性
MATCH (n:Person {name: "Alice"}) DELETE n
删除节点(必须无关系)
MATCH (n:Person {name: "Alice"}) DETACH DELETE n
删除节点及其所有关系
MATCH (a)-[r:KNOWS]->(b) DELETE r
删除关系

聚合与排序 9

MATCH (n:Person) RETURN count(n)
计数
MATCH (n:Person) RETURN DISTINCT n.city
去重
MATCH (n:Person) RETURN n.name, n.age ORDER BY n.age DESC
排序
MATCH (n:Person) RETURN n.age, count(*) ORDER BY n.age
分组计数
MATCH (n:Person) RETURN collect(n.name)
收集为数组
MATCH (n:Person) RETURN avg(n.age), min(n.age), max(n.age)
聚合函数
MATCH (n:Person) RETURN percentileCont(n.age, 0.5)
中位数(连续百分位)
MATCH (n:Person) RETURN n LIMIT 10
限制返回数量
MATCH (n:Person) RETURN n SKIP 5 LIMIT 10
分页

索引与约束 6

CREATE INDEX ON :Person(name)
创建索引
CREATE CONSTRAINT ON (n:Person) ASSERT n.name IS UNIQUE
唯一约束
CREATE CONSTRAINT ON (n:Person) ASSERT exists(n.name)
存在约束
DROP INDEX ON :Person(name)
删除索引
DROP CONSTRAINT ON (n:Person) ASSERT n.name IS UNIQUE
删除约束
CALL db.indexes()
列出所有索引

参数矩阵

参数作用示例
CREATE (n:Person {name: "Alice"})无条件创建节点;重复执行会产生重复节点,是 MERGE 的常见反面教材CREATE (n:Person {name: "Alice"})
CREATE (a:Person)-[:KNOWS]->(b:Person)创建带关系结构的模式;未写属性的关系会以 null 收尾CREATE (a:Person)-[:KNOWS]->(b:Person)
CREATE (a)-[:KNOWS {since: 2020}]->(b)在关系上挂属性;关系的属性是给边加权的关键手段CREATE (a:Person {name: "Alice"})-[:KNOWS {since: 2020}]->(b:Person {name: "Bob"})
MERGE (n:Person {name: "Alice"})存在则匹配、不存在则创建,写入脚本的默认选择,天然幂等MERGE (n:Person {name: "Alice"})
MERGE (a)-[:KNOWS]->(b) ON CREATE SET a.created = timestamp()只在真正创建时写属性,避免每次运行都刷新时间戳MERGE (a)-[:KNOWS]->(b) ON CREATE SET a.created = timestamp()
MATCH (n:Person) RETURN n按标签匹配全量节点;没有索引时是全标签扫描MATCH (n:Person) RETURN n
MATCH (n:Person {name: "Alice"})带属性匹配;为该属性建索引后才能真正走索引查找MATCH (n:Person {name: "Alice"}) RETURN n
MATCH (a)-[:KNOWS*2]->(b)定长变长路径,恰好两跳;这是最短关系链查询的标准写法MATCH (a:Person)-[:KNOWS*2]->(b:Person) RETURN a, b
MATCH (a)-[:KNOWS*1..3]->(b)深度区间遍历;范围越界上限越高代价越大,必须配合上限使用MATCH (a:Person)-[:KNOWS*1..3]->(b:Person) RETURN a, b
MATCH path = (a)-[*]->(b) RETURN path任意类型与深度遍历并返回完整路径;不加限制时在稠密图上代价极高MATCH path = (a)-[*]->(b) RETURN path
shortestPath((a)-[:KNOWS*]->(b))最短路径,遍历会在找到第一条最短路径后停止,比取全部路径便宜MATCH path = shortestPath((a)-[:KNOWS*]->(b)) RETURN path
OPTIONAL MATCH找不到匹配时返回 null 而非丢弃整行,做「有则展示、无则留空」的关键MATCH (n) OPTIONAL MATCH (n)-[:KNOWS]->(m) RETURN n, m
WITH n WHERE n.age > 18 RETURN n.name在管道中插入过滤与投影,用于先收敛再展开,是控制中间结果量的关键MATCH (n:Person) WITH n WHERE n.age > 18 RETURN n.name
WITH n, count(m) AS friends RETURN n.name, friends聚合后按 n 分组统计,是「每个人的好友数」这类查询的写法MATCH (n:Person)-[:KNOWS]->(m) WITH n, count(m) AS friends RETURN n.name, friends
UNWIND [1, 2, 3] AS x RETURN x把列表展开成多行,常用于构造测试数据或展开数组属性UNWIND [1, 2, 3] AS x RETURN x
CALL { MATCH ... RETURN ... } RETURN count(*)子查询,把一段匹配封装为独立单元后在外层聚合CALL { MATCH (n:Person) RETURN n ORDER BY n.name LIMIT 5 } RETURN count(*)
RETURN ... UNION MATCH ...合并同构结果集;两侧列名必须一致,否则查询报错MATCH (n:Person) RETURN n.name AS name UNION MATCH (n:Company) RETURN n.name AS name
SET n += {age: 30, city: "Beijing"}合并写入属性,不会清掉未列出的已有属性(对比 SET n = {...})MATCH (n:Person {name: "Alice"}) SET n += {age: 30, city: "Beijing"}
REMOVE n.age删除单个属性;删掉的是属性不是节点MATCH (n:Person {name: "Alice"}) REMOVE n.age
DELETE n vs DETACH DELETE nDELETE 只删节点;节点上还有关系时会报错,需要 DETACH DELETE 连带删除所有关系MATCH (n:Person {name: "Alice"}) DETACH DELETE n
DELETE r删除关系本身;只删边时用它比删节点安全MATCH (a)-[r:KNOWS]->(b) DELETE r
RETURN DISTINCT n.city去重;图查询里 DISTINCT 会随数据量增长明显变慢,能提前聚合更好MATCH (n:Person) RETURN DISTINCT n.city
RETURN collect(n.name)把多行聚成一个列表,是「查某人全部好友名」的标准聚合MATCH (n:Person) RETURN collect(n.name)
percentileCont(n.age, 0.5)百分位数(中位数用 0.5);cont 表示连续插值口径,另有离散口径可选MATCH (n:Person) RETURN percentileCont(n.age, 0.5)
RETURN n SKIP 5 LIMIT 10分页;SKIP 在深度分页时依然会线性扫描,生产上应优先用 seek 式分页MATCH (n:Person) RETURN n SKIP 5 LIMIT 10
CREATE INDEX ON :Person(name)建属性索引,让等值匹配走索引而不是全标签扫描CREATE INDEX ON :Person(name)
CREATE CONSTRAINT ... IS UNIQUE唯一约束既保证数据正确性也让 MERGE 能走索引;MERGE 高频字段建议加CREATE CONSTRAINT ON (n:Person) ASSERT n.name IS UNIQUE
CREATE CONSTRAINT ... ASSERT exists(n.name)存在性约束,保证属性非空,避免下游查询遇到 nullCREATE CONSTRAINT ON (n:Person) ASSERT exists(n.name)
DROP INDEX / DROP CONSTRAINT删除索引或约束;删索引会让查询退化为全扫描,需评估线上影响DROP INDEX ON :Person(name)
CALL db.indexes()列出当前所有索引与约束,是确认「索引到底建上没有」的直接手段CALL db.indexes()

易错点与避坑指南

现象脚本重复跑之后出现大量同名重复节点。

原因用 CREATE 而不是 MERGE;CREATE 无条件插入,每次执行都新增。

处置改用 MERGE 并为匹配字段建唯一约束;先用 MATCH 查重再决定是否写。

现象删除节点时报 "Node cannot be deleted because it still has relationships"。

原因DELETE 只删节点本身,节点上挂着关系时会被拒绝。

处置改用 DETACH DELETE;或先单独 DELETE 掉相关关系再删节点(需要保留其它关系时)。

现象按 name 查询越来越慢,PROFILE 显示走了全标签扫描。

原因等值匹配的属性上没有索引,或索引刚建完还没生效。

处置CREATE INDEX ON :Person(name) 并加唯一约束;用 EXPLAIN / PROFILE 确认走的是 NodeIndexSeek。

现象变长路径查询把内存打满或直接超时。

原因深度上限没设或设得过大,稠密图上的路径数量呈指数增长。

处置给深度设明确上限并用 shortestPath;必要时先按属性或标签剪枝再遍历。

现象路径少了一条,OPTIONAL MATCH 返回的 m 是 null。

原因关系方向写反了:(:A)-[:KNOWS]->(:B) 与 (:A)<-[:KNOWS]-(:B) 语义不同。

处置先统计单向关系数量确认方向,再统一箭头的写法。

现象WITH 之后原本想继续匹配的节点消失了。

原因WITH 起到过滤作用:不在 WITH 里保留的变量后续不可见;DISTINCT 语义也会改变行数。

处置在 WITH 中显式写出需要继续传递的变量,并在必要时加 count 做聚合后再展开。

现象UNION 报错,提示结果列名或数量不一致。

原因UNION 要求两侧返回相同数量与类型的列,且按位置对齐而非名字。

处置两侧用别名统一成同一批列名,并核对类型一致(数字与字符串不能混)。

现象数据导入跑几小时后磁盘暴涨,节点数却没明显增加。

原因MERGE 在无索引字段上执行会退化为全扫描,并留下大量事务日志等待落盘。

处置导入前先建好索引与约束;大批量导入时用 APOC 的分批能力并控制事务大小。

排障路径

  1. 1确认索引与约束是否真的存在

    CALL db.indexes()

    列表里没有目标索引,就是全扫描的直接原因。

  2. 2核对标签与关系类型是否拼写一致

    grep -rn "CREATE \(:\|MERGE (:" data

    大小写与下划线不一致是「明明建了却查不到」的常见原因。

  3. 3看查询实际走了什么计划

    PROFILE MATCH (n:Person {name: "Alice"}) RETURN n

    关注 operators 里的 NodeIndexSeek 与 TotalDbHits。

  4. 4先统计节点规模再决定优化方向

    MATCH (n:Person) RETURN count(n)

    数据量决定是否需要索引、分页与深度限制。

  5. 5检查写入脚本是否用了 MERGE

    grep -rn "CREATE (\|MERGE (" load

    重复导入的根因几乎都在这里。

  6. 6确认删除是否会牵连关系

    grep -rn "DELETE" data

    需要连带删除关系的场景应显式写成 DETACH DELETE。

命令示例

MERGE 做幂等写入

MERGE (a:Person {name: "Alice"}) MERGE (a)-[:KNOWS {since: 2020}]->(b:Person {name: "Bob"})

CREATE 每跑一次就多一份数据,重跑脚本会污染图库;MERGE 先按整个模式匹配,匹配到就复用,匹配不到才创建,所以脚本可以反复执行。MERGE 只匹配整个模式:给它两个条件属性时,两个都必须相等才算匹配上,漏写条件会退化成按单个属性匹配。

输出

Added 1 node, set 2 properties, created 0 relationships

用 WITH 先收窄,再做第二段匹配

MATCH (n:Person) WITH n WHERE n.age > 18 RETURN n.name MATCH (a:Person)-[:KNOWS]->(b:Person) WITH a, count(b) AS friends RETURN a.name, friends

两段 MATCH 连写时,第二个 MATCH 会对第一段结果的每一行都执行一次;中间加 WITH 就能先过滤和聚合,把基数压下来再匹配。图上遍历开销随行数线性增长,缺少这一步的写法在大图上就是分钟级与秒级的差别。

输出

friends
30

删除节点前先断开关系

MATCH (n:Person {name: "Alice"}) DELETE n MATCH (n:Person {name: "Alice"}) DETACH DELETE n

有关系的节点用 DELETE 会被拒绝并回滚整个事务,DETACH DELETE 连带删掉它所有关系。对于被很多节点引用的中心节点,DETACH DELETE 会在图上造成大面积断链——先用 MATCH (n)-[r]->(m) RETURN count(*) 评估影响面再决定。

输出

Cannot delete node<4>, because it still has relationships. (ConstraintViolation)

按标签分别计数,别全库扫

MATCH (n:Person) RETURN count(n) MATCH (n:Person) RETURN n.age, count(*) ORDER BY n.age CREATE INDEX ON :Person(name) CALL db.indexes()

MATCH (n) 会遍历所有节点做类型判定,图有几百万节点时是秒级的浪费;带上标签让 Neo4j 走标签扫描,配合 CREATE INDEX 后属性过滤走索引。改完用 EXPLAIN 看执行计划里是 NodeByLabelScan + NodeIndexSeek 还是 AllNodesScan。

限定跳数,避免全图最短路径

MATCH (a:Person)-[:KNOWS*2]->(b:Person) RETURN a, b MATCH (a:Person)-[:KNOWS*1..3]->(b:Person) RETURN a, b MATCH path = shortestPath((a)-[:KNOWS*]->(b)) RETURN path

-[:KNOWS*] 不写上界时是可变长关系,展开量随图的连通性指数级膨胀,几十万人节点上直接把内存打满。日常查询写清 *2 或 *1..3;只有确实要最短路径时才用 shortestPath,并把两端用具体节点(如 {name: "Alice"})锁死,避免从全集起算。

常见坑与注意事项

  • MATCH (a)-[r]-(b) 两端不带任何属性或标签限制,就是一次全库扫描,图越大越慢。看起来只差几个字,性能上却从索引查找变成全图遍历。加标签和属性条件,或者用 PROFILE 看是否走了索引。
  • 可变长关系 [:KNOWS*] 不写跳数上界是最容易拖垮数据库的写法:展开的行数会随路径数量爆炸,聚合查询还会把结果全收进内存。必须写 *1..3 这样的上界,或用 shortestPath 明确意图。
  • MATCH (n) 返回 n 会把整个节点连同全部属性返回给客户端,几百万节点时结果集本身就是内存问题。只加索引不够,要在 RETURN 里只取需要的属性,或者加 LIMIT / SKIP。
  • MATCH (a:Person)-[:KNOWS]->(b:Person) 和 MATCH (a:Person)-[:KNOWS*1..2]->(b:Person) 的结果差一倍但看着很像,统计「好友数」「二度人脉」时上界差一位就会得出完全错误的结论。写变长关系时用注释明确跳数含义。
  • 忘记给 WHERE 里的属性建索引:CREATE INDEX ON :Person(name) 之后,name 上等值过滤走索引;不建索引则是逐节点比较。数据量小时看不出差别,等线上量级上来,同样的查询时间可能从毫秒变分钟。
  • DETACH DELETE 会在节点已被别处引用时静默造成断链,DELETE 又会因为仍有关联而整事务回滚。批量清理前先 MATCH (n:Person {name: "Alice"})-[r]-() RETURN count(r) 看清关系数量;DELETE 失败后不要反复重试同一个查询,先决定是断链还是迁移引用。

提示

  • MATCH 查询前先用 EXPLAIN 查看执行计划,避免全图扫描。
  • MERGE 比 CREATE 安全,避免重复创建节点。
  • 长路径查询(*1..10)可能很慢,建议加 LIMIT 或用图算法。
  • `shortestPath((a)-[:KNOWS*]->(b))` 只返回最短一条,拓扑分析中比返回全部路径省一个数量级。
  • 删节点前必须 `DETACH DELETE`,否则残留关系会让普通 DELETE 直接报错。
  • `MATCH (n) OPTIONAL MATCH (n)-[:KNOWS]->(m) RETURN n, m` 保留无关系节点,避免统计时丢人。

常见问题

CREATE 和 MERGE 到底该用哪个,重复导入会不会产生脏数据?

会,所以默认用 MERGE。CREATE (n:Person {name: "Alice"}) 无条件新建,跑第二遍就多一个节点;MERGE (n:Person {name: "Alice"}) 先按标签和属性匹配,匹配上就复用,匹配不上才创建。节点上的属性最好配合唯一约束兜底:CREATE CONSTRAINT ON (n:Person) ASSERT n.name IS UNIQUE,必要时再加 exists(n.name) 的存在约束。只在批量造测试数据、确定不会重跑时才用 CREATE。MERGE 还能带回调:MERGE (a)-[:KNOWS]->(b) ON CREATE SET a.created = timestamp() 只在真正新建时写属性。

DELETE 节点报错说还挂着关系,怎么处理?

因为默认不允许删掉仍连着关系的节点。按情况选:MATCH (a)-[r:KNOWS]->(b) DELETE r 先单独删关系;MATCH (n:Person {name: "Alice"}) DETACH DELETE n 一条语句连同所有关系一起删,适合清理测试数据;属性级的修改则用 SET / REMOVE 而不是 DELETE,比如 SET n += {age: 30, city: "Beijing"} 批量更新、REMOVE n.age 删属性。大批量删除前务必先用 MATCH ... RETURN 确认命中的行数,避免手滑删多。

WITH 在查询里到底起什么作用,能替代子查询吗?

WITH 是管道式的中间结果传递,可以过滤也可以聚合,常用于分步收窄。MATCH (n:Person) WITH n WHERE n.age > 18 RETURN n.name 先过滤再输出;MATCH (n:Person)-[:KNOWS]->(m) WITH n, count(m) AS friends RETURN n.name, friends 先按人聚合计数再返回。带 LIMIT 的子查询则用 CALL:CALL { MATCH (n:Person) RETURN n ORDER BY n.name LIMIT 5 } RETURN count(*)。此外 UNWIND [1, 2, 3] AS x RETURN x 能把数组展开成行,UNION 则合并两段同名结果的查询。

要查朋友的朋友、祖孙关系这类多跳路径,语法怎么写才不会查崩?

变长关系用星号加跳数。固定两跳:(a:Person)-[:KNOWS*2]->(b:Person);区间跳:(a:Person)-[:KNOWS*1..3]->(b:Person)。多级自连接可以直接连三次:MATCH (t1.name AS grandchild...) 那段祖孙三代写法是 tree t1 JOIN... 这种链式自连接,能表达确定层级。路径本身用 MATCH path = (a)-[*]->(b) RETURN path 取回,最短路用 MATCH path = shortestPath((a)-[:KNOWS*]->(b)) RETURN path。但 *1..10 这类无约束长路径可能极慢,建议先 EXPLAIN 看执行计划、避免全图扫描,并加上 LIMIT 收窄结果。

查询越来越慢,先做哪些优化?

按性价比排序。第一,先建索引:CREATE INDEX ON :Person(name) 让属性查找走索引,用 CALL db.indexes() 确认索引存在,需要时 DROP INDEX ON :Person(name) 清理。第二,上查询前先 EXPLAIN 看执行计划,确认没有全图扫描。第三,约束同样提升查找性能,例如 CREATE CONSTRAINT ON (n:Person) ASSERT n.name IS UNIQUE。第四,用 LIMIT 收窄,RETURN n LIMIT 10 或 RETURN n SKIP 5 LIMIT 10 分页。第五,能用 WITH 提前过滤就过滤,别把整张图拉出来再筛。

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。

由 巧匠 维护

公开更新于 2026年7月21日,内容持续校对官方文档。

联系我们

命令或描述有误?提交反馈、商务合作或产品建议都可发送邮件给我们。

联系我们