聚合函数

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

要详细了解 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

使用 count(*) 按关系类型分组和计数

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

示例 5. count()
查询
MATCH (p:Person {name: 'Keanu Reeves'})-[r]->()
RETURN type(r), count(*)

匹配关系类型被分组并返回关系类型的分组计数

结果
type(r) count(*)

"ACTED_IN"

1

"KNOWS"

3

行数: 2

计数非 null

除了简单地使用 count(*) 返回行数外,函数 count(expression) 还可以用于返回表达式返回的非 null 值的数量。

示例 6. count()
查询
MATCH (p:Person)
RETURN count(p.age)

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

结果
count(p.age)

5

行数: 1

计数时包含和不包含重复项

count 函数的默认行为是计算所有匹配结果,包括重复项。为避免计算重复项,请使用 DISTINCT 关键字。

自 Neo4j 5.15 起,聚合函数也可以使用 ALL 关键字。这将计算所有结果,包括重复项,功能上与不使用 DISTINCT 关键字相同。ALL 关键字作为 Cypher GQL 合规性的一部分引入。

此示例尝试查找 Keanu Reeves 的所有朋友的朋友并计数。它展示了使用 ALLDISTINCT 关键字的行为

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

节点 Carrie Anne MossLiam Neeson 都与 Guy Pearce 有一个传出的 KNOWS 关系。因此,当不使用 DISTINCT 时,Guy Pearce 节点将被计数两次。

结果
friendOfFriend.name count(friendOfFriend) count(ALL friendOfFriend) count(DISTINCT friendOfFriend)

"Guy Pearce"

2

2

1

行数: 1

max()

详情

语法

max(input)

描述

返回一组值中的最大值。

参数

名称

类型

描述

input

ANY

要聚合的值。

返回

ANY

注意事项

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

在混合集中,任何数值始终被视为高于任何 STRING 值,任何 STRING 值始终被视为高于任何 LIST<ANY>

列表按字典顺序比较,即列表元素从列表的开头到结尾按升序逐对比较。

max(null) 返回 null

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

返回混合集中所有值中的最高值——在本例中为数值 1

结果
max(val)

1

行数: 1

'99' (一个 STRING) 被认为是低于 1 (一个 INTEGER) 的值,因为 '99' 是一个 STRING

示例 9. max()
查询
UNWIND [[1, 'a', 89], [1, 2]] AS val
RETURN max(val)

返回集合中所有列表中的最高值——在本例中为列表 [1, 2]——因为数字 2 被认为是高于字符串 'a' 的值,即使列表 [1, 'a', 89] 包含更多元素。

结果
max(val)

[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

列表按字典顺序比较,即列表元素从列表的开头到结尾按升序逐对比较。

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,因此 0.2 被认为是高于 "1" 的值。

结果
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

要聚合的值。

percentile

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

要聚合的值。

percentile

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)
结果
groupingKey groupingKey - 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

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

© . All rights reserved.