【问题标题】:MATCH prior to MERGE in single Cypher query confuses ON MATCH and ON CREATE单个 Cypher 查询中 MERGE 之前的 MATCH 混淆了 ON MATCH 和 ON CREATE
【发布时间】:2014-11-25 22:01:59
【问题描述】:

我在 Neo4j 2.1.5 上遇到过这个问题。我有一个使用 Neo4j REST API 从 Node.js 发出的查询。此查询的重点是能够创建或更新给定节点并将其状态(包括标签和属性)设置为某个已知状态。 WITH 之前的 MATCH 和 REMOVE 子句是为了解决这样一个事实,即没有直接的方法可以删除所有节点的标签,也没有办法用给定的标签集更新节点的标签。您必须明确删除您不想要的标签并添加您想要的标签。并且没有办法删除 MERGE 子句中的标签。

查询的简化版本如下所示:

MATCH (m {name:'Brian'}) 
REMOVE m:l1:l2 
WITH m 
MERGE (n {name:'Brian'}) 
ON MATCH SET n={mprops} ON CREATE SET n={cprops} 
RETURN n

mprops = {updated:true, created:false} 和 cprops = {updated:false, created:true}。我这样做是为了在单个 Cypher 查询中删除节点的所有现有标签并使用 ON MATCH 子句设置新标签。问题是包含初始 MATCH 似乎混淆了 ON MATCH 与 ON CREATE 逻辑。

假设 Brian 节点已经存在,这个查询的结果应该显示 n.created = false 和 n.updated = true。但是,我得到相反的结果,n.created=true,n.updated=false。如果我删除初始 MATCH(和 WITH)子句并仅执行 MERGE 子句,则结果符合预期。所以不知何故,包含 MATCH 子句会导致 MERGE 子句认为 CREATE vs MATCH 正在发生。

我意识到这是 WITH 子句的一种奇怪用法,但它似乎确实可以解决操作标签的限制。 Cypher 认为它是有效的 Cypher。我假设这只是一个错误和边缘案例,但我想在报告之前获得其他人的见解和可能的替代方案。

我意识到我可以创建一个事务并将 MATCH 和 MERGE 作为该事务中的单独查询发出,但有一些原因在我正在编写的 API 的设计中不能很好地工作。

谢谢!

【问题讨论】:

  • 试图理解这个查询,我认为有些东西丢失了,特别是为什么下面的 MERGE 中没有使用来自WITH m 的m?我假设mprops 和cprops 作为变量绑定传入?从您的设置和描述中,老实说,我无法说出您为什么将其作为一个查询而不是两个查询来执行。第二个查询似乎不依赖于第一个查询,但您指出第一个查询弄乱了第二个查询。哪些 API 设计问题会阻止您发出多个查询?
  • 此外,在您的示例中,只有在 MATCH 成功时才会执行 MERGE,因此这应该是一个等效查询: MATCH (m {name:'Brian'}) REMOVE m:l1: l2 SET m={mprops} 返回 m
  • 我已经根据我之前的评论添加了答案。
  • 我正在尝试添加创建或更新逻辑,作为我正在编写的 Cypher 的包装 API 的一部分。 createOrUpdate() 方法想要设置新节点的状态或更新现有节点的状态,包括标签。所以我需要一种方法来删除所有标签,以便 MERGE 可以用新标签替换它们。 MATCH 查询的唯一目的是删除标签。这就是为什么合并查询不对“m”绑定做任何事情的原因。我不知道只有在 MATCH 成功时才会执行 MERGE,所以这无论如何都行不通。
  • 出于明显的性能原因,我不想发出多个查询(需要多个 REST 请求)。我不想使用 REST 事务端点包含多个查询,因为我的包装 API 提供了创建事务并在该事务中包含多个包装 API 查询的选项。如果 createOrUpdate() 方法本身创建一个事务,这将不起作用。这就是为什么我一直在努力寻找在单个 Cypher 查询中更新节点状态的方法。

标签: neo4j cypher


【解决方案1】:

如果您在查询前加上 MATCH,如果没有现有的 ('Brian') 节点,它将永远不会执行。

您还应该使用SET n = {param} 覆盖所有属性,您应该使用SET n += {param}

MERGE (n:Label { name:'Brian' })
ON MATCH  SET n += {create :false,update:true }
ON CREATE SET n += {create :true,update:false }
REMOVE n:WrongLabel
RETURN n

【讨论】:

    【解决方案2】:

    我不明白为什么您的查询不起作用,但@FrobberOfBits 提出的问题是有效的。

    但是,从逻辑上讲,您的示例查询等同于这个:

      MATCH (m {name:'Brian'}) 
      REMOVE m:l1:l2
      SET m={mprops}
      RETURN m
    

    此查询更简单,完全避免使用 MERGE,并且可以避免您遇到的任何问题。这是否代表你想要做的事情?

    【讨论】:

    • 这实现了逻辑的“更新”部分,而不是“创建”。在我的包装 API 中,所有节点都有唯一的 ID (GUID)。您可以在节点的内存表示中调用 Node.save()。如果节点没有 GUID,那么我只做一个 CREATE。如果节点确实有 GUID,那么它可能是对现有节点的更新,或者可能是对先前导出的节点的导入。在这种情况下,我要么需要发出查询以确定具有该 GUID 的节点是否存在(昂贵),要么使用 MERGE 的 ON MATCH/CREATE 功能(更好)(续)
    • 这适用于属性,但不适用于标签。在我的查询(和@cybersam 的)中,您看到您可以使用 SET m={mprops} 在节点上设置一组显式属性,但您不能对标签执行相同操作。如果节点“m”具有“label1”,则执行 SET m:label2:label3 将导致节点具有标签“label1”、“label2”、“label3”。恕我直言,这应该在 Cypher 中修复。在那之前,我正在努力寻找一种有效的解决方法
    猜你喜欢
    • 2015-11-16
    • 2014-02-13
    • 1970-01-01
    • 2021-10-20
    • 2022-11-26
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    相关资源
    最近更新 更多