限制每行的 MATCH 结果
由于 LIMIT 作用于查询的总行数,当从多个节点匹配且限制必须针对每行的匹配结果时,LIMIT 不能使用。
以 Movies 数据库为例。
如果你需要一个查询来获取《黑客帝国》(The Matrix)中的所有演员,并且对每位演员获取其出演的 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 及更高版本中,你可以使用模式推导(pattern comprehension)作为简写方式
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 Procedures,可以使用 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 Procedures 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
此页面有帮助吗?