聚合函数

聚合函数对一组值执行计算,并返回单个值。聚合可以针对所有匹配的路径进行计算,也可以通过引入 分组键 进一步划分。

要详细了解 Cypher® 如何处理对零行执行的聚合,请参阅 Neo4j 知识库 → 了解对零行的聚合

示例图

以下图表用于下面的示例

graph aggregating functions

要重新创建该图,请对一个空的 Neo4j 数据库运行以下查询

CREATE
  (keanu:Person {name: 'Keanu Reeves', age: 58}),
  (liam:Person {name: 'Liam Neeson', age: 70}),
  (carrie:Person {name: 'Carrie Anne Moss', age: 55}),
  (guy:Person {name: 'Guy Pearce', age: 55}),
  (kathryn:Person {name: 'Kathryn Bigelow', age: 71}),
  (speed:Movie {title: 'Speed'}),
  (keanu)-[:ACTED_IN]->(speed),
  (keanu)-[:KNOWS]->(carrie),
  (keanu)-[:KNOWS]->(liam),
  (keanu)-[:KNOWS]->(kathryn),
  (carrie)-[:KNOWS]->(guy),
  (liam)-[:KNOWS]->(guy)

avg()

详细信息

语法

avg(input)

描述

返回一组 INTEGERFLOATDURATION 值的平均值。

参数

名称

类型

描述

input

INTEGER | FLOAT | DURATION

聚合以形成平均值的值。

返回

INTEGER | FLOAT | DURATION

注意事项

任何 null 值都会从计算中排除。

avg(null) 返回 null

示例 1. avg() - 数值
查询
MATCH (p:Person)
RETURN avg(p.age)

返回属性 age 中所有值的平均值

结果
avg(p.age)

61.8

行:1

示例 2. avg() - 持续时间值
查询
UNWIND [duration('P2DT3H'), duration('PT1H45S')] AS dur
RETURN avg(dur)

返回两个提供的 DURATION 值的平均值

结果
avg(dur)

P1DT2H22.5S

行:1

collect()

详细信息

语法

collect(input)

描述

返回一个包含表达式返回的值的列表。

参数

名称

类型

描述

input

ANY

聚合到列表中的值。

返回

LIST<ANY>

注意事项

任何 null 值都会被忽略,不会添加到列表中。

collect(null) 返回一个空列表。

示例 3. collect()
查询
MATCH (p:Person)
RETURN collect(p.age)

所有值都被收集并返回到一个列表中

结果
collect(p.age)

[58, 70, 55, 55, 71]

行:1

count()

详细信息

语法

count(input)

描述

返回值或行的数量。

参数

名称

类型

描述

input

ANY

要聚合的值。

返回

INTEGER

注意事项

count(*) 包括返回 null 的行。

count(input) 忽略 null 值。

count(null) 返回 0

Neo4j 维护一个事务计数存储,用于保存计数元数据,这可以显着提高使用 count() 函数的查询速度。有关计数存储的更多信息,请参阅 Neo4j 知识库 → 使用计数存储进行快速计数

使用 count(*) 返回节点数量

函数 count(*) 可用于返回节点数量;例如,与节点 n 相连的节点数量。

示例 4. count()
查询
MATCH (p:Person {name: 'Keanu Reeves'})-->(x)
RETURN labels(p), p.age, count(*)

返回起始节点 Keanu Reeves 的标签和 age 属性以及与其相关的节点数量

结果
labels(p) p.age count(*)

["Person"]

58

4

行:1

55

3

使用 count(*) 对关系类型进行分组和计数
查询
MATCH (p:Person {name: 'Keanu Reeves'})-[r]->()
RETURN type(r), count(*)

函数 count(*) 可用于对匹配关系的类型进行分组,并返回类型的组计数。

结果
示例 5. count() count(*)

对匹配关系的类型进行分组,并返回关系类型组的计数

1

type(r)

3

count(*)

"ACTED_IN"

2

