1. 什么是模式匹配 #
1.1 基础概念 #
模式匹配是 Cypher 的核心功能。它让你能够用类似"画图"的方式来描述你想要查找的数据结构,然后数据库会自动找到所有匹配的数据。
1.2 什么是模式? #
模式(Pattern)就像是一个"模板"或"模具",用来描述图的结构。想象一下:
- 如果你想要找"朋友",模式就是:
(人)-[:朋友]->(朋友) - 如果你想要找"朋友的朋友",模式就是:
(人)-[:朋友]->(朋友)-[:朋友]->(朋友的朋友)
模式的作用:
- 描述你想要查找的图结构
- 让数据库知道"什么样的数据是你想要的"
- 可以很简单,也可以很复杂
1.3 为什么需要模式匹配? #
在传统数据库中,查找数据需要写复杂的 SQL 查询。但在图数据库中,模式匹配让你能够:
- 直观描述:用接近自然语言的方式描述关系
- 高效查询:数据库自动优化查询路径
- 灵活匹配:可以匹配任意复杂的图结构
1.4 模式的组成 #
一个模式由以下部分组成:
- 节点:用圆括号
()表示 - 关系:用方括号和箭头
-[]->表示 - 变量:用来引用节点和关系
- 标签和属性:用来过滤和匹配
简单示例:
(人)-[:朋友]->(朋友)这个模式表示"一个人通过朋友关系连接到另一个朋友"。
2. 节点语法详解 #
2.1 节点语法的组成部分 #
一个完整的节点语法包含三个可选部分:
- 变量名:用来引用这个节点
- 标签:节点的类型
- 属性:节点的数据
基本格式:
(变量名:标签 {属性})2.2 节点语法示例 #
2.2.1 匿名节点:() #
最简单的节点,没有变量、标签或属性。
使用场景:
- 不关心节点的具体信息
- 只关心关系或模式结构
- 在查询中作为占位符
示例:
MATCH ()-[r:ACTED_IN]->(m:Movie)
RETURN m这个查询表示"查找任意节点通过 ACTED_IN 关系连接到电影",不关心起始节点是什么。
2.2.2 带变量的节点:(matrix) #
给节点一个变量名,可以在查询的其他地方引用它。
特点:
- 变量名通常使用小写字母
- 可以是单个字母(如
p)或单词(如person) - 变量只在当前查询中有效
示例:
MATCH (matrix)
RETURN matrix2.2.3 带标签的节点:(:Movie) #
标签用来分类节点,类似于数据库中的"表"。
示例:
(:Person) // Person 类型的节点
(:Movie) // Movie 类型的节点
(:Company) // Company 类型的节点2.2.4 带属性和标签的节点:(matrix:Movie {title: 'The Matrix'}) #
完整的节点语法,包含变量、标签和属性。
示例:
(matrix:Movie {title: 'The Matrix', released: 1997})2.3 节点语法实践 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建一个空节点
CREATE ()
// 创建一个带有标签 Person 的节点
CREATE (:Person)
// 创建一个带变量 p 且有标签 Person 的节点
CREATE (p:Person)
// 创建一个 Person 类型的节点,并带有 name 和 age 属性
CREATE (:Person {name: 'Sally', age: 30})
// 创建一个带变量 matrix、有标签 Movie、有 title 和 released 属性的节点
CREATE (matrix:Movie {title: 'The Matrix', released: 1997})
// 创建一个带变量 keanu,有 Person 和 Actor 标签,并带有 name 和 born 属性的节点
CREATE (keanu:Person:Actor {name: 'Keanu Reeves', born: 1964})
// 查询所有 Person 类型的节点并返回
MATCH (p:Person) RETURN p
// 查询名字为 Sally 的 Person 类型节点
MATCH (p:Person {name: 'Sally'}) RETURN p
// 查询即为 Person 又为 Actor 的节点,返回其 name 和 born 属性
MATCH (p:Person:Actor) RETURN p.name AS name, p.born AS born3. 关系语法详解 #
3.1 关系语法的组成部分 #
一个完整的关系语法包含:
- 变量名(可选):用来引用关系
- 关系类型(可选):关系的种类
- 属性(可选):关系的附加数据
- 方向(必需):
->或<-或-(无向)
基本格式:
-[变量名:关系类型 {属性}]->3.2 关系语法示例 #
3.2.1 最简单的关系:-[]-> #
只有方向,没有变量、类型或属性。
使用场景:
- 不关心关系的具体信息
- 只关心两个节点之间是否有关系
示例:
MATCH (p:Person)-[]->(m:Movie)
RETURN p, m3.2.2 带类型的关系:-[:ACTED_IN]-> #
指定关系类型,但不使用变量。
使用场景:
- 只关心特定类型的关系
- 不需要访问关系的属性
示例:
// 匹配所有标签为 Person 的节点与标签为 Movie 的节点,
// 且它们之间存在类型为 ACTED_IN 的关系(p 演过 m)
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
// 返回匹配到的 Person 节点和 Movie 节点
RETURN p, m3.2.3 带变量的关系:-[r:ACTED_IN]-> #
给关系一个变量名,可以在查询中使用。
使用场景:
- 需要访问关系的属性
- 需要在 WHERE 子句中过滤关系
- 需要在 RETURN 中返回关系信息
示例:
// 匹配标签为 Person 的节点 p 与标签为 Movie 的节点 m,二者之间有 ACTED_IN 类型的关系 r
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
// 返回演员姓名、电影标题和出演角色信息
RETURN p.name, m.title, r.roles3.2.4 完整的关系语法:-[r:ACTED_IN {roles: ['Neo']}]-> #
包含变量、类型和属性。
示例:
// 匹配标签为 Person 的节点 p 与标签为 Movie 的节点 m,二者之间有 ACTED_IN 类型的关系 r,且 r 的属性 roles 包含 'Neo'
MATCH (p:Person)-[r:ACTED_IN {roles: ['Neo']}]->(m:Movie)
// 返回匹配到的 Person 节点 p、关系 r 和 Movie 节点 m
RETURN p, r, m3.3 关系语法实践 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建人物和电影节点
CREATE (zhangyi:Person {name: '张译', born: 1978}),
(wujing:Person {name: '吴京', born: 1974}),
(hero:Movie {title: '满江红', released: 2023}),
(zhanchang:Movie {title: '长津湖', released: 2021})
// 创建匿名关系,连接张译和满江红
MATCH (p:Person {name: '张译'}),
(m:Movie {title: '满江红'})
CREATE (p)-[]->(m)
// 创建 ACTED_IN 类型的关系,表示李连杰出演少林寺
MATCH (p:Person {name: '吴京'}),
(m:Movie {title: '长津湖'})
CREATE (p)-[:ACTED_IN]->(m)
// 创建带变量的 ACTED_IN 关系,变量名为 r
MATCH (p:Person {name: '张译'}),
(m:Movie {title: '满江红'})
CREATE (p)-[r:ACTED_IN]->(m)
// 创建带属性的 ACTED_IN 关系(角色为"孙均"、年份为2023),变量名为 r
MATCH (p:Person {name: '张译'}),
(m:Movie {title: '满江红'})
CREATE (p)-[r:ACTED_IN {roles: ['孙均'], year: 2023}]->(m)
// 匹配所有 Person 节点与所有 Movie 节点间的任意关系,返回对应的人名和电影名
MATCH (p:Person)-[]->(m:Movie)
RETURN p.name AS person, m.title AS movie
// 匹配所有 Person 节点与 Movie 节点之间的 ACTED_IN 关系,返回人名与电影名
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name AS person, m.title AS movie4. 模式语法详解 #
4.1 什么是模式语法? #
模式语法是节点和关系语法的组合,用来描述完整的图结构。模式是 Cypher 的核心,让你能够描述"想要查找什么样的图"。
4.2 简单模式示例 #
最简单的模式包含一个关系,连接两个节点:
(keanu:Person:Actor {name: 'Keanu Reeves'})
-[role:ACTED_IN {roles: ['Neo']}]->(matrix:Movie {title: 'The Matrix'})这个模式表示:
- 一个名为 Keanu Reeves 的 Person/Actor 节点
- 通过 ACTED_IN 关系(角色是 Neo)
- 连接到一个名为 The Matrix 的 Movie 节点
4.3 复杂模式示例 #
4.3.1 多跳关系模式 #
// 匹配一个Person节点p1,通过IS_FRIENDS_WITH关系,连接到Person节点p2,再通过IS_FRIENDS_WITH关系连接到Person节点p3
(p1:Person)-[:IS_FRIENDS_WITH]->(p2:Person)-[:IS_FRIENDS_WITH]->(p3:Person)这个模式表示"朋友的朋友"(2跳关系)。
4.3.2 多关系模式 #
// 匹配一个 Person 节点 p,通过 ACTED_IN 关系指向 Movie 节点 m
// 同时,该 Movie 节点 m 被另一个 Person 节点 d 通过 DIRECTED 关系指向
(p:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(d:Person)这个模式表示"演员出演电影,且该电影被某个导演执导"。
4.3.3 可变长度模式 #
// 匹配一个 Person 节点,经过 1 到 3 跳的任意关系,连接到任意节点 connected
(p:Person)-[*1..3]-(connected)这个模式表示"1到3跳的任意关系"。
4.4 模式语法实践 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建人物和电影节点
CREATE (zhangyi:Person:Actor {name: '张译', born: 1978}),
(wujing:Person:Actor {name: '吴京', born: 1974}),
(chenkaige:Person:Director {name: '陈凯歌', born: 1952}),
(shuyuan:Movie {title: '满江红', released: 2023}),
(wandaxi:Movie {title: '我和我的祖国', released: 2019}),
(zhanchang:Movie {title: '长津湖', released: 2021})
// 创建 张译 和 满江红 之间的出演关系,并注明角色"孙均"
MATCH (zhangyi:Person {name: '张译'}),
(shuyuan:Movie {title: '满江红'})
CREATE (zhangyi)-[:ACTED_IN {roles: ['孙均']}]->(shuyuan)
// 创建 吴京 和 长津湖 之间的出演关系,并注明角色"伍千里"
MATCH (wujing:Person {name: '吴京'}),
(zhanchang:Movie {title: '长津湖'})
CREATE (wujing)-[:ACTED_IN {roles: ['伍千里']}]->(zhanchang)
// 创建 张译 和 我和我的祖国 之间的出演关系,并注明角色"高远"
MATCH (zhangyi:Person {name: '张译'}),
(wandaxi:Movie {title: '我和我的祖国'})
CREATE (zhangyi)-[:ACTED_IN {roles: ['高远']}]->(wandaxi)
// 创建 陈凯歌 和 长津湖 之间的导演关系
MATCH (chenkaige:Person {name: '陈凯歌'}),
(zhanchang:Movie {title: '长津湖'})
CREATE (chenkaige)-[:DIRECTED]->(zhanchang)
// 创建 陈凯歌 和 我和我的祖国 之间的导演关系
MATCH (chenkaige:Person {name: '陈凯歌'}),
(wandaxi:Movie {title: '我和我的祖国'})
CREATE (chenkaige)-[:DIRECTED]->(wandaxi)
// 查询所有人物的名字
MATCH (p:Person) RETURN p.name AS name LIMIT 3
// 查询所有人物和他们所出演的电影
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name AS actor, m.title AS movie
// 查询所有演员、导演及其合作的电影
MATCH (actor:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(director:Person)
RETURN actor.name AS actor, director.name AS director, m.title AS movie
// 查询出演角色为"伍千里"的所有演员及对应电影和角色信息
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE '伍千里' IN r.roles
RETURN p.name AS actor, m.title AS movie, r.roles AS roles5. CREATE 和 MATCH 的使用 #
5.1 CREATE:创建数据 #
CREATE 用于创建新的节点和关系。它总是创建新数据,即使数据已存在也会创建重复的。
5.1.1 创建单个节点 #
// 创建一个 Movie 节点,属性为 title='长津湖', released=2021
CREATE (:Movie {title: '长津湖', released: 2021})这个语句创建一个 Movie 节点,没有变量名,所以无法在后续查询中引用它。
5.1.2 创建节点并返回 #
如果想返回创建的数据,需要给节点一个变量名:
// 创建一个 Person 节点,属性为 name='吴京', born=1974
CREATE (p:Person {name: '吴京', born: 1974})
// 返回刚刚创建的节点 p
RETURN p5.1.3 创建复杂结构 #
可以一次性创建节点和关系:
// 创建一个Person节点,名字为"吴京"
// 创建一个ACTED_IN关系,属性roles为列表['伍千里'],连接到Movie节点
// 创建一个Movie节点,标题为"长津湖",上映时间为2021年
CREATE (a:Person {name: '吴京'})
-[r:ACTED_IN {roles: ['伍千里']}]->
(m:Movie {title: '长津湖', released: 2021})
// 返回刚刚创建的三个对象:a(演员)、r(关系)、m(电影)
RETURN a, r, m5.2 MATCH:匹配数据 #
MATCH 用于查找匹配模式的节点和关系。它不会创建新数据,只查找已存在的数据。
5.2.1 匹配所有节点 #
MATCH (m:Movie)
RETURN m5.2.2 匹配特定节点 #
MATCH (p:Person {name: '吴京'})
RETURN p5.2.3 匹配关系模式 #
MATCH (p:Person {name: '吴京'})-[r:ACTED_IN]->(m:Movie)
RETURN m.title, r.roles5.3 CREATE 和 MATCH 实践 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建一个电影节点,标题为"长津湖",上映年份为2021
CREATE (:Movie {title: '长津湖', released: 2021})
// 创建一个人物节点,名字为"吴京",出生年份为1974,并返回该节点
CREATE (p:Person {name: '吴京', born: 1974})
RETURN p
// 创建一个人物节点"易烊千玺",出生年份2000
// 创建一个"ACTED_IN"关系,属性roles为['伍万里']
// 关系连接到电影节点"长津湖"(2021年)
CREATE (a:Person {name: '易烊千玺', born: 2000})
-[r:ACTED_IN {roles: ['伍万里']}]->
(m:Movie {title: '长津湖', released: 2021})
// 匹配所有电影节点,并返回其标题和上映年份
MATCH (m:Movie) RETURN m.title AS title, m.released AS released
// 匹配名字为"吴京"的人物节点,返回其姓名和出生年份
MATCH (p:Person {name: '吴京'}) RETURN p.name AS name, p.born AS born
// 匹配"易烊千玺"出演的所有电影,并返回电影标题和角色列表
MATCH (p:Person {name: '易烊千玺'})-[r:ACTED_IN]->(m:Movie)
RETURN m.title AS title, r.roles AS roles6. MERGE 的使用 #
6.1 什么是 MERGE? #
MERGE 是 CREATE 和 MATCH 的组合。它会先检查数据是否存在,如果不存在则创建,如果存在则匹配。
6.2 前置知识:为什么需要 MERGE? #
问题: 使用 CREATE 可能会创建重复数据
// 第一次执行:创建节点
CREATE (p:Person {name: '吴京'})
// 第二次执行:又创建一个同名节点(重复!)
CREATE (p:Person {name: '吴京'})解决方案: 使用 MERGE 避免重复
// 第一次执行:创建节点
MERGE (p:Person {name: '吴京'})
// 第二次执行:匹配已存在的节点(不重复)
MERGE (p:Person {name: '吴京'})6.3 MERGE 的基本用法 #
6.3.1 基本 MERGE #
MERGE (m:Movie {title: '长津湖'})
RETURN m行为:
- 如果节点不存在:创建它
- 如果节点已存在:匹配它
6.3.2 使用 ON CREATE SET #
// 如果不存在标题为"长津湖"的电影节点,则创建该节点
MERGE (m:Movie {title: '长津湖'})
// 如果是新创建的节点,则设置 released 属性为 2012
ON CREATE SET m.released = 2012
// 返回节点 m
RETURN m行为:
- 如果节点不存在:创建它,并设置
released = 2012 - 如果节点已存在:匹配它,不执行
ON CREATE SET
6.3.3 使用 ON MATCH SET #
// 合并(匹配或创建)一个 Movie 节点,标题为"长津湖"
MERGE (m:Movie {title: '长津湖'})
// 如果节点已存在,则设置 lastSeen 属性为当前时间戳
ON MATCH SET m.lastSeen = timestamp()
// 返回节点 m
RETURN m行为:
- 如果节点不存在:创建它,不执行
ON MATCH SET - 如果节点已存在:匹配它,并更新
lastSeen
6.3.4 组合使用 #
// 合并(匹配或创建)一个标题为"长津湖"的 Movie 节点
MERGE (m:Movie {title: '长津湖'})
// 如果是新创建的节点,则设置 released 属性为 2012,created 属性为当前时间戳
ON CREATE SET m.released = 2012, m.created = timestamp()
// 如果节点已存在,则设置 lastSeen 属性为当前时间戳
ON MATCH SET m.lastSeen = timestamp()
// 返回节点 m
RETURN m6.4 MERGE 实践 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 合并(匹配或创建)一个标题为"长津湖"的电影节点,如果不存在则创建并返回该节点
MERGE (m:Movie {title: '长津湖'}) RETURN m
// 合并(匹配或创建)一个标题为"无问西东"的电影节点
// 如果该节点是新创建的,则设置 released 属性为 2018
MERGE (m:Movie {title: '无问西东'})
ON CREATE SET m.released = 2018
// 返回节点的 title 属性作为"标题",released 属性作为"上映年份"
RETURN m.title AS 标题, m.released AS 上映年份
// 合并(匹配或创建)一个标题为"长津湖"的电影节点
// 如果该节点已存在,则设置 lastSeen 属性为 '2024-01-01'
MERGE (m:Movie {title: '长津湖'})
ON MATCH SET m.lastSeen = '2024-01-01'
// 返回节点的 title 属性作为"标题",lastSeen 属性作为"最近查看时间"
RETURN m.title AS 标题, m.lastSeen AS 最近查看时间
// 合并(匹配或创建)一个标题为"我和我的祖国"的电影节点
// 如果是新创建的节点,则设置 released 为 2019,created 为 '2024-01-01'
// 如果节点已存在,则设置 lastSeen 为 '2024-01-01'
MERGE (m:Movie {title: '我和我的祖国'})
ON CREATE SET m.released = 2019, m.created = '2024-01-01'
ON MATCH SET m.lastSeen = '2024-01-01'
// 返回节点的各属性
RETURN m.title AS 标题, m.released AS 上映年份, m.created AS 创建时间, m.lastSeen AS 最近查看时间7. 使用别名优化查询结果 #
7.1 什么是别名? #
别名(Alias)是给查询结果中的字段起一个更易读的名字。使用 AS 关键字创建别名。
7.2 为什么需要别名? #
有些属性名可能不够直观,或者你想让结果更易读。使用别名可以让查询结果更清晰。
7.3 使用别名的示例 #
7.3.1 不使用别名 #
// 匹配名字为"易烊千玺"的人物,与其出演过的所有电影节点及关系
MATCH (y:Person {name:'易烊千玺'})-[rel:ACTED_IN]-(movie:Movie)
// 返回"易烊千玺"的姓名、出生年份,以及对应的电影标题和上映年份
RETURN y.name, y.born, movie.title, movie.released结果:
y.name | y.born | movie.title | movie.released7.3.2 使用别名 #
MATCH (y:Person {name:'易烊千玺'})
-[rel:ACTED_IN]-(movie:Movie)
RETURN y.name AS 姓名,
y.born AS `出生日期`,
movie.title AS 电影标题,
movie.released AS `发行时间`结果:
姓名 | 出生日期 | 电影标题 | 发行时间7.4 使用别名实践 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建人物和电影节点
CREATE (yiyang:Person {name: '易烊千玺', born: 2000}),
(betterDays:Movie {title: '少年的你', released: 2019})
// 匹配已创建的人物和电影节点
MATCH (yiyang:Person {name: '易烊千玺'}),
(betterDays:Movie {title: '少年的你'})
// 创建 "易烊千玺" 与 "少年的你" 之间的 ACTED_IN 关系,角色为"小北"
CREATE (yiyang)-[:ACTED_IN {roles: ['小北']}]->(betterDays)
// 匹配"易烊千玺"及其出演的电影,并返回详细属性(未使用别名)
MATCH (yiyang:Person {name:'易烊千玺'})-[rel:ACTED_IN]-(movie:Movie)
RETURN yiyang.name, yiyang.born, movie.title, movie.released
// 匹配"易烊千玺"及其出演的电影,并使用别名返回结果字段
MATCH (yiyang:Person {name:'易烊千玺'})
-[rel:ACTED_IN]-(movie:Movie)
RETURN yiyang.name AS 姓名,
yiyang.born AS `出生年份`,
movie.title AS 电影标题,
movie.released AS `上映年份`8. 小结 #
8.1 核心概念 #
- 模式匹配:用模式描述想要查找的图结构
- 节点语法:
(变量名:标签 {属性}) - 关系语法:
-[变量名:类型 {属性}]-> - 模式语法:节点和关系的组合
- CREATE:创建新数据(可能重复)
- MATCH:查找已存在的数据
- MERGE:创建或匹配数据(避免重复)
8.2 语法要点总结 #
8.2.1 节点语法 #
(变量名:标签1:标签2 {属性1: 值1, 属性2: 值2})8.2.2 关系语法 #
-[变量名:关系类型 {属性1: 值1}]-> // 从左到右
<-[变量名:关系类型 {属性1: 值1}]- // 从右到左
-[变量名:关系类型 {属性1: 值1}]- // 无向8.2.3 模式语法 #
(节点1)-[关系]->(节点2) // 简单模式
(节点1)-[关系1]->(节点2)-[关系2]->(节点3) // 多跳模式
(节点1)-[*1..3]-(节点2) // 可变长度模式8.3 最佳实践 #
- 创建节点时使用变量名,方便后续引用
- 使用标签分类节点,提高查询效率
- 使用 MERGE 避免重复数据
- 使用别名提高结果可读性
- 使用 LIMIT 限制大型查询结果