Neo4j 图数据库入门(二):一篇搞定 Cypher 增删改查——在电影社交网络里从 0 建到删
本系列导航:
- 第一篇:图数据库概念——为什么是图、四大核心概念、环境搭建
- 第二篇(本篇):Cypher 增删改查全流程
- 第三篇:MERGE 防重、聚合分析、索引约束与完整实战
所有示例仍基于第一篇定义的「电影社交网络」场景。请在 Neo4j Browser(
http://localhost:7474)中按顺序执行本文代码,你会亲眼看着一张空图长成完整的电影社交网络。20/80 承诺:掌握本篇的 CREATE / MATCH / SET / DELETE,就能覆盖日常 80% 的数据操作。
0. 热身:从一张空图开始
无论你的库里有什么旧数据,先执行这条清空(顺便认识本篇最后一位主角 DELETE):
MATCH (n) DETACH DELETE n;读法:“匹配所有节点,连同它们的关系一起删掉”。现在,图是空的。
1. 增(CREATE)
1.1 创建节点
CREATE (p:Person {name: '张三', age: 30, gender: '男'})RETURN p;拆解语法:
(p:Person ...):一个节点,变量名p(仅当前语句有效),标签Person{...}:节点属性,键值对RETURN p:把创建的节点返回到画面上(可省略)
继续把电影和公司也建出来:
CREATE (m:Movie {title: '流浪地球', year: 2019, rating: 7.9})RETURN m;
CREATE (c:Company {name: '中影股份', founded: 1999})RETURN c;1.2 批量创建
一条语句创建多个节点,逗号分隔:
CREATE (p1:Person {name: '李四', age: 28}), (p2:Person {name: '王五', age: 35}), (p3:Person {name: '赵六', age: 26}), (m1:Movie {title: '三体', year: 2023, rating: 8.5}), (m2:Movie {title: '满江红', year: 2023, rating: 7.0}), (c1:Company {name: '光线传媒', founded: 1998});现在,第一篇那张”世界观”图里的 9 个节点都在了。可以跑 MATCH (n) RETURN n; 看一眼——一堆孤立的点,下一步让他们连接起来。
1.3 创建关系:先 MATCH,再 CREATE
关系连接的是已经存在的两个节点,所以套路固定为”先 MATCH 到它们,再 CREATE 关系”:
MATCH (a:Person {name: '张三'}), (b:Person {name: '李四'})CREATE (a)-[:KNOWS {since: 2018}]->(b)RETURN a, b;用同样的套路,把好友链和合作关系统统建好:
// 好友链:李四 → 王五 → 赵六,以及张三 → 王五MATCH (a:Person {name: '李四'}), (b:Person {name: '王五'})CREATE (a)-[:KNOWS {since: 2020}]->(b);
MATCH (a:Person {name: '王五'}), (b:Person {name: '赵六'})CREATE (a)-[:KNOWS {since: 2021}]->(b);
MATCH (a:Person {name: '张三'}), (b:Person {name: '王五'})CREATE (a)-[:KNOWS {since: 2019}]->(b);// 张三 出演 流浪地球,关系上带角色属性MATCH (p:Person {name: '张三'}), (m:Movie {title: '流浪地球'})CREATE (p)-[:ACTED_IN {role: '工程师', year: 2019}]->(m);
// 李四 导演 流浪地球MATCH (p:Person {name: '李四'}), (m:Movie {title: '流浪地球'})CREATE (p)-[:DIRECTED {year: 2019}]->(m);
// 赵六 出演 三体MATCH (p:Person {name: '赵六'}), (m:Movie {title: '三体'})CREATE (p)-[:ACTED_IN {role: '科学家', year: 2023}]->(m);
// 王五 就职于 中影股份MATCH (p:Person {name: '王五'}), (c:Company {name: '中影股份'})CREATE (p)-[:WORKS_AT {position: '制片人', since: 2015}]->(c);
// 张三 评价 满江红:属性挂在关系上MATCH (p:Person {name: '张三'}), (m:Movie {title: '满江红'})CREATE (p)-[:REVIEWED {score: 8, comment: '剧情紧凑'}]->(m);再执行 MATCH (n) RETURN n;——第一篇的数据模型全景图,此刻完整呈现在你的画布上。
1.4 一步到位:建节点的同时建关系
新节点和关系可以一条语句搞定:
MATCH (p:Person {name: '赵六'})CREATE (p)-[:ACTED_IN {role: '配角', year: 2023}]->(m:Movie {title: '孤注一掷', year: 2023, rating: 6.9})RETURN p, m;注意:
CREATE每次都会新建,如果这句话跑两遍,会出现两部《孤注一掷》。怎么防重?第三篇的MERGE就是干这个的。
2. 查(MATCH + RETURN)
MATCH 画模式,RETURN 取结果——第一篇预告过的”画图思维”,现在正式上手。
2.1 基础查询
// 所有人物MATCH (p:Person) RETURN p;
// 只取电影的部分属性(返回表格而不是节点)MATCH (m:Movie) RETURN m.title, m.year, m.rating;
// 精确匹配一个人物MATCH (p:Person {name: '张三'}) RETURN p;2.2 WHERE 条件过滤
{} 内联属性适合精确匹配,灵活条件交给 WHERE:
// 年龄大于 27 的人MATCH (p:Person)WHERE p.age > 27RETURN p.name, p.age;
// 组合条件:年龄大于 25 且姓张MATCH (p:Person)WHERE p.age > 25 AND p.name STARTS WITH '张'RETURN p.name, p.age;
// 模糊匹配:名字含"三"MATCH (p:Person)WHERE p.name CONTAINS '三'RETURN p.name;
// IN 列表:2019 或 2023 年的电影MATCH (m:Movie)WHERE m.year IN [2019, 2023]RETURN m.title, m.year;2.3 关系查询:顺着箭头找
// 张三认识谁?MATCH (:Person {name: '张三'})-[:KNOWS]->(friend:Person)RETURN friend.name;
// 谁导演了流浪地球?(箭头方向反过来)MATCH (director:Person)-[:DIRECTED]->(:Movie {title: '流浪地球'})RETURN director.name;
// 张三出演的电影,以及关系上的角色属性MATCH (:Person {name: '张三'})-[r:ACTED_IN]->(m:Movie)RETURN m.title, r.role, r.year;第三条示例是图模型独有的便利:ACTED_IN 关系自带 role 属性,直接取用,不需要中间表。
2.4 多跳路径查询——图数据库的高光时刻
还记得第一篇那个”五层 JOIN”的问题吗?现在正面回答它:
// 张三的朋友的朋友(2 跳)MATCH (:Person {name: '张三'})-[:KNOWS]->()-[:KNOWS]->(fof:Person)RETURN DISTINCT fof.name;DISTINCT 去重很重要——张三认识王五(1 跳直达),王五也是他的”朋友的朋友”,会被查出来两次。
可变长度路径:*1..3 表示沿 KNOWS 关系走 1 到 3 步,一行代码就是”张三的 1~3 度人脉”:
MATCH (:Person {name: '张三'})-[:KNOWS*1..3]->(contact:Person)RETURN DISTINCT contact.name;再两个进阶玩法:
// 张三到赵六之间的所有路径MATCH path = (:Person {name: '张三'})-[:KNOWS*]->(:Person {name: '赵六'})RETURN path;
// 最短路径(跨关系类型,* 不指定类型即任意关系)MATCH path = shortestPath( (:Person {name: '张三'})-[*]-(:Person {name: '赵六'}))RETURN path;在 SQL 里,这三条每条都是多层嵌套子查询;在 Cypher 里,它们只是”把箭头多画几下”。
2.5 双向查询:不指定方向
认识关系一般视为无向,把 -> 改成 - 即可:
// 与张三有 KNOWS 关系的所有人(不区分谁认识谁)MATCH (:Person {name: '张三'})-[:KNOWS]-(other:Person)RETURN other.name;3. 改(SET 与 REMOVE)
3.1 SET:修改属性
// 改一个属性MATCH (p:Person {name: '张三'})SET p.age = 31RETURN p;
// 改多个属性MATCH (p:Person {name: '张三'})SET p.age = 31, p.email = 'zhangsan@example.com'RETURN p;
// += 合并属性:只新增/覆盖列出的键,其余保留MATCH (p:Person {name: '张三'})SET p += {city: '北京', hobby: '跑步'}RETURN p;
SET p = {...}会整体替换所有属性,SET p += {...}是合并。日常批量补字段用+=更安全。
3.2 SET / REMOVE:给节点增删标签
标签可以动态增删(这在关系型数据库里相当于”把一行挪到另一张表”,图里只是一次标记变更):
// 给张三加一个 Actor 标签MATCH (p:Person {name: '张三'})SET p:ActorRETURN p;
// 再删掉MATCH (p:Person {name: '张三'})REMOVE p:ActorRETURN p;3.3 REMOVE:删除属性
MATCH (p:Person {name: '张三'})REMOVE p.emailRETURN p;4. 删(DELETE)
4.1 删除关系
// 删掉张三和李四之间的 KNOWS 关系MATCH (:Person {name: '张三'})-[r:KNOWS]->(:Person {name: '李四'})DELETE r;4.2 删除节点:DETACH DELETE(新手必踩的坑)
先试试点删赵六:
MATCH (p:Person {name: '赵六'})DELETE p;会报错 Neo.ClientError.Schema.ConstraintValidationFailed 之类的错误——因为赵六身上还连着关系(认识王五、出演三体)。Neo4j 不允许删除仍被关系引用的节点,这其实是保护机制。
正确姿势是 DETACH DELETE:先摘掉所有关系,再删节点:
MATCH (p:Person {name: '赵六'})DETACH DELETE p;4.3 清空整个数据库
// 危险操作!删除所有节点和关系MATCH (n) DETACH DELETE n;清空后别慌——第三篇的完整实战脚本会把整张电影社交网络一键重建回来。
5. CRUD 速查表
| 操作 | 语法骨架 | 记忆点 |
|---|---|---|
| 建节点 | CREATE (p:Label {props}) | 直接创建,可能重复 |
| 建关系 | MATCH (a),(b) CREATE (a)-[:TYPE]->(b) | 先 MATCH 到节点再连 |
| 查 | MATCH 模式 WHERE 条件 RETURN 结果 | 画模式 → 过滤 → 取值 |
| 多跳 | -[:TYPE*1..3]-> | 星号 + 跳数范围 |
| 改属性 | SET p.k = v / SET p += {...} | = 替换单值,+= 合并 |
| 增删标签 | SET p:X / REMOVE p:X | 标签动态可变 |
| 删属性 | REMOVE p.k | 属性和标签都用 REMOVE |
| 删关系 | DELETE r | 只删关系不影响节点 |
| 删节点 | DETACH DELETE p | 有关系的节点必须 DETACH |
小结与预告
到这里,你已经在电影社交网络里完整走了一遍增删改查:
- 增:
CREATE节点与关系,关系的套路是”先 MATCH 再 CREATE” - 查:
MATCH画模式 +WHERE过滤,多跳路径是图数据库的杀手锏 - 改:
SET改属性/标签,REMOVE删属性/标签 - 删:
DELETE删关系,DETACH DELETE删节点,清库一句话
但还有两个隐患没有解决:CREATE 会产生重复数据,查询多了怎么更快、怎么统计?下一篇《Neo4j 图数据库入门(三):从会写到写好——MERGE 防重、聚合分析、索引约束与完整实战》,我们把”能用”升级为”用好”,并用一段完整脚本重建全图,给整个系列收官。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!