Neo4j Cypher 语法大全
一、核心概念
Neo4j 图数据库由三种核心实体构成:节点(Nodes)、关系(Relationships) 和 路径(Paths)。
| 实体 | 表示方式 | 特点 |
|---|---|---|
| 节点 | (n:Label {prop: value}) |
可有多个标签,属性以键值对存储 |
| 关系 | -[r:TYPE {prop: value}]-> |
必须有方向、起点、终点,且只能有一个类型 |
| 路径 | p = (a)-[:R]->(b) |
由节点和关系交替连接而成 |
Cypher 语法具有“可视化”特征:()-[]->() 的形状本身就描绘了要匹配的图模式。
二、读取与匹配
MATCH
指定要在数据库中搜索的图模式。
// 匹配特定标签和属性的节点
MATCH (n:Person {name: 'Alice'})
RETURN n
// 匹配关系
MATCH (a:Person)-[:KNOWS]->(b:Person)
RETURN a.name, b.name
// 在关系上添加过滤条件(Cypher 25)
MATCH (:Person {name: 'Anna'})-[r:KNOWS WHERE r.since < 2020]->(friend:Person)
RETURN count(r) AS numberOfFriends
OPTIONAL MATCH
与 MATCH 类似,但如果模式部分缺失,会用 null 填充而不是过滤掉整行。
WHERE
为 MATCH、OPTIONAL MATCH 或 WITH 添加过滤条件。
MATCH (n:Person)
WHERE n.age > 30 AND n.city = 'Beijing'
RETURN n
// 标签过滤
MATCH (n)
WHERE n:Swedish
RETURN n
可变长度关系
使用 * 或 {} 量化符匹配多跳路径。
// 1 到 3 跳
MATCH (a)-[:FOLLOWS*1..3]->(b)
RETURN a, b
// 最多 5 跳(最小值默认为 1)
MATCH (a)-[:FOLLOWS*..5]->(b)
RETURN a, b
// 精确 2 跳
MATCH (a)-[:KNOWS*2]->(b)
RETURN a, b
⚠️ 注意:无界遍历(
*)在大图上可能导致性能爆炸,生产环境建议设置合理上限(通常 4–7 跳)。
最短路径
// 单条最短路径
MATCH p = shortestPath((a:User {id: 1})-[*..6]->(b:User {id: 99}))
RETURN nodes(p), relationships(p), length(p)
// 所有最短路径
MATCH p = allShortestPaths((a)-[*..10]->(b))
RETURN p
SHORTEST 选择器(Cypher 25)
MATCH p = SHORTEST 1 (:Person {name: 'Anna'})-[:KNOWS]-+(:Person {nationality: 'Canadian'})
RETURN p
三、写入与更新
CREATE
无条件创建节点和关系。不检查重复,重复执行会创建重复数据。
// 创建节点
CREATE (n:User {name: 'Alice', age: 32})
// 创建关系(需要先匹配两个端点)
MATCH (a:User {name: 'Alice'}), (b:User {name: 'Bob'})
CREATE (a)-[:FOLLOWS {since: 2024}]->(b)
// 创建完整路径
CREATE (a:User {name: 'Carol'})-[:WORKS_AT]->(c:Company {name: 'Acme'})
MERGE
“存在则匹配,不存在则创建”的 upsert 操作,是防止重复数据的关键。
// 合并节点
MERGE (n:User {email: 'alice@example.com'})
ON CREATE SET n.created = timestamp()
ON MATCH SET n.lastSeen = timestamp()
// 合并关系
MATCH (a:User {id: 1}), (b:User {id: 2})
MERGE (a)-[:FOLLOWS]->(b)
⚠️ 重要:
MERGE应基于唯一键。基于非唯一属性合并仍可能产生重复。
SET
更新节点标签或属性。
// 更新单个属性
MATCH (n:User {id: 1})
SET n.name = 'New Name'
// 替换所有属性
SET n = {name: 'Alice', age: 30}
// 添加/更新属性(保留现有属性)
SET n += {city: 'Shanghai'}
// 添加标签
SET n:Admin
DELETE / DETACH DELETE
// 删除关系
MATCH (a)-[r:FOLLOWS]->(b) WHERE a.id = 1
DELETE r
// 删除节点(必须无关系)
MATCH (n:User {email: 'old@example.com'})
DELETE n
// 删除节点及其所有关系
MATCH (n:User {email: 'old@example.com'})
DETACH DELETE n
REMOVE
移除属性或标签。
MATCH (n:User {id: 1})
REMOVE n.status
MATCH (n:User {id: 1})
REMOVE n:Admin
四、投影与聚合
RETURN
定义查询结果集,支持 AS 别名。
MATCH (n:Person)
RETURN n.name AS name, n.age AS age
WITH
将查询部分链接起来,定义新的作用域。未列出的变量会被丢弃。
// 变量 b 在 WITH 之后不可用
MATCH (a:Person)-[:KNOWS]->(b:Person)
WITH a, count(*) AS friends
WHERE friends > 5
RETURN a.name, friends
ORDER BY friends DESC
// 使用 WITH * 保留所有变量
MATCH (a)-[:KNOWS]->(b)
WITH *
RETURN a, b
UNWIND
将列表展开为一系列行。
UNWIND [1, 2, 3] AS x
RETURN x
排序与分页
MATCH (n:Person)
RETURN n.name, n.age
ORDER BY n.age DESC, n.name ASC
SKIP 10
LIMIT 5
⚠️
ORDER BY中不能使用AS别名,也不支持 SQL 的NULLS LAST/FIRST。空值在升序中排最后,降序中排最前。
五、聚合函数
| 函数 | 说明 |
|---|---|
count(*) |
统计所有行(包括 null) |
count(expr) |
统计非 null 值的行数 |
count(DISTINCT n.prop) |
去重计数 |
collect(expr) |
收集为列表,忽略 null |
sum(), avg(), min(), max() |
标准聚合 |
分组键:
RETURN或WITH中所有非聚合表达式都隐式成为分组键。
六、条件表达式
// 通用 CASE(if-elseif-else)
RETURN n.title,
CASE
WHEN n.rating >= 8 THEN 'great'
WHEN n.rating >= 6 THEN 'good'
ELSE 'skip'
END AS verdict
// 简单 CASE(switch)
RETURN n.status,
CASE n.status
WHEN 'A' THEN 'Active'
WHEN 'I' THEN 'Inactive'
ELSE 'Unknown'
END AS label
// 条件计数
sum(CASE WHEN r.rating = 5 THEN 1 ELSE 0 END) AS fiveStarCount
七、Null 处理
// 正确:使用 IS NULL / IS NOT NULL
WHERE n.email IS NOT NULL
WHERE n.email IS NULL
// 错误:永远不匹配
WHERE n.email = null
// coalesce() 返回第一个非空值
RETURN coalesce(n.nickname, n.name) AS displayName
null = null的结果是null(不是 true),WHERE将null视为 false。
八、类型系统
属性类型
| 类型 | 说明 |
|---|---|
BOOLEAN |
布尔 |
STRING |
字符串 |
INTEGER |
整数 |
FLOAT |
浮点数 |
DATE / LOCAL TIME / ZONED TIME |
日期/时间 |
LOCAL DATETIME / ZONED DATETIME |
日期时间 |
DURATION |
持续时间 |
POINT |
空间点 |
UUID |
仅 Cypher 25(Neo4j 2026.08+) |
VECTOR<TYPE>(DIMENSION) |
向量,维度 1–4096 |
列表类型
LIST<BOOLEAN NOT NULL>、LIST<STRING NOT NULL>、LIST<INTEGER NOT NULL> 等。
动态联合
INTEGER | FLOAT | STRING
类型转换
优先使用 OrNull 变体,转换失败返回 null 而非报错:
toIntegerOrNull(n.score) toFloatOrNull(n.score) toBooleanOrNull(n.flag) toStringOrNull(n.value)
类型谓词
WHERE n.value IS :: INTEGER NOT NULL
九、索引与约束
创建索引
// 单属性索引
CREATE INDEX FOR (n:User) ON (n.email)
// 复合索引
CREATE INDEX FOR (n:User) ON (n.lastName, n.firstName)
// 唯一约束
CREATE CONSTRAINT user_email_unique
FOR (n:User)
REQUIRE n.email IS UNIQUE
创建属性存在约束(企业版)
CREATE CONSTRAINT user_name_exists
FOR (n:User)
REQUIRE n.name IS NOT NULL
创建属性类型约束(企业版)
CREATE CONSTRAINT user_age_type
FOR (n:User)
REQUIRE n.age IS :: INTEGER
显示约束
SHOW CONSTRAINTS
十、管理子句
// 显示索引
SHOW INDEXES
// 显示约束
SHOW CONSTRAINTS
// 显示图类型(Cypher 25)
SHOW GRAPH TYPES
十一、查询调优
// 查看执行计划(不执行)
EXPLAIN MATCH (n:User) WHERE n.email = 'a@b.com' RETURN n
// 执行并分析实际行数和数据库命中
PROFILE MATCH (n:User) WHERE n.email = 'a@b.com' RETURN n
关注执行计划中是否出现
NodeByLabelScan——这通常意味着索引未生效。
十二、实用要点
- 参数化查询:始终使用
$param而非字符串拼接,防止注入并提升性能。 CREATEvsMERGE:CREATE无条件创建,MERGE存在则复用。防止重复节点的核心是MATCH现有节点后再MERGE关系。count(*)vscount(n):前者统计所有行,后者只统计非 null 值。id()已弃用:使用elementId()获取元素 ID。- WITH 作用域:
WITH会丢弃未列出的变量,用WITH *保留全部。
