Neo4j Cypher语句
核心概念
-
节点(node):图中的实体,用圆括号表示,例如
(p:Person)
表示带有Person
标签的节点(Label)。节点可以有任意属性(properties),例如p.name
、p.age
。 -
关系(relationship):两个节点之间的有向/无向边,用方括号表示并放在箭头中,例如
-[:KNOWS]->
。关系也可以有属性。 -
标签(Label):给节点分类(相当于表/类型),例如
:Person
、:Movie
。在查询中使用标签可以让查询走索引,从而更快。 -
属性(Property):节点或关系上的键值对,如
name: "Alice"
、age: 30
。 -
模式(pattern):节点与关系的组合,用于匹配或创建图数据,如
(a:Person)-[:KNOWS]->(b:Person)
。
Cypher 语法骨架(常见子句与执行顺序)
常见子句(按逻辑执行顺序理解):
-
MATCH
:模式匹配(只读)。 -
WHERE
:过滤条件(用于 MATCH 或 WITH)。 -
OPTIONAL MATCH
:可选匹配(类似 SQL 的 LEFT JOIN)。 -
MERGE
:匹配或创建(类似MATCH
+CREATE
合并行为)。(Graph Database & Analytics) -
CREATE
:无条件创建节点/关系(每次执行都会创建新的实体)。 -
SET
:设置/更新属性或标签(包括+=
合并属性)。 -
DELETE
/DETACH DELETE
:删除节点/关系(DETACH
会先删除关系)。 -
WITH
:管道(把中间结果带到下一阶段),常用于分组/聚合/避免笛卡尔积。 -
RETURN
:查询结果输出(可聚合、排序、分页)。 -
UNWIND
:把列表“展开”为多行,常用于逐行处理 CSV 字段或数组。 -
LOAD CSV
:从 CSV 导入数据(可配合USING PERIODIC COMMIT
做批量导入)。 -
UNION
:合并多个查询的结果,并去重(即最终结果集中的行是唯一的)。 -
UNION ALL
:合并多个查询的结果,但不去重(保留重复行)。
1) MATCH / WHERE / RETURN —— 查询与模式匹配
示例数据想象:有 Person
节点和 KNOWS
关系。
MATCH (a:Person)-[:KNOWS]->(b:Person)
WHERE a.age > 30 AND b.city = 'Beijing'
RETURN a.name, collect(b.name) AS friends
ORDER BY a.name
解释:
-
MATCH (a:Person)-[:KNOWS]->(b:Person)
:匹配满足模式的所有路径,把左右节点绑定到变量a
、b
。 -
WHERE
放在MATCH
后做过滤;注意可以使用逻辑运算符AND/OR/NOT
,也支持比较函数(=, <, >, IN, IS NULL
等)。 -
RETURN
可以返回聚合(count()
、collect()
、avg()
等),也可以用DISTINCT
去重。
常见坑:
-
忘了给节点加标签会导致全图扫描(慢)。使用标签能利用索引。
-
MATCH
会返回多个匹配(重复路径),合并/聚合时要注意collect()
/DISTINCT
的使用。
2) CREATE 与 MERGE —— 创建实体(及 MERGE 的语义细节)
CREATE
:无条件新建。
CREATE (p:Person {name:'Alice', age:30})
MERGE
:如果匹配到就返回匹配结果;如果没匹配到就创建。它像 MATCH
+ CREATE
的组合。使用 MERGE
时要小心 匹配键 的设计 —— 通常只对唯一标识(如 id
、name
等)用 MERGE
,把非关键属性用 SET
/ON CREATE SET
/ON MATCH SET
来赋值。
示例:
MERGE (p:Person {email: row.email}) // 仅用唯一标识做 MERGE
ON CREATE SET p.name = row.name, p.createdAt = timestamp()
ON MATCH SET p.lastSeen = timestamp()
解释:
-
ON CREATE SET
仅在新建时执行,ON MATCH SET
仅在匹配到现有节点时执行。 -
如果对一个复杂模式使用
MERGE
(例如MERGE (a)-[r:KNOWS]->(b)
),注意它会尝试匹配整个模式,不匹配时可能同时创建a
、b
和r
,这常常不是你想要的 —— 通常先MERGE
节点,再MERGE
关系。
常见坑:
-
MERGE
的匹配键必须精心设计,避免条件不全导致重复节点(例如把很多可变属性放进 MERGE 的 property map 会导致重复)。
3) 更新属性:SET / += / REMOVE
-
全量设置:
SET n = {name:'A', age:20}
(会替换节点的属性集)。 -
增量合并属性:
SET n += {age:21}
(只更新/新增给定键)。 -
新增标签:
SET n:Customer
。 -
删除属性或标签:
REMOVE n.age
或REMOVE n:OldLabel
。
示例:
MATCH (p:Person {name:'Alice'})
SET p.age = toInteger(row.age), p += {active:true}
4) 删除:DELETE / DETACH DELETE
-
DELETE r
:删除关系r
。 -
DELETE n
:只能删除没有任何关系的节点,否则会报错。 -
DETACH DELETE n
:先删除节点的所有关系,再删除节点(批量清理时常用)。
MATCH (p:Person {name:'Bob'}) DETACH DELETE p
5) 路径、可变长度匹配、方向
-
固定关系:
-[:KNOWS]->
-
可变长度:
-[:KNOWS*1..3]->
(长度 1 到 3);*..
表示不限制上界。 -
方向:
->
(有向)、<-
(反向)、-[]-
(无向/方向忽略)。 -
shortestPath((a)-[*]-(b))
可以求最短路径,但要注意性能;对大图慎用。
6) WITH:管道与中间聚合
WITH
常用于分段处理、聚合后再继续匹配,或者把上下文限制在少量记录,避免笛卡尔积。例如:
MATCH (p:Person)-[:KNOWS]->(f:Person)
WHERE p.age > 30
WITH p, collect(f) AS friends
WHERE size(friends) >= 3
RETURN p.name, friends
WITH
同时是控制执行顺序、分组聚合的地方;如果用错位置会导致意料外的重复或笛卡尔积。
7) UNWIND:把列表拆成多行(常用于 CSV 或 JSON 导入)
UNWIND ['a','b','c'] AS tag
MERGE (t:Tag {name:tag})
常见用法:UNWIND split(row.tags, ';') AS tag
,把 CSV 的分号分隔标签展开后逐条处理。
8) LOAD CSV(CSV 导入)与批量导入注意事项
基础:
LOAD CSV WITH HEADERS FROM 'file:///medicines.csv' AS row
MERGE (m:Medicinal {name: row.name})
SET m.property = row.property
-
如果 CSV 很大,使用
USING PERIODIC COMMIT <n>
可以让导入分批提交以减少内存峰值(适用于LOAD CSV
)。 -
LOAD CSV
支持本地文件与远程 URL(HTTP/HTTPS/FTP),但远程导入有安全与性能考虑。 -
对数值字段要做类型转换:
toInteger()
、toFloat()
等,避免把数值当作字符串存储。 -
对大量数据,考虑使用 Neo4j 提供的
neo4j-admin import
(离线批量导入)或使用 APOC 的批处理工具来提高效率。
9) 约束(Constraint)与索引(Index)
-
对频繁用于
MATCH
的属性建立索引/约束,可以显著提升性能(例如节点的唯一键、主键类字段)。 -
在 Neo4j v5+ 的新语法中,推荐使用
CREATE CONSTRAINT ... FOR (n:Label) REQUIRE n.prop IS UNIQUE
的形式,并建议为约束命名。注意 Neo4j 的 DDL 语法在不同版本有差异(ASSERT
→REQUIRE
等变化),具体请参考官方文档。
示例:
CREATE CONSTRAINT person_email_unique FOR (p:Person) REQUIRE p.email IS UNIQUE;
(重要:不同 Neo4j 版本的语法略有差异,生产环境中请根据你安装的 Neo4j 版本对照官方文档执行。)
10) 查询分析:EXPLAIN / PROFILE / 以及性能调优
-
EXPLAIN <query>
:只返回执行计划(不执行)。 -
PROFILE <query>
:运行查询并返回带有实际运行统计信息的执行计划。用PROFILE
可以看到是否走索引、是否出现CartesianProduct
、各步骤的实际耗时和记录数,从而定位瓶颈。
调优要点:
-
优先让关键
MATCH
使用标签 + 索引查找(避免全表扫描)。 -
避免不必要的
MATCH
或把太多变量带入WITH
(这会增加中间数据量)。 -
使用
EXPLAIN
/PROFILE
来找出Eager
或CartesianProduct
节点,再重写查询(比如先WITH
限定小集再匹配)。 -
批量数据导入时使用分批(
USING PERIODIC COMMIT
或 APOC 批处理)来减小单次事务的内存占用。
11) APOC:扩展函数与批处理(常见实用工具)
APOC(Awesome Procedures On Cypher)是 Neo4j 社区非常常用的扩展库,提供大量实用的存储过程/函数,比如批量操作、导入导出、文本处理、图算法接口等。批量导入常用 apoc.periodic.iterate
(把一个查询的结果作为流,分批交给另一个写入语句处理),在实际大量数据迁移时非常方便。
示例(apoc 批处理伪例):
CALL apoc.periodic.iterate("LOAD CSV WITH HEADERS FROM 'file:///big.csv' AS row RETURN row","MERGE (m:Thing {id:row.id}) SET m += row",{batchSize:1000, parallel:false}
)
12) 常见错误与排查技巧(Checklist)
-
导入 CSV 时:注意编码、分隔符、空行与表头是否一致,类型转换(
toInteger()
)是否需要。 -
MERGE
使用不当会产生重复节点 —— 把MERGE
限定到唯一标识字段,其他属性用SET
。 -
出现
CartesianProduct
:说明某个或多个MATCH
没有关联条件,导致笛卡尔积,需使用WITH
或补充匹配条件。 -
执行慢:先
EXPLAIN
/PROFILE
,看是否走索引或出现Eager
操作(某些聚合/UNWIND等会造成)。 -
删除节点失败:若直接
DELETE node
报错,用DETACH DELETE
。