用Cypher语言将两个合并合并为一个

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-我希望它返回。

综上所述,我想要一个查询:

  1. 总是返回。
  2. b node如果尚不存在,则创建一个结合了a,c和d 的新值。
  3. 如果确实存在,请找到它并返回它。

在处理简单合并时,这非常简单: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阅读:给人类B的电子邮件

到目前为止,一切都很好。我可以重新运行查询,并保留相同的确切状态。

现在,我希望人类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阅读:给人类B的电子邮件

现在,我想授予人类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的全部或全部。

数量很多,但我希望它能为问题提供一些见识。

cyb*_*sam 3

[更新(两次)]

这个技巧可能对你的“三足合并”(创造一个术语)有用。您问题中的第二个插图显示了 3 条腿合并所需结果的示例,其中给定Scope节点与 3 个特定节点且仅与这 3 个节点有关系。

技巧是这样的:向每个属性添加 2 个属性(或 3 个,请参阅下面的注释),以Grant唯一标识关联的,以及授权所代表的Scope关联。Human如果您还与实际节点ScopeHuman节点有关系,那么这无疑是多余的信息,但它应该确保您可以使用它为每组唯一的 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)”)将减少速度损失。