"KNOWS"
查询
MATCH (p:Person)
RETURN count(p.age)

4

结果
Rows: 2

5

行:1

对非 null 值进行计数

函数 count(expression) 可以用来返回表达式返回的非 null 值的数量,而不是简单地使用 count(*) 返回行数。

示例 6. count()

返回具有标签 Person 且具有属性 age 的节点数量:(要计算总和,请使用 sum(n.age)

count(p.age)
查询
MATCH (p:Person)-->(friend:Person)-->(friendOfFriend:Person)
WHERE p.name = 'Keanu Reeves'
RETURN friendOfFriend.name, count(friendOfFriend), count(ALL friendOfFriend), count(DISTINCT friendOfFriend)

3

结果
有无重复计数 count 函数的默认行为是计算所有匹配的结果,包括重复项。要避免计数重复项,请使用 DISTINCT 关键字。 从 Neo4j 5.15 开始,还可以将 ALL 关键字与聚合函数一起使用。这将计算所有结果,包括重复项,在功能上等同于不使用 DISTINCT 关键字。ALL 关键字是在 Cypher 的 GQL 符合性 中引入的。 此示例尝试查找 Keanu Reeves 的所有朋友的朋友,并对他们进行计数。它展示了使用 ALLDISTINCT 关键字的行为

示例 7. count()

2

2

1

节点 Carrie Anne MossLiam Neeson 都与 Guy Pearce 存在出站 KNOWS 关系。因此,Guy Pearce 节点在不使用 DISTINCT 的情况下会被计算两次。

详细信息

语法

friendOfFriend.name

描述

count(friendOfFriend)

参数

名称

类型

描述

input

ANY

要聚合的值。

返回

ANY

注意事项

任何 null 值都会从计算中排除。

count(ALL friendOfFriend)

count(DISTINCT friendOfFriend)

"Guy Pearce"

2
查询
UNWIND [1, 'a', null, 0.2, 'b', '1', '99'] AS val
RETURN max(val)

2

结果
1

1

行:1

max()

max(input)
查询
UNWIND [[1, 'a', 89], [1, 2]] AS val
RETURN max(val)

在集合中返回所有列表中最高的列表 - 在这种情况下是列表 [1, 2] - 因为数字 2 被认为比 STRING 'a' 更高,即使列表 [1, 'a', 89] 包含更多元素。

结果
1

[1,2]

行:1

示例 10. max()
查询
MATCH (p:Person)
RETURN max(p.age)

返回属性 age 中所有值中最高的那个。

结果
max(p.age)

71

行:1

min()

详细信息

语法

min(input)

描述

返回一组值中的最小值。

参数

名称

类型

描述

input

ANY

要聚合的值。

返回

ANY

注意事项

任何 null 值都会从计算中排除。

在混合集合中,任何 STRING 值始终被认为比任何数值都低,任何 LIST<ANY> 始终被认为比任何 STRING 都低。

count(DISTINCT friendOfFriend)

min(null) 返回 null

示例 11. min()
查询
UNWIND [1, 'a', null, 0.2, 'b', '1', '99'] AS val
RETURN min(val)

返回混合集合中所有值中最小的那个 - 在这种情况下是 STRING"1" -。注意,(数值)值 0.2 乍一看似乎是列表中最小的值,但它被认为比 "1" 更高,因为后者是 STRING

结果
min(val)

"1"

行:1

示例 12. min()
查询
UNWIND ['d', [1, 2], ['a', 'c', 23]] AS val
RETURN min(val)

返回集合中所有值中最小的那个 - 在这种情况下是列表 ['a', 'c', 23] -,因为 (i) 两个列表被认为比 STRING "d" 值低,并且 (ii) STRING "a" 被认为比数值 1 值低。

结果
min(val)

["a","c",23]

行:1

示例 13. min()
查询
MATCH (p:Person)
RETURN min(p.age)

返回属性 age 中所有值中最小的那个。

结果
min(p.age)

55

行:1

percentileCont()

详细信息

语法

percentileCont(input, percentile)

描述

使用线性插值返回一组值中某个值的百分位数。

参数

名称

类型

描述

input

FLOAT

要聚合的值。

百分位数

FLOAT

介于 0.0 和 1.0 之间的百分位数。

返回

FLOAT

注意事项

任何 null 值都会从计算中排除。

percentileCont(null, percentile) 返回 null

示例 14. percentileCont()
查询
MATCH (p:Person)
RETURN percentileCont(p.age, 0.4)

返回属性 age 中值的第 40 个百分位数,使用加权平均值计算。

结果
percentileCont(p.age, 0.4)

56.8

行:1

percentileDisc()

详细信息

语法

percentileDisc(input, percentile)

描述

使用舍入方法返回一组值中最近的 INTEGERFLOAT 值到给定百分位数。

参数

名称

类型

描述

input

INTEGER | FLOAT

要聚合的值。

百分位数

FLOAT

介于 0.0 和 1.0 之间的百分位数。

返回

INTEGER | FLOAT

注意事项

任何 null 值都会从计算中排除。

percentileDisc(null, percentile) 返回 null

示例 15. percentileDisc()
查询
MATCH (p:Person)
RETURN percentileDisc(p.age, 0.5)

返回属性 age 中值的第 50 个百分位数。

结果
percentileDisc(p.age, 0.5)

58

行:1

stDev()

详细信息

语法

stDev(input)

描述

返回给定值在一组中对于总体样本的标准差。

参数

名称

类型

描述

input

FLOAT

要计算标准差的值。

返回

FLOAT

注意事项

任何 null 值都会从计算中排除。

stDev(null) 返回 0

示例 16. stDev()
查询
MATCH (p:Person)
WHERE p.name IN ['Keanu Reeves', 'Liam Neeson', 'Carrie Anne Moss']
RETURN stDev(p.age)

返回属性 age 中值的标准差。

结果
stDev(p.age)

7.937253933193772

行:1

stDevP()

详细信息

语法

stDevP(input)

描述

返回给定值在一组中对于整个总体的标准差。

参数

名称

类型

描述

input

FLOAT

要计算总体标准差的值。

返回

FLOAT

注意事项

任何 null 值都会从计算中排除。

stDevP(null) 返回 0

示例 17. stDevP()
查询
MATCH (p:Person)
WHERE p.name IN ['Keanu Reeves', 'Liam Neeson', 'Carrie Anne Moss']
RETURN stDevP(p.age)

返回属性 age 中值的总体标准差。

结果
stDevP(p.age)

6.48074069840786

行:1

sum()

详细信息

语法

sum(input)

描述

返回一组 INTEGERFLOATDURATION 值的总和。

参数

名称

类型

描述

input

INTEGER | FLOAT | DURATION

要聚合的值。

返回

INTEGER | FLOAT | DURATION

注意事项

任何 null 值都会从计算中排除。

sum(null) 返回 0

示例 18. sum() - 数值
查询
MATCH (p:Person)
RETURN sum(p.age)

返回属性 age 中所有值的总和。

结果
sum(p.age)

309

行:1

示例 19. sum() - 持续时间值
查询
UNWIND [duration('P2DT3H'), duration('PT1H45S')] AS dur
RETURN sum(dur)

返回两个提供的持续时间的总和。

结果
sum(dur)

P2DT4H45S

行:1

聚合表达式和分组键

聚合表达式是包含一个或多个聚合函数的表达式。一个简单的聚合表达式包含一个聚合函数。例如,sum(x.a) 是一个聚合表达式,它只包含聚合函数 sum( ),其中 x.a 是它的参数。聚合表达式也可以更复杂,其中一个或多个聚合函数的结果是其他表达式的输入参数。例如,0.1 * (sum(x.a) / count(x.b)) 是一个聚合表达式,它包含两个聚合函数,sum( ),其中 x.a 是它的参数,以及 count( ),其中 x.b 是它的参数。两者都是除法表达式的输入参数。

分组键是非聚合表达式,用于对输入聚合函数的值进行分组。例如,给定以下查询,其中包含两个返回表达式,ncount(*)

RETURN n, count(*)

第一个,n 不是聚合函数,因此它将是分组键。第二个,count(*) 是一个聚合函数。匹配的路径将被划分为不同的桶,具体取决于分组键。然后,聚合函数将在这些桶上运行,计算每个桶的聚合值。

聚合函数的输入表达式可以包含任何表达式,包括非分组键表达式。但是,并非所有表达式都可以与聚合函数组合。下面的示例将抛出错误,因为 n.x(不是分组键)与聚合函数 count(*) 组合在一起。

RETURN n.x + count(*)

要使用聚合函数对结果集进行排序,必须在 RETURN 子句后面的 ORDER BY 子句中包含聚合。

示例

示例 20. 没有分组键的简单聚合
查询
MATCH (p:Person)
RETURN max(p.age)
结果
max(p.age)

71

行:1

示例 21. 聚合和常量的加法,没有分组键
查询
MATCH (p:Person)
RETURN max(p.age) + 1
结果
max(p.age) + 1

72

行:1

示例 22. 属性访问和聚合的减法

注意 p 是一个分组键

查询
MATCH (p:Person{name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
RETURN p, p.age - max(f.age)
结果
p p.age - max(f.age)

{{"name":"Keanu Reeves","age":58}}

-13

行:1

示例 23. 属性访问和聚合的减法。

注意 p.age 是一个分组键

查询
MATCH (p:Person {name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
RETURN p.age, p.age - max(f.age)
结果
p.age p.age - max(f.age)

58

-13

行:1

分组键本身可以是复杂的表达式。为了更好地查询可读性,Cypher 仅在聚合表达式中将子表达式识别为分组键,如果分组键是以下之一:

  • 变量 - 例如 RETURN p, p.age - max(f.age) 中的 p

  • 属性访问 - 例如 RETURN p.age, p.age - max(f.age) 中的 p.age

  • 映射访问 - 例如 WITH {name:'Keanu Reeves', age:58} AS p RETURN p.age, p.age - max(p.age) 中的 p.age

如果需要更复杂的分组键作为聚合表达式的操作数,则始终可以使用 WITH 预先投影它们。

使用属性 p.age 将会抛出异常,因为 p.age 不是分组键。因此,它不能用于包含聚合函数的表达式中。因此,以下两个查询将返回相同的错误消息

查询
MATCH (p:Person {name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
RETURN p.age - max(f.age)
查询
MATCH (p:Person {name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
RETURN p.age + p.age, p.age + p.age - max(f.age)
错误消息
Aggregation column contains implicit grouping expressions. For example, in 'RETURN n.a, n.a + n.b + count(*)' the aggregation expression 'n.a + n.b + count(*)' includes the implicit grouping key 'n.b'. It may be possible to rewrite the query by extracting these grouping/aggregation expressions into a preceding WITH clause. Illegal expression(s): n.age

但是,如果将后面的查询改写为以下形式,则可以正常工作

查询
MATCH (p:Person {name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
WITH p.age + p.age AS groupingKey, f
RETURN groupingKey, groupingKey - max(f.age)
结果
分组键 分组键 - max(f.age)

116

45

行:1

聚合表达式的规则

为了使聚合表达式能够针对由分组键形成的桶正确计算,它们必须满足一些要求。具体来说,聚合表达式中的每个子表达式都必须是:

  • 一个聚合函数,例如 sum(x.a)

  • 一个常量,例如 0.1

  • 一个参数,例如 $param

  • 一个分组键,例如 RETURN a, count(*) 中的 a

  • 一个局部变量,例如 count(*) + size([ x IN range(1, 10) | x ]) 中的 x

  • 一个子表达式,所有操作数都必须在聚合表达式中被允许。