图分组

大型图通常难以理解或可视化。

表格结果可以聚合以提供概览,例如在图表中显示总和、计数等。

按属性值将节点分组到虚拟节点中,有助于在图可视化中实现同样的效果。

这样做时,这些组之间的关系也会被聚合,因此您只能看到汇总信息。

此功能灵感来源于 Martin Junghanns 在为 Gradoop 图处理系统提供的 Grouping Demo 中的工作。

基本上,您可以使用任何 (entity)<-->(entity) 图进行分组,图投影支持正在路线图中。

这是一个使用数据集的示例,数据集来源于 :play movies

电影图示例
MATCH (n)
SET n.century = toInteger(coalesce(n.born,n.released)/100) * 100;

CALL apoc.nodes.group(['Person','Movie'],['century']);
apoc.nodes.group

有时 UI 在处理分组的返回值(节点列表和关系列表)时可能出现问题,这时运行以下命令可能会有所帮助

CALL apoc.nodes.group(['Person','Movie'],['century'])
YIELD nodes, relationships
UNWIND nodes as node
UNWIND relationships as rel
RETURN node, rel;

用法

CALL apoc.nodes.group(labels,properties, [grouping], [config])

唯一必需的参数是一个标签列表(也可以是 ['*'])以及一个用于分组的属性名称列表(适用于关系/节点)。

您还可以选择提供按字段分组的操作符以及一些配置选项。

分组操作符

对于分组操作符,您以这种形式提供一个每字段操作的 MAP{fieldName: [operators]}

一个用于节点的映射,一个用于关系的映射:[{nodeOperators},{relOperators}]

可能的操作符

  • count_*

  • count

  • sum

  • min/max

  • avg

  • collect

默认值是:[{`*`:"count"},{`*`:"count"}],这只计算节点和关系。

配置

配置中有更多选项

选项 默认值 描述

selfRels

true

在结果图中显示自循环关系

orphans

true

在结果图中显示孤立节点

limitNodes

-1

限制最大节点数

limitRels

-1

限制最大关系数

relsPerNode

-1

限制每个节点的关系数量

filter

null

一个按属性值进行的最小/最大过滤器,例如 {User.count_*.min:2},详见下文

includeRels

[]

要包含的关系类型。默认是包含所有关系类型。可以是类型列表或单个类型。

excludeRels

[]

要排除的关系类型。默认是不排除任何关系类型。可以是类型列表或单个类型。

filter 配置选项是一个 MAP,形式为 {Label/TYPE.operator_property.min/max: number},其中 Label/TYPE. 前缀是可选的。

例如,您可以筛选出分组中最小年龄为 21 岁的人:Person.min_age.min: 21,或者最多拥有 10 个共同的 KNOWS 关系:KNOWS.count_*.max:10

示例

图设置
CREATE
 (alice:Person {name:'Alice', gender:'female', age:32, kids:1}),
 (bob:Person   {name:'Bob',   gender:'male',   age:42, kids:3}),
 (eve:Person   {name:'Eve',   gender:'female', age:28, kids:2}),
 (graphs:Forum {name:'Graphs',    members:23}),
 (dbs:Forum    {name:'Databases', members:42}),
 (alice)-[:KNOWS {since:2017}]->(bob),
 (eve)-[:KNOWS   {since:2018}]->(bob),
 (alice)-[:MEMBER_OF]->(graphs),
 (alice)-[:MEMBER_OF]->(dbs),
 (bob)-[:MEMBER_OF]->(dbs),
 (eve)-[:MEMBER_OF]->(graphs)
查询
CALL apoc.nodes.group(['*'],['gender'],
  [{`*`:'count', age:'min'}, {`*`:'count'} ])
表 1. 结果
节点 关系 节点 关系

[(:Person {gender: "female",min_age: 28,count_*: 2})]

[[:MEMBER_OF {count_*: 3}], [:KNOWS {count_*: 2}]]

(:Person {gender: "female",min_age: 28,count_*: 2})

[:MEMBER_OF {count_*: 3}]

[(:Person {gender: "female",min_age: 28,count_*: 2})]

[[:KNOWS {count_*: 2}]]

(:Person {gender: "female",min_age: 28,count_*: 2})

[:KNOWS {count_*: 2}]

[(:Person {gender: "male",min_age: 42,count_*: 1})]

[[:MEMBER_OF {count_*: 1}]]

(:Person {gender: "male",min_age: 42,count_*: 1})

[:MEMBER_OF {count_*: 1}]

[(:Forum {gender: null,count_*: 2})]

[]

(:Forum {gender: null,count_*: 2})

null

请注意,此查询在 Neo4j Browser 的“图”模式下不起作用,仅在“表格”模式下(或在 cypher-shell 中)起作用,因为 Forum 没有 gender 属性,所以在 node 结果中会出现一个不受支持并返回 TypeError"gender": null 属性。相反,下面的查询在“图”模式下也能正常工作

CALL apoc.nodes.group(
        ['Person'],['gender'],
        [{`*`:'count', kids:'sum', age:['min', 'max', 'avg'], gender:'collect'},
         {`*`:'count', since:['min', 'max']}]);

大型示例

图设置
WITH ["US","DE","UK","FR","CA","BR","SE"] AS tld
UNWIND range(1,1000) AS id
CREATE (u:User {id:id, age : id % 100, female: rand() < 0.5, name: "Name "+id, country:tld[toInteger(rand()*size(tld))]})
WITH collect(u) AS users
UNWIND users AS u
WITH u, users[toInteger(rand()*size(users))] AS u2
WHERE u <> u2
MERGE (u)-[:KNOWS]-(u2);
CALL apoc.nodes.group(['*'], ['country'])
YIELD node, relationship return *
grouping country all
查询
CALL apoc.nodes.group(['*'], ['country'], null,
    {selfRels:false, orphans:false,
     filter:{`User.count_*.min`:130,`KNOWS.count_*.max`:200}})
YIELD node, relationship return *
grouping country filter

要在 Neo4j Browser 中可视化此结果,最好有一个自定义的图样式表 (GRASS),它可以使用一些聚合来渲染分组属性。

node {
  diameter: 50px;
  color: #A5ABB6;
  border-color: #9AA1AC;
  border-width: 2px;
  text-color-internal: #FFFFFF;
  font-size: 10px;
}

relationship {
  color: #A5ABB6;
  shaft-width: 3px;
  font-size: 8px;
  padding: 3px;
  text-color-external: #000000;
  text-color-internal: #FFFFFF;
  caption: '{count_*}';
}

node.Country {
  color: #68BDF6;
  diameter: 80px;
  border-color: #5CA8DB;
  text-color-internal: #FFFFFF;
  caption: '{country} ({count_*})';
}