知识库

UNION 后处理

Cypher 不允许对 UNIONUNION ALL 的结果进行进一步处理,因为联合查询中的所有查询都要求有 RETURN 子句。

以下是一些解决方法。

Neo4j 4.0 中的 UNION 后处理

在 Neo4j 4.0 中,现在可以通过子查询进行 UNION 后处理。

使用示例

CALL {
  MATCH (movieOrPerson:Movie) RETURN movieOrPerson
  UNION
  MATCH (movieOrPerson:Person) RETURN movieOrPerson
}
WITH movieOrPerson
...

这使我们能够继续处理 UNION 子查询的结果。

然而,在 4.0 的初始版本中,只支持非关联子查询,这意味着子查询不能使用来自外部调用的变量。这意味着在一个更复杂的查询中间使用子查询进行 UNION 后处理可能不可行,因为您无法将外部查询中的变量传递到子查询中使用。

在 4.0.x 版本中,关联子查询(更实用,可以使用来自子查询外部的变量)目前仅在使用 Neo4j Fabric 时可用。

4.1+ 版本中的 UNION 后处理增强功能

随着 4.1 版本的发布,CALL 子查询功能已得到增强,允许使用关联子查询。这使我们能够在查询中间在子查询中使用现有变量。

这要求在子查询 CALL 块中将 WITH 用作第一个子句,目的是将变量导入到子查询中。

使用 UNIONUNION ALL 时,我们可以为每个联合查询提供类似的导入 WITH 子句

MATCH (m:Movie {title:'The Matrix'})
CALL {
    WITH m
    MATCH (m)<-[:ACTED_IN]-(p)
    RETURN p
    UNION
    WITH m
    MATCH (m)<-[:DIRECTED]-(p)
    RETURN p
}
RETURN p.name as name
ORDER BY name ASC

这将正确返回《黑客帝国》中所有演员和导演按字母顺序排序的姓名。

这种导入用法有一些特殊限制,通常不适用于 WITH 的用法

  1. 您只能包含来自外部查询的变量,不能包含其他变量。+ 您不能在初始的 WITH 中执行计算、聚合或引入新变量。

  2. 您不能在此初始 WITH 中对任何变量进行别名。

  3. 您不能在初始 WITH 之后使用 WHERE 子句进行过滤。

如果您尝试这些操作中的任何一个,将会遇到某种错误,例如

Importing WITH should consist only of simple references to outside variables. Aliasing or expressions are not supported.

或者更隐晦地,如果您尝试在初始 WITH 之后使用 WHERE 子句

Variable `x` not defined

(其中变量是 WITH 子句中存在的第一个)

您可以通过在导入 WITH 之后简单地引入一个额外的 WITH 子句来绕过所有这些限制,如下所示

MATCH (m:Movie)
WHERE m.title CONTAINS 'Matrix'
CALL {
    WITH m
    WITH m as movie
    MATCH (m)<-[:DIRECTED]-(p)
    RETURN p.name as name
    UNION
    WITH m
    WITH m
    WHERE m.title CONTAINS 'Reloaded'
    MATCH (m)<-[:ACTED_IN]-(p)
    RETURN p.name as name
}
RETURN DISTINCT name
ORDER BY name ASC

这演示了对导入变量进行别名和过滤的能力,前提是我们使用第二个 WITH 子句,该子句不像用于导入到子查询中的初始 WITH 那样受到相同的限制。

适用于 3.5.x 及更早版本

对于更早的版本,不支持原生子查询,因此必须使用其他解决方法。

合并集合,然后 UNWIND 回到行并应用 DISTINCT

MATCH (m:Movie)
WITH collect(m) AS movies
MATCH (p:Person)
WITH movies + collect(p) AS moviesAndPeople
UNWIND moviesAndPeople AS movieOrPerson
WITH DISTINCT movieOrPerson
...

在上述查询中,DISTINCT 并非真正需要,但如果一个结果可能存在于多个合并的集合中,并且您想要唯一值,则需要它。

使用 apoc.cypher.run() 从子查询返回 UNION 结果

使用 APOC 过程,您可以使用 apoc.cypher.run() 在子查询中执行 UNION,并返回其结果。

CALL apoc.cypher.run('
 MATCH (movieOrPerson:Movie)
 RETURN movieOrPerson
 UNION
 MATCH (movieOrPerson:Person)
 RETURN movieOrPerson',
 {}) yield value
WITH value.movieOrPerson as movieOrPerson
...

请记住,过程调用是按行执行的,因此当多行已存在时使用此方法可能会导致意外和不可预知的结果。

© . All rights reserved.