限制每行 MATCH 结果数
由于 LIMIT
适用于查询的总行数,因此在从多个节点匹配时,如果限制必须针对每行匹配结果,则无法使用它。
以电影数据库为例。
如果你需要一个查询来获取《黑客帝国》中的所有演员,并针对每个演员,获取该演员参演的 3 部电影,那么第一次(不正确)尝试可能如下所示:
MATCH (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
MATCH (p)-[:ACTED_IN]->(m)
RETURN p, m LIMIT 3
上述查询不会返回期望的结果。LIMIT
反而会使查询总共只返回 3 行。
以下是一些针对每行匹配结果应用限制的解决方案:
在 4.1+ 版本中在子查询内使用 LIMIT
Neo4j 4.1 引入了关联子查询,允许我们使用查询中存在的变量执行子查询。
由于子查询是按行执行的,我们可以在子查询中执行 MATCH 并应用 LIMIT,这为我们提供了限制每行匹配结果的最简单方法。
这要求在子查询 CALL 块中将 WITH
作为第一个子句使用,目的是将变量导入到子查询中。
MATCH (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
CALL {
WITH p
MATCH (p)-[:ACTED_IN]->(m)
RETURN m
LIMIT 3
}
RETURN p, m
这将正确返回《黑客帝国》中的所有演员,以及他们参演的最多 3 部电影。
如果我们希望每个演员只出现一次,并包含每位演员最多 3 部电影的集合,我们可以在限制后,在子查询中收集这些电影
MATCH (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
CALL {
WITH p
MATCH (p)-[:ACTED_IN]->(m)
WITH m
LIMIT 3
RETURN collect(m) as movies
}
RETURN p, movies
请注意,此 WITH
导入有一些特殊限制,这些限制通常不适用于 WITH
的常规用法
-
你只能包含来自外部查询的变量,不能包含其他变量。
-
你不能在初始
WITH
中执行计算、聚合或引入新变量。 -
你不能在此初始
WITH
中为任何变量设置别名。 -
你不能在初始
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 (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
CALL {
WITH p
WITH p as actor
MATCH (actor)-[:ACTED_IN]->(m)
RETURN m
LIMIT 3
}
RETURN p, m
这表明我们如何可以为导入的变量设置别名(或者,如果需要,进行过滤),但不能在初始导入的 WITH
本身中进行。
对于早期版本,原生关联子查询不可用,因此必须使用其他变通方法。
获取集合中感兴趣的部分
一种常见的解决方案是 collect()
并获取感兴趣的部分
MATCH (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
MATCH (p)-[:ACTED_IN]->(m)
RETURN p, collect(m)[..3] AS movies
在 Neo4j 3.1.x 及更高版本中,你可以使用模式推导作为简写方法
MATCH (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
RETURN p, [(p)-[:ACTED_IN]->(m) | m][..3] as movies
如果只需要集合中的一个元素,可以使用 head()
函数从模式推导中获取第一个元素
MATCH (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
RETURN p, head([(p)-[:ACTED_IN]->(m) | m]) as movie
虽然这在每个节点关系较少时有效,但在关系数量庞大的超级节点上可能会变得不可行,因为它必须在收集之前展开所有 :ACTED_IN
关系。
使用 apoc.cypher.run() 执行有限子查询
Neo4j 目前除了模式推导之外,不提供原生子查询支持,但即使是模式推导也不支持 LIMIT
。
然而,在 Neo4j 3.0.x 及更高版本中,使用 APOC 过程,你可以使用 apoc.cypher.run()
执行带有 LIMIT
的子查询,由于它是按行执行的,因此其行为符合我们的期望。
MATCH (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
CALL apoc.cypher.run('
WITH {p} AS p
MATCH (p)-[:ACTED_IN]->(m)
RETURN m LIMIT 3',
{p:p}) YIELD value
RETURN p, value.m AS movie
这种方法是高效的,因为通过使用 LIMIT
,我们无需为扩展所有 :ACTED_IN
关系付出代价,我们只需每行扩展 3 个。
使用 APOC 路径扩展器,利用结束节点或终止过滤器和 limit
参数
在 Neo4j 3.1.3 及更高版本,以及 APOC 过程 3.1.3.6 及更高版本中,你可以使用新的路径扩展器功能来限制对某些节点的扩展。
limit
参数仅适用于接受配置映射的路径扩展器过程,并且仅在使用结束节点(>
)或终止标签过滤器(/
)时才可用
-
apoc.path.expandConfig()
-
apoc.path.subgraphNodes()
-
apoc.path.subgraphAll()
-
apoc.path.spanningTree()
使用这种方法,查询变为
MATCH (:Movie{title:'The Matrix'})<-[:ACTED_IN]-(p:Person)
CALL apoc.path.subgraphNodes(p, {relationshipFilter:'ACTED_IN>', labelFilter:'/Movie', limit:3}) YIELD node
RETURN p, node as movie
此页面有帮助吗?