从 apoc.do.when 返回条件 CASE WHEN 值的奇怪结果

Weird result returning conditional CASE WHEN value from apoc.do.when

我们有下图

其中灰色节点 (:Conversation) 代表用户之间的对话 (:User 粉红色节点)。我创建了查询,试图找到人与人之间的当前对话,如果不存在,则创建它。在这两种情况下,都必须返回对话。 这是它的代码:

MATCH (u1:User {login:"User_1"})
MATCH (u2:User {login:"User_2"})
MATCH (u3:User {login:"User_3"})

OPTIONAL MATCH
    (conv:Conversation)-[:CONDUCTED_BY]->(u1),
    (conv)-[:CONDUCTED_BY]->(u2),
    (conv)-[:CONDUCTED_BY]->(u3)
WHERE NOT EXISTS { 
    MATCH (conv)-[:CONDUCTED_BY]->(u:User)
    WHERE NOT u IN [u1, u2, u3]
}

CALL apoc.do.when(conv IS NULL, 
    "WITH $u1 AS u1, $u2 AS u2, $u3 AS u3 " +
    "CREATE (conv:Conversation) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u1) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u2) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u3) " +
    "RETURN conv AS conv",
    "RETURN $conv AS conv", {u1:u1, u2:u2, u3:u3, conv:conv}) YIELD value
[...WEIRD PART...]

解释:
可选匹配 - 尝试查找用户 1、2、3 之间的当前对话 - 对话 7172
WHERE NOT EXIST - 排除这些用户之间可能包含其他人的其他对话,例如 User_4 - 72
我们最终只有一个我们感兴趣的对话:71
...现在奇怪的部分出现在 [...WEIRD PART...]

如果我们用代码

替换[...WEIRD PART...]
RETURN value.conv

一切都很好,但在我想出这个解决方案之前,我一直在努力处理其他代码,在 apoc 映射中 conv:conv 没有被包含并且 else-query 只是 ""

WITH CASE WHEN conv IS NULL THEN value ELSE conv END AS conv
RETURN conv

那部分是在我们每次 运行 查询时在这些用户 1、2、3 之间创建新对话。 但是,如果我将其替换为

RETURN value

它工作正常,我的意思是它没有在用户 1、2、3 之间创建新对话(如果存在)。

问题:我不明白为什么下面的代码

MATCH (u1:User {login:"User_1"})
MATCH (u2:User {login:"User_2"})
MATCH (u3:User {login:"User_3"})

OPTIONAL MATCH
    (conv:Conversation)-[:CONDUCTED_BY]->(u1),
    (conv)-[:CONDUCTED_BY]->(u2),
    (conv)-[:CONDUCTED_BY]->(u3)
WHERE NOT EXISTS { 
    MATCH (conv)-[:CONDUCTED_BY]->(u:User)
    WHERE NOT u IN [u1, u2, u3]
}

CALL apoc.do.when(conv IS NULL, 
    "WITH $u1 AS u1, $u2 AS u2, $u3 AS u3 " +
    "CREATE (conv:Conversation) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u1) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u2) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u3) " +
    "RETURN conv",
    "", {u1:u1, u2:u2, u3:u3}) YIELD value

WITH CASE WHEN conv IS NULL THEN value ELSE conv END AS conv
RETURN conv

可能对这种奇怪的行为负责。

感谢您的澄清,我可以重现这个,这绝对不是预期的。这对我来说像是一个错误。

我们可以通过将您的 CASE 中使用的别名重命名为 conv2conv 以外的任何别名来规避此问题。这应该有效:

MATCH (u1:User {login:"User_1"})
MATCH (u2:User {login:"User_2"})
MATCH (u3:User {login:"User_3"})

OPTIONAL MATCH
    (conv:Conversation)-[:CONDUCTED_BY]->(u1),
    (conv)-[:CONDUCTED_BY]->(u2),
    (conv)-[:CONDUCTED_BY]->(u3)
WHERE NOT EXISTS { 
    MATCH (conv)-[:CONDUCTED_BY]->(u:User)
    WHERE NOT u IN [u1, u2, u3]
}

CALL apoc.do.when(conv IS NULL, 
    "WITH $u1 AS u1, $u2 AS u2, $u3 AS u3 " +
    "CREATE (conv:Conversation) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u1) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u2) " +
    "MERGE (conv)-[:CONDUCTED_BY]->(u3) " +
    "RETURN conv",
    "", {u1:u1, u2:u2, u3:u3}) YIELD value

WITH CASE WHEN conv IS NULL THEN value ELSE conv END AS conv2
RETURN conv2

我会向我们的工程师提出这个问题以确认并开始修复错误。

这是存在子查询名称间距的错误。由于 conv 被重用以表示其他含义,因此 Cypher 将除最后两个 conv 之外的所有内容重写为 conv@x,将最后两个重写为 conv@y,以便区分它们。这里的 x 和 y 是具有特定含义的 conv 第一次出现的位置。此重写未正确传播到内部子查询。

已在此处修复:https://github.com/neo4j/neo4j/commit/2890463ad6d2f323bfbad5cf453f14b42f51c830并将包含在 Neo4j 4.0 的下一个补丁版本中。