Cypher路径匹配

Neo
Neo
2022-03-12 / 0 评论 / 71 阅读

本节内容源自Cypher官方手册V4.4版,笔者对其进行学习、翻译、记录。

Neo4j Cypher利用关系同构进行路径匹配,是减少结果集大小和防止无限遍历的非常有效的方法。

在 Neo4j 中,所有关系都有一个方向。但是,您可以在查询时拥有无向关系的概念。

在可变长度模式表达式的情况下,进行约束检查尤为重要,否则可能会找到无限数量的结果记录。

为了更好地理解这一点,让我们考虑一些可选方案:

同态:路径匹配没有限制。

节点同构:对于每个路径匹配记录,不能多次返回同一节点。

关系同构:对于每个路径匹配记录,不能多次返回相同的关系。Cypher 利用关系同构进行路径匹配。

同态

约束:路径匹配没有约束。

该图仅由两个节点(a)和组成(b),由一个关系连接,(a:Node)-[r:R]->(b:Node)。

如果查询正在寻找长度路径n并且不关心方向,n则将返回一个长度路径,一遍又一遍地重复两个节点。

例如,查找所有具有 5 个关系的路径并且不关心关系方向:

MATCH p = ()-[*5]-()
RETURN nodes(p)

如果使用同态,这将返回两个结果记录[a,b,a,b,a,b],以及[b,a,b,a,b,a]。

节点同构

约束:对于每个路径匹配记录,不能多次返回同一节点。

在另一个双节点示例中,例如(a:Node)-[r:R]->(b:Node); 使用节点同构约束只能找到长度为 1 的路径。

该图仅由两个节点(a)和组成(b),由一个关系连接,(a:Node)-[r:R]->(b:Node)。

MATCH p = ()-[*1]-()
RETURN nodes(p)

如果使用节点同构,这将返回两个结果记录[a, b],以及[b, a]。

关系同构

约束:对于每个路径匹配记录,不能多次返回相同的关系。

在另一个双节点示例中,例如(a:Node)-[r:R]->(b:Node); 使用关系同构约束只能找到长度为 1 的路径。

该图仅由两个节点(a)和组成(b),由一个关系连接,(a:Node)-[r:R]->(b:Node)。

MATCH p = ()-[*1]-()
RETURN nodes(p)

这将返回两个结果记录[a, b],以及[b, a].

Cypher 路径匹配示例

Cypher 利用关系同构进行路径匹配。

寻找用户的朋友的朋友不应该返回该用户。

为了证明这一点,让我们创建一些节点和关系:

创建数据。

CREATE
  (adam:User {name: 'Adam'}),
  (pernilla:User {name: 'Pernilla'}),
  (david:User {name: 'David'}),
  (adam)-[:FRIEND]->(pernilla),
  (pernilla)-[:FRIEND]->(david)
Nodes created: 3
Relationships created: 2
Properties set: 3

这给了我们下图:

image.png

现在让我们寻找亚当朋友的朋友:

MATCH (user:User {name: 'Adam'})-[r1:FRIEND]-()-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
+---------+
| fofName |
+---------+
| "David" |
+---------+

Rows: 1

在此查询中,Cypher 确保不返回模式关系r1并r2指向相同图关系的匹配项。

然而,这并不总是需要的。如果查询应该返回用户,则可以将匹配分布在多个MATCH子句中,如下所示:

MATCH (user:User {name: 'Adam'})-[r1:FRIEND]-(friend)
MATCH (friend)-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
+---------+
| fofName |
+---------+
| "David" |
| "Adam"  |
+---------+

Rows: 2

请注意,虽然下面的查询4​​看起来与查询3相似,但它实际上等同于查询2。

MATCH
  (user:User {name: 'Adam'})-[r1:FRIEND]-(friend),
  (friend)-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
+---------+
| fofName |
+---------+
| "David" |
+---------+

Rows: 1