邻居函数

邻居搜索过程能够快速发现基于特定关系类型和跳数的周围节点。

可用过程

下表描述了可用过程

限定名 类型

apoc.neighbors.athop
apoc.neighbors.athop(node NODE, relTypes STRING, distance INTEGER) - 返回给定距离内由指定 RELATIONSHIP 类型连接的所有 NODE 值。

过程

apoc.neighbors.byhop
apoc.neighbors.byhop(node NODE, relTypes STRING, distance INTEGER) - 返回指定距离内由给定 RELATIONSHIP 类型连接的所有 NODE 值。返回 LIST<NODE> 值,其中每个 NODE 值的 PATH 代表 LIST<NODE> 值中的一行。

过程

apoc.neighbors.tohop
apoc.neighbors.tohop(node NODE, relTypes STRING, distance INTEGER) - 返回指定距离内由给定 RELATIONSHIP 类型连接的所有 NODE 值。NODE 值将为每一行单独返回。

过程

关系过滤器

邻居搜索过程中,每个过程的第二个参数都是关系过滤器。关系过滤器是一个由 | 分隔的关系类型列表,使用以下语法:

Syntax: [<]RELATIONSHIP_TYPE1[>]|[<]RELATIONSHIP_TYPE2[>]|…​

输入 类型 方向

LIKES>

LIKES

出站

<FOLLOWS

FOLLOWS

入站

KNOWS

KNOWS

双向

>

任意类型

出站

<

任意类型

入站

关系过滤器对空格敏感,因此,如果您没有看到预期结果,请检查(并删除!)末尾的空格。

示例

本节中的示例基于以下示例图

MERGE (mark:Person {name: "Mark"})
MERGE (praveena:Person {name: "Praveena"})
MERGE (joe:Person {name: "Joe"})
MERGE (lju:Person {name: "Lju"})
MERGE (michael:Person {name: "Michael"})
MERGE (emil:Person {name: "Emil"})
MERGE (ryan:Person {name: "Ryan"})

MERGE (ryan)-[:FOLLOWS]->(joe)
MERGE (joe)-[:FOLLOWS]->(mark)
MERGE (mark)-[:FOLLOWS]->(emil)
MERGE (michael)-[:KNOWS]-(emil)
MERGE (michael)-[:KNOWS]-(lju)
MERGE (michael)-[:KNOWS]-(praveena)
MERGE (emil)-[:FOLLOWS]->(joe)
MERGE (praveena)-[:FOLLOWS]->(joe)

KNOWS 关系类型被认为是双向的,如果 Michael 认识 Emil,我们就可以推断 Emil 也认识 Michael。使用 KNOWS 关系时,我们将忽略方向。

FOLLOWS 关系具有方向,因此,在使用它时我们将指定方向。

查找指定跳数的邻居

apoc.neighbors.athop 过程计算节点在特定跳数上的邻居。

以下查询返回 Emil 在 1 跳距离内 KNOWS 的人物
MATCH (p:Person {name: "Emil"})
CALL apoc.neighbors.athop(p, "KNOWS", 1)
YIELD node
RETURN node
表 1. 结果
节点

(:Person {name: "Michael"})

Emil 只与 Michael 有直接的 KNOWS 关系,因此 Michael 是此查询返回的唯一节点。

以下查询返回 Emil 在 2 跳距离内 KNOWS 的人物
MATCH (p:Person {name: "Emil"})
CALL apoc.neighbors.athop(p, "KNOWS", 2)
YIELD node
RETURN node
表 2. 结果
节点

(:Person {name: "Praveena"})

(:Person {name: "Lju"})

Michael 也 KNOWS Praveena 和 Lju,由于 Emil 不直接 KNOWS 他们,所以他只在跳数为 2 的距离上 KNOWS 他们。如果我们不关心邻居中有哪些节点,而只是想知道数量,我们也可以这样做。

以下查询返回 Emil 在 2 跳距离内 KNOWS 的人数
MATCH (p:Person {name: "Emil"})
CALL apoc.neighbors.athop.count(p, "KNOWS", 2)
YIELD value
RETURN value
表 3. 结果

2

正如所料,我们得到了 2 的计数,他们就是 Praveena 和 Lju!

查找指定跳数范围内的邻居

apoc.neighbors.byhop 过程计算节点在多个跳数上的邻居。

以下查询返回 Emil 在 2 跳距离以内 KNOWS 的人物
MATCH (p:Person {name: "Emil"})
CALL apoc.neighbors.byhop(p, "KNOWS", 2)
YIELD nodes
RETURN nodes
表 4. 结果
节点

[(:Person {name: "Michael"})]

[(:Person {name: "Praveena"}), (:Person {name: "Lju"})]

