【发布时间】:2019-11-13 16:29:08
【问题描述】:
我正在尝试制作一个 Cypher,它合并的边缘比我能设法放入 Cypher 语言的 ASCII 艺术中的多。
TLDR; 如何完成此合并:
MERGE (a)-[:REL1]->(b:B)-[:REL2]->(c), (b)-[:REL3]->(d)
我有这些简化的密码查询来描述问题:
// ensure required nodes exists
MATCH (a:A {id: "<uuid1>"})
MATCH (c:C {id: "<uuid2>"})
MATCH (d:D {id: "<uuid3>"})
// Make B connect the nodes
MERGE (a)-[:REL1]->(b:B)-[:REL2]->(c)
MERGE (b)-[:REL3]->(d) // <- thats the main problem - a seperate merge to make this relation, but it should be part of the first merge.
// Conclude
RETURN a,b,c,d
这个查询可以工作,但是当它被多次调用时,b 被重用了。我的意思是,这些关系中的多个是由同一个 b 组成的:(b)-:REL3->(d)。这在我的系统中是不允许的,因为我应该能够删除 b,并且只会影响第一次调用创建的内容。
为了确保 b 是唯一的,我可以这样做:
// ensure required nodes exists
MATCH (a:A {id: "<uuid1>"})
MATCH (c:C {id: "<uuid2>"})
MATCH (d:D {id: "<uuid3>"})
// ensure unique B
CREATE (b:B)
// Make B connect the nodes
MERGE (a)-[:REL1]->(b)-[:REL2]->(c)
MERGE (b)-[:REL3]->(d)
// Conclude
RETURN a,b,c,d
这个问题是,每次调用它时,都会创建一个新的 B 节点,即使路径已经存在。现在这只是重复的数据,我也不想要。
我可以通过添加 WITH/WHERE 语句来解决这个问题
// ensure required nodes exists
MATCH (a:A {id: "<uuid1>"})
MATCH (c:C {id: "<uuid2>"})
MATCH (d:D {id: "<uuid3>"})
OPTIONAL MATCH (a)-[:REL1]->(existingB:B)-[:REL2]->(c)
OPTIONAL MATCH (b)-[:REL3]->(d)
WITH a,exisingB,c,d
WHERE existingB is null // query ends here and I end up with zero rows returned
// ensure unique B
CREATE (b:B)
// Make B connect the nodes
MERGE (a)-[:REL1]->(b)-[:REL2]->(c)
MERGE (b)-[:REL3]->(d)
// Conclude
RETURN a,b,c,d
但是,现在查询不返回 a,b,c,d - 这是我想要的。
总结起来,我想要一个查询:
- 总是返回。
- 创建一个新的
b node,它结合了 a、c 和 d(如果它尚不存在)。 - 如果确实存在,请找到并返回它。
这在处理简单合并时非常简单:MATCH > MERGE > RETURN。唯一让我心烦意乱的是,我看不到如何使用单个 MERGE 命令来执行此操作。
据我所知,多个 MERGE 命令的组合是不可能的,但我希望有人对此有解决方案。
更新了现实生活中的例子
让我们从在我们的访问管理示例中创建所需的节点开始:
// create required nodes
CREATE (:Human {name:"Human A"})
CREATE (:Human {name:"Human B"})
CREATE (:Human {name:"Human C"})
CREATE (:Scope {name:"read:email"})
现在我想代表人类 B 授予人类 A 阅读:电子邮件的权限:
// grant "Human A" access to "read:email" on behalf of "Human B" - aka let Human A read Human B's email address
MATCH (humanA:Human {name:"Human A"})
MATCH (readEmail:Scope {name:"read:email"})
MATCH (humanB:Human {name:"Human B"})
MERGE (humanA)-[:IS_GRANTED]->(gr:Grant:Rule)-[:GRANTS]->(readEmail)
MERGE (gr)-[:ON_BEHALF_OF]->(humanB)
到目前为止,一切都很好。我可以重新运行查询并保持相同的确切状态。
现在我希望人类 A 也阅读:电子邮件给 Humn C。相同的查询,新的“代表”。
// grant "Human A" access to "read:email" on behalf of "Human C" - aka let Human A read Human C's email address
MATCH (humanA:Human {name:"Human A"})
MATCH (readEmail:Scope {name:"read:email"})
MATCH (humanC:Human {name:"Human C"})
MERGE (humanA)-[:IS_GRANTED]->(gr:Grant:Rule)-[:GRANTS]->(readEmail)
MERGE (gr)-[:ON_BEHALF_OF]->(humanC)
授权规则正在被重用,这是一个有多种原因的问题,但让我们只说明一个显而易见的问题:当我想删除人类 A 对人类 B 的电子邮件的访问权限时,它也会被删除给人类 C,因为他们共享同样的规则。
现在有人可能会说,为什么不先合并“代表”来避免这个问题呢? 让我们尝试重新开始,但添加另一个范围“read:phone”:
// create required nodes
CREATE (:Human {name:"Human A"})
CREATE (:Human {name:"Human B"})
CREATE (:Human {name:"Human C"})
CREATE (:Scope {name:"read:email"})
CREATE (:Scope {name:"read:phone"})
并尝试移动它:
// grant "Human A" access to "read:email" on behalf of "Human B" - aka let Human A read Human B's email address
MATCH (humanA:Human {name:"Human A"})
MATCH (readEmail:Scope {name:"read:email"})
MATCH (humanB:Human {name:"Human B"})
MERGE (humanA)-[:IS_GRANTED]->(gr:Grant:Rule)-[:ON_BEHALF_OF]->(humanB)
MERGE (gr)-[:GRANTS]->(readEmail)
就像上次一样,我们最终得到了一个正确的状态:
现在我想授予人类 A 访问 Humn B 的 read:phone 的权限:
// grant "Human A" access to "read:phone" on behalf of "Human B" - aka let Human A read Human B's phone number
MATCH (humanA:Human {name:"Human A"})
MATCH (readPhone:Scope {name:"read:phone"})
MATCH (humanB:Human {name:"Human B"})
MERGE (humanA)-[:IS_GRANTED]->(gr:Grant:Rule)-[:ON_BEHALF_OF]->(humanC)
MERGE (gr)-[:GRANTS]->(readPhone)
现在这给了我们这个:
这是不正确的。现在我只能从人类 A 到人类 B 全部或全部删除。
这很多,但我希望它能对问题提供一些见解。
【问题讨论】:
-
因此,关键是确保
b:B是唯一的,但除了REL1、REL2、REL3与@987654351 的连接之外,您没有其他方法可以确定它是否唯一@、B和C?即您没有唯一的 uuid 来查找或创建 B? -
@DaveBennett 正确,
b:B只能与REL1, REL2, REL3一起存在一次。即使我在b:B中添加了一个 uuid,我也不知道应该合并哪个,因为关系是定义它的主要内容。 -
最后一个例子,带有 read:phone 和 read:email 节点,看起来差不多。如果您需要删除特定的授予功能,您可以匹配相关模式,然后删除与 read:phone 权限的 :GRANTS 关系,检查 :Grant 节点的程度,如果没有更多,则仅删除该节点:存在 GRANTS 关系。或者,如果您只需要授予只有 1 个特权,您可以在授予节点上为此设置一个属性......并且要么让它单独确定它,要么两者兼而有之。这允许属性帮助您的 MERGE,然后您可以 MERGE 到特权。