Las*_*sse 6 merge neo4j cypher
我正在尝试制作一个合并了更多边缘的Cypher,这比我在Cypher语言的ASCII艺术中无法做到的要多。
TLDR; 如何完成合并:
MERGE (a)-[:REL1]->(b:B)-[:REL2]->(c), (b)-[:REL3]->(d)
Run Code Online (Sandbox Code Playgroud)
我有这些简化的密码查询来描述问题:
// 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
Run Code Online (Sandbox Code Playgroud)
该查询将起作用,但是当多次调用它时,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
Run Code Online (Sandbox Code Playgroud)
这个问题是,即使路径已经存在,每次调用它时,都会创建一个新的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
Run Code Online (Sandbox Code Playgroud)
但是,现在查询不返回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"})
Run Code Online (Sandbox Code Playgroud)
现在,我要授予人员A代表人员B读取read:email的权限:
// 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)
Run Code Online (Sandbox Code Playgroud)
到目前为止,一切都很好。我可以重新运行查询,并保留相同的确切状态。
现在,我希望人类A已阅读:也请发送电子邮件给HuamnC。同样的查询,新的“代表”。
// 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)
Run Code Online (Sandbox Code Playgroud)
授予规则正在被重用,这是一个有多种原因的问题,但仅说明一个显而易见的问题:当我要删除人员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"})
Run Code Online (Sandbox Code Playgroud)
并尝试移动它:
// 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)
Run Code Online (Sandbox Code Playgroud)
就像上次一样,我们最终得到一个正确的状态:
现在,我想授予人类A访问Huamn 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)
Run Code Online (Sandbox Code Playgroud)
现在,这给了我们:
不对 现在,我只能删除人类A到人类B的全部或全部。
数量很多,但我希望它能为问题提供一些见识。
[更新(两次)]
这个技巧可能对你的“三足合并”(创造一个术语)有用。您问题中的第二个插图显示了 3 条腿合并所需结果的示例,其中给定Scope
节点与 3 个特定节点且仅与这 3 个节点有关系。
技巧是这样的:向每个属性添加 2 个属性(或 3 个,请参阅下面的注释),以Grant
唯一标识关联的,以及授权所代表的Scope
关联。Human
如果您还与实际节点Scope
和Human
节点有关系,那么这无疑是多余的信息,但它应该确保您可以使用它为每组唯一的 3 条腿MERGE
创建唯一的节点。Grant
例如,要正确执行第二个查询(在您的更新中),假设name
值是唯一的:
MATCH (humanA:Human {name:"Human A"})
MATCH (readEmail:Scope {name:"read:email"})
MATCH (humanC:Human {name:"Human C"})
MERGE (humanA)-[:IS_GRANTED]->(g:Grant:Rule {for: humanC.name, scope: readEmail.name})
MERGE (g)-[:ON_BEHALF_OF]->(humanC)
MERGE (g)-[:GRANTS]->(readEmail)
Run Code Online (Sandbox Code Playgroud)
笔记:
第一个MERGE
确保g
节点仅与“Human A”相关联,因此不需要添加第三个属性来g
具有“Human A”的唯一标识符——当且仅当您始终以以下方式启动 3-legged-merge:的IS_GRANTED
关系。
但是,如果您有时可以创建Grant
以其他“腿”之一开始的节点,那么您需要为每条腿都有一个属性。
Grant
即使在创建关联关系之后,您也必须保留这些属性,以便将来的 3 足合并能够正常工作。
严格来说,您实际上不需要执行最后 2MERGE
秒中的任何一个,因为Grant
节点将包含足够的信息来根据需要动态获取缺失的(虚拟)支路。例如,要获取Scope
代表“人类 C”的涉及“人类 A”的 s:
MATCH
(:Human {name:"Human A"})-[:IS_GRANTED]->(g {for: "Human C"}),
(scope:Scope {name: g.scope})
RETURN scope
Run Code Online (Sandbox Code Playgroud)
这比拥有实际关系的效率要低,但可以节省存储空间。创建适当的索引(例如,在本例中为“:Scope(name)”)将减少速度损失。