场景
需要实现如下的场景:
传入用户的 id, user_status, username。
如果传入的 user_status 不在 1,2,3 中,则不做任何执行;
如果状态在 1,2,3中,根据 id 匹配,找到就更新,找不到就创建。
neo4j 版本:v5.12.0,不同版本可能不同。需要实际验证。
正确的写法
方式1
满足条件的
1)第一次执行
WITH 1 AS id, 1 AS status, '用户1' AS username
WHERE status IN [1, 2, 3]
MERGE (u:User {id: id}) SET u.status = status, u.username = username, u.create_time=timestamp()
RETURN u;
结果:
╒════════════════════════════════════════════════════════════════════╕
│u │
╞════════════════════════════════════════════════════════════════════╡
│(:User {create_time: 1712917794838,id: 1,username: "用户1",status: 1})│
└────────────────────────────────────────────────────────────────────┘
2)再次执行
则发现可以更新执行时间。
╒════════════════════════════════════════════════════════════════════╕
│u │
╞════════════════════════════════════════════════════════════════════╡
│(:User {create_time: 1712917816646,id: 1,username: "用户1",status: 1})│
└────────────────────────────────────────────────────────────────────┘
不满条件的
1) 已存在的 id
已经存在的 id=1. 不满足的状态 status=9
WITH 1 AS id, 9 AS status, '用户1' AS username
WHERE status IN [1, 2, 3]
MERGE (u:User {id: id}) SET u.status = status, u.username = username, u.create_time=timestamp()
RETURN u;
无任何变化:
(no changes, no records)
2) 不存在的 id
WITH 99 AS id, 9 AS status, '用户1' AS username
WHERE status IN [1, 2, 3]
MERGE (u:User {id: id}) SET u.status = status, u.username = username, u.create_time=timestamp()
RETURN u;
结果:
(no changes, no records)
符合预期。
总体而言这种写法比较优雅,下面的方法也行,感兴趣可以继续阅读。
调整的写法
也可以调整如下:
WITH 2 AS status
WHERE status IN [1, 2, 3]
MERGE (u:User {id: 72}) SET u.status = status, u.username = 'u-72', u.create_time=timestamp()
RETURN u;
只把需要处理的状态提上去,其他的正常写。
方式2
满足条件的状态
1)第一次执行
WITH 2 AS id, 1 AS user_status, '用户2' AS username
WHERE user_status IN [1, 2, 3]
MERGE (n:User {id: id})
ON CREATE SET n.username = username, n.user_status = user_status, n.create_time=timestamp()
ON MATCH SET n.username = username, n.user_status = user_status, n.create_time=timestamp()
RETURN n
再次执行:
WITH 2 AS id, 1 AS user_status, '用户2' AS username
WHERE user_status IN [1, 2, 3]
MERGE (n:User {id: id})
ON CREATE SET n.username = username, n.user_status = user_status, n.create_time=timestamp()
ON MATCH SET n.username = username, n.user_status = user_status, n.create_time=timestamp()
RETURN n
对应的时间发生更新。符合预期。
不满足条件的状态
1)不存在的 id 和状态。
WITH 3 AS id, 9 AS user_status, '用户3' AS username
WHERE user_status IN [1, 2, 3]
MERGE (n:User {id: id})
ON CREATE SET n.username = username, n.user_status = user_status, n.create_time=timestamp()
ON MATCH SET n.username = username, n.user_status = user_status, n.create_time=timestamp()
RETURN n
此时没有任何变化。
(no changes, no records)
符合预期。
2)不符合条件的状态+已经存在的 id 呢?
id=2 的数据,我们前面已经创建了。
╒══════════════════════════════════════════════════════════════════════╕
│n │
╞══════════════════════════════════════════════════════════════════════╡
│(:User {user_status: 1,create_time: 1712917401770,id: 2,username: "用户2│
│"}) │
└──────────────────────────────────────────────────────────────────────┘
执行:
WITH 2 AS id, 9 AS user_status, '用户3' AS username
WHERE user_status IN [1, 2, 3]
MERGE (n:User {id: id})
ON CREATE SET n.username = username, n.user_status = user_status, n.create_time=timestamp()
ON MATCH SET n.username = username, n.user_status = user_status, n.create_time=timestamp()
RETURN n
结果:
(no changes, no records)
符合预期。
失败的尝试。
节点存在时更新,不存在时创建
语句满足一定条件时才执行,不满足时不执行。
MERGE (p:User {id: 1})
WHERE p.user_status = '1' OR p.user_status = '2' OR p.user_status = '3'
SET p.username = '新的用户名', p.user_status = '1';
on create
这个实际上是一个限定条件,表达的是当创建的时候(匹配不到时)才执行,不创建就不执行:
1)第一次不存在
如果不存在 id=2 的数据
merge (c:User{id:2})
on create set c.username = '用户名', c.id = 2
return c
2) 如果存在的时候,则不执行:
merge (c:User{id:2})
on create set c.username = '用户名-2', c.id = 2
return c
on match
这个命令和上述表达差不多,不同的是它是匹配上了就进行set
1) 匹配上
merge (c:User{id:2})
on MATCH set c.username = '用户名-match'
return c
2) 没有匹配的
这个时候发现结果是直接 merge 了一个 id=3 的实体。
merge (c:User{id:3})
on MATCH set c.username = '用户名-match-3'
return c
二者合并
1) 没有的时候
MERGE (c:User {id: 4, status: [1,2,3]})
ON CREATE SET c.username = '用户名-创建-4', c.status=1
ON MATCH SET c.username = '用户名-match-4', c.status=1
RETURN c;
原始模板:
MERGE (u:User {id: $userId, user_status IN [过滤条件列表,可以改成其他的]})
ON CREATE SET u.username = $username, u.user_status = $user_status
ON MATCH SET u.username = $username, u.user_status = $user_status
报错场景
实际测试
MERGE (u:User {id: 5})
WHERE 1 IN [1,2,3]
ON CREATE SET u.username = '用户5-create', u.user_status = 1
ON MATCH SET u.username = '用户5-update', u.user_status = 1
报错:
Invalid input 'WHERE':
expected
"("
"CALL"
"CREATE"
"DELETE"
"DETACH"
"FOREACH"
"LOAD"
"MATCH"
"MERGE"
"ON"
"OPTIONAL"
"REMOVE"
"RETURN"
"SET"
"UNION"
"UNWIND"
"USE"
"WITH"
<EOF> (line 2, column 1 (offset: 25))
"WHERE 1 IN [1,2,3]"
插入上下文替换后,假设 userId=5,username=’用户5’, user_status=1
第一次尝试
1) 没有的场景
MERGE (u:User {id: 5, user_status: ['1','2','3']})
ON CREATE SET u.username = '用户5-create', u.user_status = '1'
ON MATCH SET u.username = '用户5-update', u.user_status = '1'
RETURN u;
数据:
╒═════════════════════════════════════════════════════╕
│n │
╞═════════════════════════════════════════════════════╡
│(:User {user_status: 1,id: 5,username: "用户5-create"})│
└─────────────────────────────────────────────────────┘
2) 已经存在的场景
MERGE (u:User {id: 5, user_status IN [1,2,3]})
ON CREATE SET u.username = '用户5-create', u.user_status = 1
ON MATCH SET u.username = '用户5-update', u.user_status = 1
RETURN u;
发现第二次没有执行。
猜测是直接把 user_status 当做值为 [1,2,3]
小结
merge 可以考虑和 unwind 批量操作,这样会方便很多。