从这些结果中我们可以看到,在第 1 层,Emil KNOWS Michael;在第 2 层,Emil KNOWS Lju 和 Praveena。以下图模式描述了 Emil 如何认识不同的人:

第 1 层
  • (emil)-[:KNOWS]-(michael)

第 2 层
  • (emil)-[:KNOWS]-(michael)-[:KNOWS]-(lju)

  • (emil)-[:KNOWS]-(michael)-[:KNOWS]-(praveena)

在搜索邻居时,我们还可以使用多种关系类型。

假设除了找到 Emil 认识的人之外,我们还想找到关注他的人。我们可以为关系类型指定方向,使用 < 表示入站关系,或使用 > 表示出站关系。因此,要找到关注 Emil 的人,我们将使用 <FOLLOWS

以下查询返回 Emil 在 3 跳距离以内 KNOWS 的人和与他有 FOLLOWS 关系的人
MATCH (p:Person {name: "Emil"})
CALL apoc.neighbors.byhop(p, "KNOWS|<FOLLOWS", 3)
YIELD nodes
RETURN nodes
表 5. 结果
节点

[(:Person {name: "Mark"}), (:Person {name: "Michael"})]

[(:Person {name: "Praveena"}), (:Person {name: "Joe"}), (:Person {name: "Lju"})]

[(:Person {name: "Ryan"})]

这次我们得到了更多结果。Mark 在 Emil 的第 1 层邻居中,Joe 在他的第 2 层邻居中,Ryan 在他的第 3 层邻居中。

以下图模式描述了 Emil 如何认识不同的人:

第 1 层
  • (emil)-[:KNOWS]-(michael)

  • (mark)-[:FOLLOWS]→(emil)

第 2 层
  • (emil)-[:KNOWS]-(michael)-[:KNOWS]-(lju)

  • (emil)-[:KNOWS]-(michael)-[:KNOWS]-(praveena)

  • (joe)-[:FOLLOWS]→(mark)-[:FOLLOWS]→(emil)

第 3 层
  • (ryan)-[:FOLLOWS]→(joe)-[:FOLLOWS]→(mark)-[:FOLLOWS]→(emil)

apoc.neighbors.athop 过程一样,我们也可以只返回每个跳数的邻居大小。

以下查询返回 Emil 在 3 跳距离以内 KNOWS 的人数和与他有 FOLLOWS 关系的人数
MATCH (p:Person {name: "Emil"})
CALL apoc.neighbors.byhop.count(p, "KNOWS|<FOLLOWS", 3)
YIELD value
RETURN value
表 6. 结果

[2, 3, 1]

正如所料,我们在第 1 层有 2 个计数,第 2 层有 3 个,第 3 层有 1 个。

我们甚至可以将该数字列表转换为映射,其中键是跳数,值是邻居大小。以下查询展示了如何使用 apoc.map.fromLists 函数实现此操作

MATCH (p:Person {name: "Emil"})
CALL apoc.neighbors.byhop.count(p, "KNOWS|<FOLLOWS", 3)
YIELD value
RETURN apoc.map.fromLists(
         [value in range(1, size(value)) | toString(value)],
         value) AS value
表 7. 结果

{1: 2, 2: 3, 3: 1}

查找指定跳数以内的邻居

apoc.neighbors.tohop 过程计算节点在指定跳数以内的邻居。

以下查询返回 Praveena 在 1 跳距离以内 FOLLOWS 的人物
MATCH (p:Person {name: "Praveena"})
CALL apoc.neighbors.tohop(p, "FOLLOWS>", 1)
YIELD node
RETURN node
表 8. 结果
节点

(:Person {name: "Joe"})

Praveena 唯一关注的人是 Joe,所以这是返回的唯一节点。如果我们包含 2 跳距离以内的人呢?

以下查询返回 Praveena 在 2 跳距离以内 FOLLOWS 的人物
MATCH (p:Person {name: "Praveena"})
CALL apoc.neighbors.tohop(p, "FOLLOWS>", 2)
YIELD node
RETURN node
表 9. 结果
节点

(:Person {name: "Mark"})

(:Person {name: "Joe"})

现在 Mark 也被返回了。以下图模式描述了 Emil 如何认识不同的人:

  • (praveena)-[:FOLLOWS]-(joe)

  • (praveena)-[:FOLLOWS]-(joe)-[:FOLLOWS]→(mark)

如果我们只想知道人数,可以使用计数变体。

以下查询返回 Praveena 在 2 跳距离以内 FOLLOWS 的人数
MATCH (p:Person {name: "Praveena"})
CALL apoc.neighbors.tohop.count(p, "FOLLOWS>", 2)
YIELD value
RETURN value
表 10. 结果

2