如何在可变长度的 Cypher 查询中按模式过滤

How to filter by pattern in a variable length Cypher query

我有一个非常简单的图表,其中包含 5 个节点(名为 n1 - n5)、1 个节点类型 (:Node) 和 2 个关系类型 (:r1, :r2)。节点和关系排列如下(为ascii艺术道歉):

(n1)-[:r1]->(n2)-[:r1]->(n3)
(n1)-[:r2]->(n4)-[:r2]->(n3)
(n1)-[:r1]->(n5)-[:r2]->(n3)

我有一个使用可变长度路径的查询。我希望能够通过在 WHERE 子句中描述特定模式来限制返回的路径:

MATCH p = (n:Node {name: 'n1'})-[*..2]->()
WHERE (n)-[:r1]->()-[:r1]->()
RETURN p

问题是响应returns所有可能的路径。我的问题;在查询中指定可变长度路径时是否可以过滤返回的路径?

如果所有关系或节点都必须遵守同一个谓词,这很容易。您需要一个路径变量,并且需要在 WHERE 子句中使用 all()(或 none())以将谓词应用于路径中的所有关系或节点:

MATCH p = (n:Node {name: 'n1'})-[*..2]->()
WHERE all(rel in relationships(p) WHERE type(rel) = 'r1')
RETURN p

就是说,如果您只希望变长路径中的所有关系都属于同一类型(或类型,如果您想要多个),那么最好在模式本身中完成:

MATCH p = (n:Node {name: 'n1'})-[:r1*..2]->()
RETURN p

对于更复杂的情况,例如多种关系类型(这些类型的顺序在路径中很重要),或路径中类型或节点标签的重复序列,则需要替代方法。 APOC path expanders 可能会有帮助。

编辑

您在评论中提到您的案例涉及不同长度的关系序列。虽然 APOC 路径扩展器可能会有所帮助,但它有一些限制:

  1. 路径扩展器目前对节点标签和关系类型进行操作,但不对属性进行操作,因此如果您的扩展依赖于属性上的谓词,则路径扩展器将无法在扩展,这必须通过在之后过滤路径扩展器结果来完成。

  2. 路径扩展器的关系序列支持存在限制。我们可以定义任意长度的序列,并且可以在序列的每一步接受多种关系类型,但我们目前不支持发散序列((r1 然后 r2 然后 r3)或(r2 然后 r5 然后 r6))。

如果我们想执行 r1(传入)、r2(传出)、r3 或 r4(r3 在任一方向和 r4 传出)的 3 步序列,重复序列最多 3 次我们可以这样做:

MATCH (n:Node {name: 'n1'})
CALL apoc.path.expandConfig(n, {relationshipFilter:'<r1, r2>, r3 | r4>', minLevel:1, maxLevel:9) YIELD path
RETURN path

请注意,我们可以在过滤器中为每个关系提供不同的方向,或者如果我们不关心方向,则完全不使用箭头。

标签过滤更复杂,但到目前为止,我在示例中没有看到任何需要。

您的查询 return 所有路径,因为您的 WHERE 子句(过滤器运算符)在 VarLengthExpand 运算符之前应用:

+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+----------------------------+------------------------------------------------------------------------------------------------------------+
| Operator              | Estimated Rows | Rows | DB Hits | Page Cache Hits | Page Cache Misses | Page Cache Hit Ratio | Variables                  | Other                                                                                                      |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+----------------------------+------------------------------------------------------------------------------------------------------------+
| +ProduceResults       |              0 |    4 |       0 |               0 |                 0 |               0.0000 | anon[32], anon[41], n, p   |                                                                                                            |
| |                     +----------------+------+---------+-----------------+-------------------+----------------------+----------------------------+------------------------------------------------------------------------------------------------------------+
| +Projection           |              0 |    4 |       0 |               0 |                 0 |               0.0000 | p -- anon[32], anon[41], n | {p : PathExpression(NodePathStep(Variable(n),MultiRelationshipPathStep(Variable(),OUTGOING,NilPathStep)))} |
| |                     +----------------+------+---------+-----------------+-------------------+----------------------+----------------------------+------------------------------------------------------------------------------------------------------------+
| +VarLengthExpand(All) |              0 |    4 |       7 |               0 |                 0 |               0.0000 | anon[32], anon[41] -- n    | (n)-[:*..2]->()                                                                                            |
| |                     +----------------+------+---------+-----------------+-------------------+----------------------+----------------------------+------------------------------------------------------------------------------------------------------------+
| +Filter               |              0 |    1 |       6 |               0 |                 0 |               0.0000 | n                          | n.name = {  AUTOSTRING0}; GetDegree(Variable(n),Some(RelTypeName(KNOWS)),OUTGOING) > 0                     |
| |                     +----------------+------+---------+-----------------+-------------------+----------------------+----------------------------+------------------------------------------------------------------------------------------------------------+
| +NodeByLabelScan      |              4 |    4 |       5 |               0 |                 0 |               0.0000 | n                          | :Crew                                                                                                      |
+-----------------------+----------------+------+---------+-----------------+-------------------+----------------------+----------------------------+------------------------------------------------------------------------------------------------------------+

这应该让你继续:

MATCH p = (n:Node {name: 'n1'})-[*..2]->()
WITH n, relationships(p)[0] as rel0, relationships(p)[1] as rel1, p
MATCH (n)-[rel0:r1]->()-[rel1:r1]->()
RETURN p