1. 什么是CRUD操作 #
在数据库操作中,CRUD是最基本的四个操作:
- Create(创建):创建新的数据
- Read(读取):查询和读取数据
- Update(更新):修改现有数据
- Delete(删除):删除数据
在图数据库中,CRUD操作有一些特殊之处:
- 创建:可以创建节点和关系,以及它们的属性
- 更新:可以更新节点和关系的属性,也可以添加或删除标签
- 删除:可以删除节点、关系或属性,但需要注意节点和关系之间的依赖关系
2. 创建数据(CREATE) #
创建数据是数据库操作的第一步。在Cypher中,使用CREATE关键字来创建节点和关系。
2.1 CREATE命令的基本语法 #
CREATE命令用于创建新的节点和关系。它的基本语法是:
- 创建节点:
CREATE (变量名:标签 {属性}) - 创建关系:
CREATE (节点1)-[:关系类型 {属性}]->(节点2)
2.2 创建节点 #
创建节点是最基本的操作。可以创建带标签和属性的节点。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建一个简单的演员节点
CREATE (a:Actor {name: "周星驰"})
// 查询创建的节点
MATCH (a:Actor)
RETURN a
// 创建电影节点
CREATE (m:Movie {title: "功夫"})
// 查询所有节点
MATCH (n)
RETURN n2.3 创建带多个属性的节点 #
节点可以有多个属性,用于存储更多信息。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建带多个属性的演员节点
CREATE (a:Actor {
name: "周星驰",
born: 1962,
birthplace: "香港",
nickname: "星爷"
})
// 查询节点及其属性
MATCH (a:Actor)
RETURN a.name, a.born, a.birthplace, a.nickname
// 创建多个带属性的节点
CREATE (a1:Actor {name: "吴孟达", born: 1952, birthplace: "福建厦门"}),
(a2:Actor {name: "朱茵", born: 1971, birthplace: "香港"})
// 查询所有演员
MATCH (a:Actor)
RETURN a.name, a.born, a.birthplace2.4 创建关系 #
关系连接两个节点,也可以有自己的属性。
// 清空数据库
MATCH (n) DETACH DELETE n
// 先创建节点
CREATE (a1:Actor {name: "周星驰"}),
(a2:Actor {name: "吴孟达"}),
(m1:Movie {title: "功夫", released: 2004})
// 查询插入后的节点(还没有关系)
MATCH (n)
RETURN n
// 查询初始关系(应该为空)
MATCH (a:Actor)-[r]->(target)
RETURN count(r) AS initial_relationship_count
// 创建关系(无属性)- 合作关系
MATCH (a1:Actor {name: "周星驰"}),
(a2:Actor {name: "吴孟达"})
CREATE (a1)-[:CO_STARRED_WITH]->(a2)
// 查询创建关系后的状态
MATCH (a:Actor)-[r]->(target)
RETURN a.name, type(r), target.name
// 创建带属性的出演关系
MATCH (a1:Actor {name: "周星驰"}),
(m1:Movie {title: "功夫"})
CREATE (a1)-[:ACTED_IN {roles: ["阿星"], year: 2004}]->(m1)
// 查询所有节点和关系
MATCH (a:Actor)-[r]->(target)
RETURN a.name, type(r), target.title, r
// 查询出演关系及其属性
MATCH (a:Actor)-[r:ACTED_IN]->(m:Movie)
RETURN a.name, r.roles, r.year, m.title2.5 创建复杂图结构 #
可以一次性创建多个节点和关系,构建复杂的图结构。
// 清空数据库
MATCH (n) DETACH DELETE n
// 一次性创建多个节点和关系(周星驰电影宇宙)
CREATE (zxc:Actor {name: "周星驰", born: 1962}),
(wmd:Actor {name: "吴孟达", born: 1952}),
(zy:Actor {name: "朱茵", born: 1971}),
(m1:Movie {title: "大话西游", released: 1995}),
(m2:Movie {title: "喜剧之王", released: 1999}),
(zxc)-[:CO_STARRED_WITH]->(wmd),
(zxc)-[:CO_STARRED_WITH]->(zy),
(zxc)-[:ACTED_IN {roles: ["至尊宝"]}]->(m1),
(zy)-[:ACTED_IN {roles: ["紫霞仙子"]}]->(m1),
(zxc)-[:ACTED_IN {roles: ["尹天仇"]}]->(m2)
// 查询完整的图结构
MATCH (a:Actor)-[r]->(target)
RETURN a.name, type(r), COALESCE(target.name, target.title) AS target
// 查询演员合作关系网络
MATCH (a1:Actor)-[:CO_STARRED_WITH]->(a2:Actor)
RETURN a1.name, a2.name
// 查询出演关系
MATCH (a:Actor)-[r:ACTED_IN]->(m:Movie)
RETURN a.name, m.title, r.roles3. 更新数据(SET) #
更新数据是修改现有节点或关系属性的操作。在Cypher中,使用SET关键字来更新属性。
3.1 前置知识:SET命令 #
SET命令用于设置或更新节点和关系的属性。它的基本语法是:
- 更新节点属性:
SET 节点变量.属性名 = 值 - 更新关系属性:
SET 关系变量.属性名 = 值
3.2 更新节点属性 #
可以更新节点的单个或多个属性。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (a:Actor {name: "周星驰", born: 1962})
// 查询插入后的初始数据
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born
// 更新单个属性
MATCH (a:Actor {name: "周星驰"})
SET a.nickname = "星爷"
RETURN a
// 查询更新后的节点
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born, a.nickname
// 更新另一个属性
MATCH (a:Actor {name: "周星驰"})
SET a.birthplace = "香港"
RETURN a.name, a.born, a.birthplace
// 查询最终状态
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born, a.nickname, a.birthplace3.3 更新多个属性 #
可以一次更新多个属性,用逗号分隔。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (a:Actor {name: "周星驰", born: 1962})
// 查询插入后的初始数据
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born
// 一次更新多个属性
MATCH (a:Actor {name: "周星驰"})
SET a.nickname = "星爷", a.birthplace = "香港", a.profession = "演员/导演"
RETURN a
// 查询更新后的所有属性
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born, a.nickname, a.birthplace, a.profession
// 创建更多测试数据
CREATE (a2:Actor {name: "吴孟达", born: 1952})
// 更新多个节点的属性
MATCH (a:Actor)
SET a.updated = true, a.update_time = timestamp()
RETURN a.name, a.updated, a.update_time3.4 使用表达式更新属性 #
可以使用表达式来计算新的属性值。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据(电影节点)
CREATE (m:Movie {title: "功夫", released: 2004, budget: 20000000})
// 查询插入后的初始数据
MATCH (m:Movie {title: "功夫"})
RETURN m.title, m.released, m.budget
// 使用表达式更新属性(计算距今年数)
MATCH (m:Movie {title: "功夫"})
SET m.years_ago = 2024 - m.released
RETURN m.title, m.years_ago
// 查询更新后的年数
MATCH (m:Movie {title: "功夫"})
RETURN m.title, m.released, m.years_ago
// 使用表达式计算预算(美元转人民币,假设汇率7)
MATCH (m:Movie {title: "功夫"})
SET m.budget_cny = m.budget * 7
RETURN m.title, m.budget, m.budget_cny
// 使用字符串拼接
MATCH (m:Movie {title: "功夫"})
SET m.display_name = m.title + " (" + toString(m.released) + ")"
RETURN m.display_name
// 使用条件表达式判断电影时代
MATCH (m:Movie {title: "功夫"})
SET m.era = CASE
WHEN m.released < 1990 THEN "80年代"
WHEN m.released < 2000 THEN "90年代"
WHEN m.released < 2010 THEN "00年代"
ELSE "10年代后"
END
RETURN m.title, m.released, m.era
// 查询最终状态
MATCH (m:Movie {title: "功夫"})
RETURN m.title, m.released, m.budget, m.budget_cny, m.display_name, m.era3.5 更新关系属性 #
关系的属性也可以使用SET来更新。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (a:Actor {name: "周星驰"}),
(m:Movie {title: "少林足球", released: 2001}),
(a)-[:ACTED_IN {roles: ["五师兄"], year: 2001}]->(m)
// 查询插入后的初始关系数据
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
RETURN a.name, r.roles, r.year, m.title
// 更新关系属性
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
SET r.award = "最佳导演"
RETURN a.name, r.roles, r.year, r.award, m.title
// 查询更新后的关系
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
RETURN a.name, r.roles, r.year, r.award, m.title
// 更新关系的多个属性
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
SET r.roles = ["五师兄/星"], r.is_lead = true, r.box_office = 60000000
RETURN a.name, r.roles, r.is_lead, r.box_office, m.title
// 使用表达式更新关系属性
MATCH (a:Actor)-[r:ACTED_IN]->(m:Movie)
SET r.years_ago = 2024 - r.year
RETURN a.name, r.year, r.years_ago, m.title
// 查询最终状态
MATCH (a:Actor)-[r:ACTED_IN]->(m:Movie)
RETURN a.name, r.roles, r.year, r.award, r.box_office, r.years_ago, m.title3.6 添加和移除标签 #
标签是节点的分类标识,可以使用SET添加标签,使用REMOVE移除标签。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (a:Actor {name: "周星驰", born: 1962})
// 查询插入后的初始标签
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, labels(a)
// 添加标签(周星驰也是导演)
MATCH (a:Actor {name: "周星驰"})
SET a:Director
RETURN labels(a), a
// 查询添加标签后的状态
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, labels(a)
// 添加多个标签(周星驰还是编剧和制片人)
MATCH (a:Actor {name: "周星驰"})
SET a:Writer:Producer
RETURN labels(a), a
// 查询节点的所有标签
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, labels(a)
// 移除标签
MATCH (a:Actor {name: "周星驰"})
REMOVE a:Producer
RETURN labels(a), a
// 查询移除标签后的状态
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, labels(a)
// 移除多个标签
MATCH (a:Actor {name: "周星驰"})
REMOVE a:Writer
RETURN labels(a), a
// 查看最终标签(应该是 Actor 和 Director)
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, labels(a)4. 删除数据(DELETE/REMOVE) #
删除数据是移除节点、关系或属性的操作。在Cypher中,使用DELETE删除节点和关系,使用REMOVE删除属性。
4.1 前置知识:DELETE和REMOVE的区别 #
- DELETE:用于删除节点和关系
- REMOVE:用于删除属性和标签
4.2 删除关系 #
删除关系时,只删除关系本身,不删除关系两端的节点。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (a1:Actor {name: "周星驰"}),
(a2:Actor {name: "吴孟达"}),
(m1:Movie {title: "功夫", released: 2004}),
(a1)-[:CO_STARRED_WITH]->(a2),
(a1)-[:ACTED_IN]->(m1)
// 查询删除前的关系
MATCH (a:Actor)-[r]->(target)
RETURN a.name, type(r), COALESCE(target.name, target.title) AS target
// 删除特定关系(合作关系)
MATCH (a1:Actor {name: "周星驰"})-[r:CO_STARRED_WITH]->(a2:Actor)
DELETE r
// 查询删除后的关系(合作关系已删除,出演关系还在)
MATCH (a:Actor)-[r]->(target)
RETURN a.name, type(r), COALESCE(target.name, target.title) AS target
// 删除所有出演关系
MATCH ()-[r:ACTED_IN]->()
DELETE r
// 确认节点仍然存在
MATCH (n)
RETURN n4.3 删除节点(DELETE) #
使用DELETE删除节点时,只能删除没有关系的节点。如果节点有关系,必须先删除关系。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (a1:Actor {name: "周星驰"}),
(a2:Actor {name: "吴孟达"}),
(a3:Actor {name: "朱茵"}),
(a1)-[:CO_STARRED_WITH]->(a2)
// 查询插入后的初始数据
MATCH (n)
RETURN n
// 查询节点和关系
MATCH (a:Actor)-[r]->(target)
RETURN a.name, type(r), target.name
// 删除没有关系的节点(可以成功)
MATCH (a:Actor {name: "朱茵"})
DELETE a
// 查询剩余节点
MATCH (n)
RETURN n
// 尝试删除有关系的节点(会失败)
// MATCH (a:Actor {name: "周星驰"})
// DELETE a
// 错误:Cannot delete node<id>, because it still has relationships.
// 先删除关系,再删除节点
MATCH (a1:Actor {name: "周星驰"})-[r:CO_STARRED_WITH]->(a2:Actor)
DELETE r
// 查询删除关系后的状态
MATCH (n)
RETURN n
MATCH (a:Actor {name: "周星驰"})
DELETE a
// 查询最终结果
MATCH (n)
RETURN n4.4 删除节点及其关系(DETACH DELETE) #
如果要删除一个节点及其所有关系,应该使用DETACH DELETE。这是删除节点的推荐方式。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (a1:Actor {name: "周星驰"}),
(a2:Actor {name: "吴孟达"}),
(a3:Actor {name: "朱茵"}),
(m1:Movie {title: "大话西游", released: 1995}),
(a1)-[:CO_STARRED_WITH]->(a2),
(a1)-[:CO_STARRED_WITH]->(a3),
(a1)-[:ACTED_IN]->(m1)
// 查询删除前的图结构
MATCH (a:Actor)-[r]->(target)
RETURN a.name, type(r), COALESCE(target.name, target.title) AS target
// 使用 DETACH DELETE 删除节点及其所有关系
MATCH (a:Actor {name: "周星驰"})
DETACH DELETE a
// 查询删除后的结果(周星驰及其所有关系都被删除)
MATCH (n)
RETURN n
// 删除所有节点及其关系
MATCH (n)
DETACH DELETE n
// 确认数据库已清空
MATCH (n)
RETURN count(n) AS node_count4.5 删除属性(REMOVE) #
可以使用REMOVE删除节点或关系的属性。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (a:Actor {name: "周星驰", born: 1962, birthplace: "香港", nickname: "星爷"}),
(m:Movie {title: "功夫", released: 2004}),
(a)-[:ACTED_IN {roles: ["阿星"], year: 2004, award: "最佳导演"}]->(m)
// 查询插入后的初始节点属性
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born, a.birthplace, a.nickname
// 查询插入后的初始关系属性
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
RETURN a.name, r.roles, r.year, r.award, m.title
// 删除节点的单个属性
MATCH (a:Actor {name: "周星驰"})
REMOVE a.nickname
RETURN a
// 查询删除后的属性
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born, a.birthplace, a.nickname
// 删除节点的多个属性
MATCH (a:Actor {name: "周星驰"})
REMOVE a.birthplace, a.born
RETURN a
// 查询删除多个属性后的状态
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born, a.birthplace, a.nickname
// 删除关系属性
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
REMOVE r.award
RETURN a.name, r.roles, r.year, r.award, m.title
// 查询删除关系属性后的状态
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
RETURN a.name, r.roles, r.year, r.award, m.title
// 删除关系的多个属性
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
REMOVE r.roles, r.year
RETURN a.name, r, m.title
// 查询最终状态
MATCH (a:Actor {name: "周星驰"})-[r:ACTED_IN]->(m:Movie)
RETURN a.name, r, m.title5. 避免重复数据(MERGE) #
MERGE是Cypher中非常重要的命令,它确保数据存在(不存在时创建),避免重复数据。
5.1 前置知识:为什么需要MERGE? #
在使用CREATE创建数据时,如果多次执行相同的CREATE语句,会创建重复的节点。MERGE可以解决这个问题:
- 如果数据不存在:创建新数据
- 如果数据已存在:匹配现有数据(不创建重复数据)
5.2 MERGE节点 #
使用MERGE可以确保节点只创建一次。
// 清空数据库
MATCH (n) DETACH DELETE n
// 查询初始状态(应该为空)
MATCH (a:Actor)
RETURN count(a) AS initial_count
// 第一次执行 MERGE:节点不存在,会创建
MERGE (a:Actor {name: "周星驰", born: 1962})
RETURN a
// 查询第一次 MERGE 后的节点
MATCH (a:Actor)
RETURN a.name, a.born, count(a) AS count
// 第二次执行相同的 MERGE:节点已存在,不会创建重复节点
MERGE (a:Actor {name: "周星驰", born: 1962})
RETURN a
// 查询节点(只有一个节点,证明没有重复创建)
MATCH (a:Actor)
RETURN a.name, a.born, count(a) AS count
// 对比:使用 CREATE 会创建重复节点
CREATE (a:Actor {name: "吴孟达", born: 1952})
CREATE (a:Actor {name: "吴孟达", born: 1952})
// 查询(会有两个重复的节点)
MATCH (a:Actor {name: "吴孟达"})
RETURN a.name, a.born, count(a) AS count
// 使用 MERGE 避免重复
MERGE (a:Actor {name: "朱茵", born: 1971})
MERGE (a:Actor {name: "朱茵", born: 1971})
// 查询(只有一个节点)
MATCH (a:Actor {name: "朱茵"})
RETURN a.name, a.born, count(a) AS count
// 查询所有节点
MATCH (a:Actor)
RETURN a.name, a.born5.3 MERGE关系 #
也可以使用MERGE来确保关系只创建一次。
// 清空数据库
MATCH (n) DETACH DELETE n
// 先创建节点
CREATE (a1:Actor {name: "周星驰"}),
(a2:Actor {name: "吴孟达"}),
(m1:Movie {title: "功夫", released: 2004})
// 查询插入后的初始节点
MATCH (n)
RETURN n
// 查询初始关系(应该为空)
MATCH (a1:Actor {name: "周星驰"})-[r:CO_STARRED_WITH]->(a2:Actor {name: "吴孟达"})
RETURN count(r) AS initial_relationship_count
// 使用 MERGE 创建关系(第一次)
MATCH (a1:Actor {name: "周星驰"}),
(a2:Actor {name: "吴孟达"})
MERGE (a1)-[:CO_STARRED_WITH]->(a2)
RETURN a1.name, a2.name
// 查询第一次 MERGE 后的关系
MATCH (a1:Actor {name: "周星驰"})-[r:CO_STARRED_WITH]->(a2:Actor {name: "吴孟达"})
RETURN count(r) AS relationship_count
// 再次执行相同的 MERGE(不会创建重复关系)
MATCH (a1:Actor {name: "周星驰"}),
(a2:Actor {name: "吴孟达"})
MERGE (a1)-[:CO_STARRED_WITH]->(a2)
RETURN a1.name, a2.name
// 查询关系(只有一条,证明没有重复创建)
MATCH (a1:Actor {name: "周星驰"})-[r:CO_STARRED_WITH]->(a2:Actor {name: "吴孟达"})
RETURN count(r) AS relationship_count
// MERGE 节点和关系一起
MERGE (a:Actor {name: "朱茵", born: 1971})
MERGE (m:Movie {title: "大话西游", released: 1995})
MERGE (a)-[:ACTED_IN]->(m)
RETURN a.name, m.title
// 查询 MERGE 后的节点和关系
MATCH (a:Actor {name: "朱茵"})-[r:ACTED_IN]->(m:Movie {title: "大话西游"})
RETURN a.name, m.title, count(r) AS relationship_count
// 再次执行(不会创建重复)
MERGE (a:Actor {name: "朱茵", born: 1971})
MERGE (m:Movie {title: "大话西游", released: 1995})
MERGE (a)-[:ACTED_IN]->(m)
RETURN a.name, m.title
// 查询最终状态(关系数量应该不变)
MATCH (a:Actor {name: "朱茵"})-[r:ACTED_IN]->(m:Movie {title: "大话西游"})
RETURN a.name, m.title, count(r) AS relationship_count5.4 ON CREATE SET 和 ON MATCH SET #
可以在MERGE时使用ON CREATE SET和ON MATCH SET来分别处理创建和匹配的情况。
// 清空数据库
MATCH (n) DETACH DELETE n
// 查询初始状态(应该为空)
MATCH (m:Movie {title: "功夫"})
RETURN count(m) AS initial_count
// 第一次 MERGE:节点不存在,执行 ON CREATE SET
MERGE (m:Movie {title: "功夫"})
ON CREATE SET m.released = 2004, m.created_at = timestamp(), m.view_count = 1
ON MATCH SET m.view_count = m.view_count + 1, m.last_viewed = timestamp()
RETURN m
// 查询第一次 MERGE 后的节点(应该执行了 ON CREATE SET)
MATCH (m:Movie {title: "功夫"})
RETURN m.title, m.released, m.view_count, m.created_at, m.last_viewed
// 第二次 MERGE:节点已存在,执行 ON MATCH SET
MERGE (m:Movie {title: "功夫"})
ON CREATE SET m.released = 2004, m.created_at = timestamp(), m.view_count = 1
ON MATCH SET m.view_count = m.view_count + 1, m.last_viewed = timestamp()
RETURN m
// 查询节点(view_count 应该增加了)
MATCH (m:Movie {title: "功夫"})
RETURN m.title, m.released, m.view_count, m.created_at, m.last_viewed
// 再次执行 MERGE
MERGE (m:Movie {title: "功夫"})
ON CREATE SET m.released = 2004, m.created_at = timestamp(), m.view_count = 1
ON MATCH SET m.view_count = m.view_count + 1, m.last_viewed = timestamp()
RETURN m.view_count
// 查看最终结果
MATCH (m:Movie {title: "功夫"})
RETURN m.title, m.view_count, m.created_at, m.last_viewed5.5 MERGE最佳实践 #
使用MERGE时,应该使用唯一标识符(如ID、特定属性组合等)作为匹配条件。
// 清空数据库
MATCH (n) DETACH DELETE n
// 查询初始状态
MATCH (n)
RETURN count(n) AS initial_count
// 最佳实践:使用唯一标识符(title + released)作为匹配条件
MERGE (m:Movie {title: "少林足球", released: 2001})
ON CREATE SET m.director = "周星驰", m.created_at = timestamp()
ON MATCH SET m.last_updated = timestamp()
RETURN m
// 查询 MERGE 后的节点
MATCH (m:Movie {title: "少林足球"})
RETURN m.title, m.released, m.director, m.created_at, m.last_updated
// 最佳实践:使用 ID 作为唯一标识符
MERGE (a:Actor {id: "ACTOR001"})
ON CREATE SET a.name = "周星驰", a.born = 1962, a.created_at = timestamp()
RETURN a
// 查询 MERGE 后的演员
MATCH (a:Actor {id: "ACTOR001"})
RETURN a.name, a.id, a.born, a.created_at
// ❌ 不推荐:使用非唯一属性(name)作为匹配条件,可能创建重复节点
MERGE (a:Actor {name: "周星驰"})
RETURN a
// 查询使用 name 匹配的节点
MATCH (a:Actor {name: "周星驰"})
RETURN a.name, a.born, count(a) AS count
// 推荐:组合唯一属性
MERGE (a:Actor {name: "周星驰", born: 1962})
ON CREATE SET a.nickname = "星爷"
RETURN a
// 查询组合唯一属性的节点
MATCH (a:Actor {name: "周星驰", born: 1962})
RETURN a.name, a.born, a.nickname
// 最佳实践:MERGE 节点和关系时,确保节点已存在或使用 MERGE
MERGE (a:Actor {name: "吴孟达", born: 1952})
ON CREATE SET a.nickname = "达叔"
MERGE (m:Movie {title: "少林足球", released: 2001})
ON CREATE SET m.director = "周星驰"
MERGE (a)-[:ACTED_IN {roles: ["黄金右脚"]}]->(m)
RETURN a.name, m.title
// 查询结果
MATCH (a:Actor)-[r:ACTED_IN]->(m:Movie)
RETURN a.name, m.title, r.roles
// 查询最终状态
MATCH (n)
RETURN labels(n), count(n) AS count6. 小结 #
6.1 核心概念回顾 #
介绍了Cypher中数据创建、更新和删除的核心操作:
| 操作类型 | 命令 | 说明 | 示例 |
|---|---|---|---|
| 创建节点 | CREATE (n:Label {property: value}) |
创建新节点 | CREATE (a:Actor {name: '周星驰'}) |
| 创建关系 | CREATE (a)-[:REL]->(b) |
创建新关系 | CREATE (a)-[:ACTED_IN]->(m) |
| 更新属性 | SET n.property = value |
更新节点或关系的属性 | SET m.released = 2004 |
| 删除关系 | DELETE r |
删除关系 | DELETE r |
| 删除节点 | DELETE n 或 DETACH DELETE n |
删除节点 | DETACH DELETE n |
| 删除属性 | REMOVE n.property |
删除属性 | REMOVE a.nickname |
| 避免重复 | MERGE (n:Label {id: value}) |
存在则匹配,不存在则创建 | MERGE (m:Movie {title: '功夫'}) |
6.2 CREATE vs MERGE 对比 #
| 特性 | CREATE | MERGE |
|---|---|---|
| 行为 | 总是创建新数据 | 存在则匹配,不存在则创建 |
| 重复数据 | 可能创建重复数据 | 避免重复数据 |
| 使用场景 | 确定要创建新数据时 | 避免重复数据时 |
| 性能 | 较快 | 较慢(需要检查是否存在) |
6.3 DELETE vs DETACH DELETE 对比 #
| 特性 | DELETE | DETACH DELETE |
|---|---|---|
| 删除节点 | 只能删除无关系的节点 | 删除节点及其所有关系 |
| 删除关系 | 可以删除关系 | 不单独删除关系 |
| 安全性 | 如果节点有关系会失败 | 总是成功 |
| 使用场景 | 确定节点没有关系时 | 删除节点及其所有关系时(推荐) |
6.4 最佳实践 #
创建数据
- 使用CREATE创建确定要新建的数据
- 使用MERGE避免重复数据
- 在MERGE中使用唯一标识符(如email、ID)
更新数据
- 使用SET更新属性
- 可以一次更新多个属性
- 使用表达式计算新值
删除数据
- 使用DETACH DELETE删除节点(更安全)
- 删除前先查询确认
- 使用REMOVE删除属性
避免重复
- 使用MERGE避免重复创建
- 在MERGE中使用唯一标识符
- 使用ON CREATE和ON MATCH处理不同情况
6.5 常见错误和解决方案 #
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 无法删除有关系的节点 | 节点仍有关联关系 | 使用DETACH DELETE |
| MERGE创建了重复节点 | 匹配条件不唯一 | 使用唯一标识符(如email、ID) |
| 更新属性失败 | 节点不存在 | 先使用MATCH确认节点存在,或使用MERGE |
| CREATE创建了重复数据 | 多次执行相同的CREATE | 使用MERGE代替CREATE |