Cypher语法
目标:掌握 Cypher 的基本语法规则,能独立完成 “创建 - 查询 - 更新 - 删除”(CRUD)操作,理解图数据的表达逻辑。
1. 先搞懂 3 个核心语法符号(基础中的基础)
Cypher 语法高度可视化,记住这 3 个符号就能描述任何图结构:
| 符号 | 含义 | 示例 |
|---|---|---|
() |
节点(可加标签 / 属性) | (u:User)(标签:User)、(p:Product {name:"手机"})(带属性) |
-[:类型]-> |
有向关系(可加属性) | -[:FRIENDS_WITH]->(类型:FRIENDS_WITH)、-[:PURCHASED {time:"2025-11-18"}]->(带属性) |
[] |
关系变量(用于引用) | -[r:PURCHASED]->(用 r 指代该关系,后续可查询 / 更新 r 的属性) |
步骤 1:创建节点(用户、商品)
// 创建3个用户节点(标签:User,带id/name/age属性)
CREATE (u1:User {id:1, name:"张三", age:28, city:"北京"}),
(u2:User {id:2, name:"李四", age:30, city:"上海"}),
(u3:User {id:3, name:"王五", age:25, city:"广州"});
// 创建2个商品节点(标签:Product,带id/name/price/category属性)
CREATE (p1:Product {id:101, name:"iPhone 15", price:7999, category:"手机"}),
(p2:Product {id:102, name:"华为Mate 60", price:6999, category:"手机"}),
(p3:Product {id:103, name:"AirPods Pro", price:1999, category:"配件"});
Cypher
CREATE 语法中,u1、u2、u3 是 节点变量(Node Variables),它们的核心区别和作用如下:一、本质区别:指向不同的节点实例
u1、u2、u3 是三个独立的变量名,分别对应你创建的 3 个 User 节点:u1→ 指向{id:1, name:"张三", ...}的 User 节点u2→ 指向{id:2, name:"李四", ...}的 User 节点u3→ 指向{id:3, name:"王五", ...}的 User 节点
变量名本身可以任意定义(比如改成
zhangsan、lisi、wangwu),但必须唯一(同一语句中不能重复用同一个变量指向不同节点)。二、核心作用:临时引用节点,方便后续操作
变量的核心价值是「在当前 Cypher 语句中临时引用节点」,避免重复写匹配条件。具体有两个常见场景:
1. 同一 CREATE 语句中创建关联关系(简化语法)
如果创建节点的同时要创建节点间的关系,变量可以直接关联,无需额外
MATCH:// 创建3个用户节点(u1/u2/u3),同时创建张三和李四的好友关系
CREATE (u1:User {id:1, name:"张三"}),
(u2:User {id:2, name:"李四"}),
(u3:User {id:3, name:"王五"}),
(u1)-[:FRIENDS_WITH]->(u2); // 直接用u1/u2引用节点,创建关系
如果没有变量,你需要先
CREATE 节点,再 MATCH 节点才能创建关系,步骤更繁琐。2. 后续语句中匹配 / 操作指定节点
如果后续要对某个节点单独操作(比如修改属性、创建关系、删除),可以通过变量快速定位(仅限同一语句上下文):
// 创建节点后,立即给u1(张三)添加一个新属性
CREATE (u1:User {id:1, name:"张三"}),
(u2:User {id:2, name:"李四"}),
(u3:User {id:3, name:"王五"})
SET u1.email = "zhangsan@xxx.com"; // 用u1直接定位张三,添加属性
三、关键注意点
- 变量仅在当前语句有效:
u1、u2、u3只在你写的这个CREATE语句中起作用,语句执行结束后,变量会失效。如果之后要操作这些节点,需要通过MATCH (u:User {id:1})重新匹配(不能直接用u1)。 - 变量名不影响节点本身:节点的本质是「标签(User)+ 属性(id/name/age 等)」,变量名只是临时引用,哪怕把
u1改成x,节点的内容(属性、标签)完全不变。 - 变量可省略(但不推荐):如果创建节点后不需要立即操作,也可以省略变量:
但这种写法后续无法在同一语句中创建关系,灵活性差,所以一般都会定义变量
// 省略变量,仅创建节点(无法直接关联关系) CREATE (:User {id:1, name:"张三"}), (:User {id:2, name:"李四"}), (:User {id:3, name:"王五"});
步骤 2:创建关系(好友、购买)
// 1. 创建好友关系(张三↔李四,张三→王五)
MATCH (u1:User {id:1}), (u2:User {id:2})
CREATE (u1)-[:FRIENDS_WITH {since:2020, status:"close"}]->(u2),
(u2)-[:FRIENDS_WITH {since:2020, status:"close"}]->(u1); // 双向关系需单独创建
MATCH (u1:User {id:1}), (u3:User {id:3})
CREATE (u1)-[:FRIENDS_WITH {since:2022, status:"ordinary"}]->(u3);
// 2. 创建购买关系(张三买了iPhone 15,李四买了华为Mate 60和AirPods)
MATCH (u1:User {id:1}), (p1:Product {id:101})
CREATE (u1)-[:PURCHASED {time:"2025-01-10", amount:1}]->(p1);
MATCH (u2:User {id:2}), (p2:Product {id:102}), (p3:Product {id:103})
CREATE (u2)-[:PURCHASED {time:"2025-03-15", amount:1}]->(p2),
(u2)-[:PURCHASED {time:"2025-03-15", amount:1}]->(p3);
在这个 Cypher 语法中,
-[:FRIENDS_WITH {since:2020, status:"close"}]-> 表示一条带有属性的有向关系,拆解后每个部分的含义如下,结合场景能更清晰理解:一、完整结构拆解
| 符号 / 语法部分 | 含义 |
|---|---|
- 和 -> |
关系的「方向标记」:- 表示关系的一端,-> 表示关系的「指向」(从左到右) |
[:FRIENDS_WITH] |
关系的「类型」(必填):FRIENDS_WITH 是关系类型名,用于标识节点间的关联语义(这里是「好友关系」) |
{since:2020, status:"close"} |
关系的「属性」(可选):用键值对存储关系的附加信息,和节点属性逻辑一致 |
1. 方向标记:- 和 ->
-
- 核心作用:定义关系的「起点」和「终点」,Cypher 中关系默认是有向的(除非用
--表示无向,但实际存储仍会隐含方向)。
- 核心作用:定义关系的「起点」和「终点」,Cypher 中关系默认是有向的(除非用
2. 关系类型:[:FRIENDS_WITH]
- 相当于给关系贴的「标签」,用于区分不同语义的关联(比如
PURCHASED是购买关系、FOLLOWS是关注关系)。 - 规则:
- 关系类型只能有一个(不能写
[:FRIENDS_WITH:COLLEAGUE],一个关系只能对应一种类型); - 命名规范:通常用大写字母 + 下划线(蛇形命名),语义要明确,方便后续查询(比如
MATCH (a)-[:FRIENDS_WITH]->(b)可快速筛选所有好友关系)。
- 关系类型只能有一个(不能写
3. 关系属性:{since:2020, status:"close"}
- 存储关系的附加信息,和节点属性一样支持字符串、数字、布尔值等类型,核心是描述「关系的特征」:
since:2020:表示这条好友关系的「建立时间是 2020 年」;status:"close":表示这条关系的「状态是亲密好友」。
- 作用:后续查询时可以过滤或返回这些属性,比如:
// 查询张三(id=1)的所有2020年建立的亲密好友
MATCH (u1:User {id:1})-[r:FRIENDS_WITH]->(u2)
WHERE r.since = 2020 AND r.status = "close"
RETURN u2.name, r.since;
步骤 3:查询练习(从简单到复杂)
// 1. 基础查询:查询所有用户的姓名和年龄
MATCH (u:User) RETURN u.name AS 用户名, u.age AS 年龄;
// 2. 条件查询:查询年龄>25的用户
MATCH (u:User) WHERE u.age > 25 RETURN u.name, u.age;
// 3. 关系查询:查询“购买了手机的用户”
MATCH (u:User)-[r:PURCHASED]->(p:Product {category:"手机"})
RETURN u.name AS 用户名, r.time AS 购买时间, p.name AS 商品名;
// 4. 关联查询:查询“张三的朋友购买了什么商品”(1度关联)
MATCH (z:User {name:"张三"})-[:FRIENDS_WITH]->(f:User)-[r:PURCHASED]->(p:Product)
RETURN z.name AS 本人, f.name AS 朋友, p.name AS 朋友购买的商品;
// 5. 多深度查询:查询“张三的1-2度朋友”(直接朋友+朋友的朋友)
MATCH (z:User {name:"张三"})-[:FRIENDS_WITH*1..2]->(f:User)
RETURN z.name, f.name, length(relationships(p)) AS 关系深度; // length()计算路径长度
// 6. 聚合查询:统计每个商品的购买次数
MATCH (u:User)-[r:PURCHASED]->(p:Product)
RETURN p.name AS 商品名, sum(r.amount) AS 总销量
ORDER BY 总销量 DESC;
核心语法:可变长度关系
Neo4j 中用
-[*最小深度..最大深度]-> 表示多深度关系,此处需求是 1-2 度,因此使用 -[*1..2]->。匹配 n 到 m 步的关系,语法格式为:
()-[:关系类型*最小深度..最大深度]->()
关键细节:
- 深度边界:
*1..2:匹配 1 到 2 度关系(直接关联 + 间接关联 1 次)*2:等价于*2..2,仅匹配 2 度关系(精确深度)*1..:匹配 1 度及以上(无最大深度,谨慎使用)*:等价于*0..∞(0 度 = 节点本身,无上限,极易性能爆炸)
- 关系方向:
->:仅匹配「从左到右」的单向关系<-:仅匹配「从右到左」的单向关系- 无箭头
()-[]-():匹配双向关系(如朋友、同事等无明确方向的关联)
- 关系类型:
- 单一类型:
[:FRIENDS_WITH*1..2](仅朋友关系) - 多类型:
[:FRIENDS_WITH|COLLEAGUE*1..2](朋友或同事关系,用|分隔)
- 单一类型:
完整查询语句
// 查询张三的1-2度朋友(直接朋友+朋友的朋友)
MATCH (zhangsan:User {name:"张三"})-[:FRIENDS_WITH*1..2]-(friend:User)
WHERE friend <> zhangsan // 排除张三本人
RETURN
friend.id AS 朋友ID,
friend.name AS 朋友姓名,
friend.age AS 朋友年龄,
// 计算关系深度(1=直接朋友,2=朋友的朋友)
length((zhangsan)-[:FRIENDS_WITH*]-(friend)) AS 关系度数
ORDER BY 关系度数 ASC, 朋友ID ASC;
关键细节解释
-
关系方向无关性:用
-(关系)-(无箭头)而非->,因为FRIENDS_WITH是双向关系(张三→李四,李四→张三),无箭头可同时匹配两种方向,避免遗漏。 -
排除本人:
WHERE friend <> zhangsan至关重要 —— 若不排除,当深度包含 0 时(如*0..2)会匹配自己,此处虽深度从 1 开始,但养成排除习惯可避免异常数据。 -
深度计算:
length((zhangsan)-[:FRIENDS_WITH*]-(friend))用于明确标注 “几度朋友”:- 长度 = 1 → 1 度(直接朋友)
- 长度 = 2 → 2 度(朋友的朋友)
执行报错:
Type mismatch: expected Path but was List<Path> (line 8, column 10 (offset: 223))
" length((zhangsan)-[:FRIENDS_WITH*]-(friend)) AS 关系度数",执行上述脚本报错
报错原因是:
(zhangsan)-[:FRIENDS_WITH*]-(friend) 用了无边界的可变长度关系(* 等价于 *0..∞),会返回多条路径(List<Path>),而 length() 函数只能接收单条路径(Path),无法直接计算列表长度,导致类型不匹配。修复方案:用 shortestPath() 取最短路径计算度数
朋友关系的 “度数” 本质是两个节点间的最短路径长度(1 度 = 直接相连,2 度 = 最短路径需 2 步),因此用
shortestPath() 获取单条最短路径,再用 length() 计算长度即可。修复后的完整查询语句
// 查询张三的1-2度朋友(直接朋友+朋友的朋友)- 修复度数计算报错
MATCH (zhangsan:User {name:"张三"})-[:FRIENDS_WITH*1..2]-(friend:User)
WHERE friend <> zhangsan // 排除张三本人
WITH zhangsan, friend, shortestPath((zhangsan)-[:FRIENDS_WITH*]-(friend)) AS shortest_path
RETURN
friend.id AS 朋友ID,
friend.name AS 朋友姓名,
friend.age AS 朋友年龄,
length(shortest_path) AS 关系度数 // 基于最短路径计算度数
ORDER BY 关系度数 ASC, 朋友ID ASC;
核心修复逻辑
shortestPath()函数:专门用于获取两个节点间的最短路径(单条 Path),解决了原查询中 “多条路径列表” 的问题,length()可正常计算其长度。WITH子句:先筛选出符合条件的朋友,再计算最短路径,避免直接在RETURN中嵌套复杂路径表达式,逻辑更清晰。
简化版(无需度数,仅需去重朋友列表)
如果不需要显示 “关系度数”,仅需获取 1-2 度朋友,可直接去重,避免路径相关报错:
// 简化版:仅返回张三的1-2度朋友(去重)
MATCH (zhangsan:User {name:"张三"})-[:FRIENDS_WITH*1..2]-(friend:User)
WHERE friend <> zhangsan
RETURN DISTINCT // 去重(若存在多条路径指向同一朋友)
friend.id AS 朋友ID,
friend.name AS 朋友姓名,
friend.age AS 朋友年龄
ORDER BY friend.id ASC;
步骤 4:更新与删除练习
// 1. 更新:给张三增加“职业”属性,修改年龄为29
MATCH (u:User {name:"张三"})
SET u.job = "工程师", u.age = 29
RETURN u;
// 2. 更新关系:修改李四购买华为Mate 60的数量为2
MATCH (u:User {name:"李四"})-[r:PURCHASED]->(p:Product {name:"华为Mate 60"})
SET r.amount = 2
RETURN r;
// 3. 删除:删除王五的所有关系和王五节点
MATCH (u:User {name:"王五"})-[r]->() // 匹配王五的所有关系
DELETE r, u; // 先删关系,再删节点
// 4. 删除属性:删除张三的“city”属性
MATCH (u:User {name:"张三"})
REMOVE u.city
RETURN u;
二、进阶阶段:掌握高频场景语法
入门后,重点攻克 Cypher 的 “高级特性”,覆盖实际开发中的高频场景:
1. 索引与约束(性能优化核心)
当数据量较大(万级 +)时,必须通过索引加速查询,约束保证数据一致性:
// 1. 创建索引:对User的name属性创建索引(高频查询字段)
CREATE INDEX idx_user_name ON :User(name);
// 2. 创建唯一约束:保证User的id属性不重复(类似主键)
CREATE CONSTRAINT unique_user_id ON :User(id) ASSERT id IS UNIQUE;
// 3. 查看索引/约束
SHOW INDEXES;
SHOW CONSTRAINTS;
// 4. 删除索引/约束
DROP INDEX idx_user_name;
DROP CONSTRAINT unique_user_id;
2. 路径操作与函数
Cypher 内置大量函数,简化路径分析、属性处理等操作:
// 1. 路径函数:获取路径中的所有节点/关系
MATCH p = (u:User)-[:PURCHASED]->(p:Product)
RETURN nodes(p) AS 路径节点, relationships(p) AS 路径关系;
// 2. 最短路径查询(内置算法)
MATCH shortestPath(p = (u1:User {name:"张三"})-[*]->(u2:User {name:"李四"}))
RETURN p, length(p) AS 最短路径长度;
// 3. 字符串函数:模糊查询(类似SQL的LIKE)
MATCH (p:Product) WHERE p.name CONTAINS "手机" // 包含“手机”
RETURN p.name;
// 4. 聚合函数:统计、分组
MATCH (u:User)-[r:PURCHASED]->(p:Product)
GROUP BY p.category // 按商品分类分组
RETURN p.category AS 分类, count(u) AS 购买人数, sum(r.amount) AS 总销量;
3. 批量操作(高效处理大量数据)
实际项目中常需批量导入 / 更新数据,避免逐条执行:
// 1. 批量创建:通过UNWIND遍历列表批量创建节点
WITH ["赵六", "孙七", "周八"] AS names // 定义姓名列表
UNWIND names AS name // 遍历列表,每个name对应一条记录
CREATE (u:User {name: name, age: 26, city: "深圳"});
// 2. 批量更新:给所有手机类商品降价1000
MATCH (p:Product {category:"手机"})
SET p.price = p.price - 1000
RETURN p.name, p.price;
4. 条件分支(CASE 语句)
类似 SQL 的 CASE,支持复杂逻辑判断:
// 根据商品价格分类
MATCH (p:Product)
RETURN p.name AS 商品名,
CASE
WHEN p.price > 5000 THEN "高端产品"
WHEN p.price > 2000 THEN "中端产品"
ELSE "低端产品"
END AS 价格等级;

浙公网安备 33010602011771号