聚合

简介

通常,聚合 aggr(expr) 会处理传入记录中找到的每个聚合键的所有匹配行(键使用等价性进行比较)。

在常规聚合(即 aggr(expr) 形式)中,聚合值列表是从候选值列表中移除所有 null 值后的列表。

注意! 在 YMatrix AGE 中,聚合的结果是无序的。如果想要聚合的结构有序,则需要在cypher 子句外面给cypher 子查询排序,cypher内的 order by 无用(只能保证在Segments结果有序,不能保证全局有序)。 Example: SELECT * FROM cypher('graph_name', $$> /* aggr(expr) */>$$) AS (grouping_key agtype) **ORDER BY grouping_key**;

数据准备

SELECT * FROM cypher('graph_name', $$
        CREATE (a:Person {name: 'A', age: 13}),
        (b:Person {name: 'B', age: 33, eyes: "blue"}),
        (c:Person {name: 'C', age: 44, eyes: "blue"}),
        (d1:Person {name: 'D', eyes: "brown"}),
        (d2:Person {name: 'D'}),
        (a)-[:KNOWS]->(b),
        (a)-[:KNOWS]->(c),
        (a)-[:KNOWS]->(d1),
        (b)-[:KNOWS]->(d2),
        (c)-[:KNOWS]->(d2)
$$) as (a agtype);

自动分组(Auto Group By)

为了计算聚合数据,Cypher 提供了聚合功能,类似于 SQL 的 GROUP BY

聚合函数接受一组值并计算它们的聚合值。例如 avg() 计算多个数值的平均值,或 min() 查找一组值中最小的数值或字符串值。当我们说聚合函数对一组值进行操作时,我们指的是将内部表达式(如 n.age)应用于同一聚合组内所有记录的结果。

聚合可以在所有匹配的子图上计算,也可以通过引入分组键进一步划分。这些是非聚合表达式,用于对进入聚合函数的值进行分组。

假设我们有以下 return 语句:

SELECT * FROM cypher('graph_name', $$
        MATCH (v:Person)
        RETURN v.name, count(*)
$$) as (grouping_key agtype, count agtype);
count key
"A" 1
"B" 1
"C" 1
"D" 2
1 row

我们有两个返回表达式:grouping_keycount(*)。第一个 grouping_key 不是聚合函数,因此它将成为分组键。后者 count(*) 是聚合表达式。匹配的子图将根据分组键被划分到不同的桶中。然后聚合函数将在这些桶上运行,为每个桶计算一个聚合值。

Distinct 聚合

在 distinct 聚合(即 aggr(DISTINCT expr) 形式)中,聚合值列表是从候选值列表中移除所有 null 值后的列表。此外,在 distinct 聚合中,所有等价候选值中只有一个被包含在聚合值列表中,即在等价性下的重复值被移除。

DISTINCT 运算符与聚合配合使用。它用于在通过聚合函数运行之前使所有值唯一。

SELECT *
FROM cypher('graph_name', $$
        MATCH (v:Person)
        RETURN count(DISTINCT v.eyes), count(v.eyes)
$$) as (distinct_eyes agtype, eyes agtype);
distinct_eyes eyes
2 3
1 row

歧义分组语句

不要求用户为查询指定分组键的这一特性允许 Cypher 在确定分组键时存在歧义。

数据准备

SELECT * FROM cypher('graph_name', $$
CREATE (:L {a: 1, b: 2, c: 3}),
       (:L {a: 2, b: 3, c: 1}),
       (:L {a: 3, b: 1, c: 2})
$$) as (a agtype);

AGE 中的无效查询

AGE 对此问题的解决方案是不允许 WITHRETURN 列将聚合函数与未在同一 WITHRETURN 子句的另一列中显式列出的变量组合。

查询:

SELECT * FROM cypher('graph_name', $$
        MATCH (x:L)
        RETURN x.a + count(*) + x.b + count(*) + x.c
$$) as (a agtype);

结果:

ERROR:  "x" must be either part of an explicitly listed key or used inside an aggregate function
LINE 3: RETURN x.a + count(*) + x.b + count(*) + x.c

AGE 中的有效查询

在 AGE 中,不包含聚合函数的列被视为该 WITHRETURN 子句的分组键。

对于上述查询,用户可以用几种方式重写查询以返回结果:

查询:

SELECT * FROM cypher('graph_name', $$
        MATCH (x:L)
        RETURN (x.a + x.b + x.c) + count(*) + count(*), x.a + x.b + x.c
$$) as (count agtype, key agtype);

x.a + x.b + x.c 是分组键。以这种方式创建的分组键必须包含括号。

结果:

count key
12 6
1 row

查询:

SELECT * FROM cypher('graph_name', $$
        MATCH (x:L)
        RETURN x.a + count(*) + x.b + count(*) + x.c, x.a, x.b, x.c
$$) as (count agtype, a agtype, b agtype, c agtype);

x.ax.bx.c 将被视为不同的分组键。

结果:

count a b c
8 3 1 2
8 2 3 1
8 1 2 3
3 rows

歧义分组中的顶点和边

或者,分组键可以是顶点或边,然后可以指定顶点或边的任何属性,而无需在 WITHRETURN 列中显式声明。

SELECT * FROM cypher('graph_name', $$
        MATCH (x:L)
        RETURN count(*) + count(*) + x.a + x.b + x.c, x
$$) as (count agtype, key agtype);

结果将按 x 分组,因为可以安全地假设属性对于分组的明确性是不必要的。

隐藏不需要的分组键

如果分组键对于查询输出是不必要的,可以在 WITH 子句中进行聚合,然后将信息传递给 RETURN 子句。

SELECT * FROM cypher('graph_name', $$
        MATCH (x:L)
        WITH count(*) + count(*) + x.a + x.b + x.c as column, x
        RETURN column
$$) as (a agtype);

结果:

a
8
8
8
3 rows