• 分组

分组搜索结果通常有助于获取每个组的匹配计数或其他聚合信息。例如,这对于创建显示每月匹配博客文章数量的图表,或者按网站对网页搜索结果进行分组、按作者对论坛帖子进行分组等非常有用。

Manticore 支持按单列、多列或计算表达式对搜索结果进行分组。结果可以:

  • 在组内排序

  • 返回每个组中超过一行的结果

  • 对组进行过滤

  • 对组进行排序

  • 使用 聚合函数 进行聚合

通用语法:

通用语法:

SELECT {* | SELECT_expr [, SELECT_expr ...]}
...
GROUP BY {field_name | alias } [, ...]
[HAVING where_condition]
[WITHIN GROUP ORDER BY field_name {ASC | DESC} [, ...]]
...

SELECT_expr: { field_name | function_name(...) }
where_condition: {aggregation expression alias | COUNT(*)}

JSON 查询格式目前支持基本的分组功能,能够检索聚合值及其 count(*)

标准查询输出返回未分组的结果集,可以通过使用 limit(或 size)来隐藏它。要进行聚合,需要为组的结果集设置一个 size

仅分组

分组非常简单——只需在 SELECT 查询末尾添加 "GROUP BY smth"。该分组依据可以是:

  • 表中的任何非全文字段:整数、浮点数、字符串、MVA(多值属性)

  • 或者,如果在 SELECT 列表中使用了别名,也可以按此别名进行分组

您可以省略 SELECT 列表中的任何聚合函数,查询仍然能够正常运行。

示例:

不过,在大多数情况下,您可能希望为每个组获取一些聚合数据,例如:

  • COUNT(*) 用于获取每个组中的元素数量

  • 或者 AVG(field) 用于计算组内该字段的平均值

示例:

对组进行排序

默认情况下,组没有排序,接下来您通常希望根据某些内容对它们进行排序,比如按您分组的字段进行排序:

示例:

或者,您可以按聚合结果进行排序:

  • count(*) 排序,以首先显示包含最多元素的组

  • avg(rental_rate) 排序,以首先显示评分最高的电影。请注意,在示例中,这是通过别名完成的:avg(rental_rate) 首先在 SELECT 列表中映射为 avg,然后我们简单地执行 ORDER BY avg

示例:

同时按多个字段分组

在某些情况下,您可能希望不仅按一个字段分组,还可以同时按多个字段分组,例如按电影的类别和年份:

示例:

返回 N 行

有时查看每组中的多个元素而不仅仅是一个是很有用的。可以通过使用 GROUP N BY 来轻松实现。例如,在以下情况下,我们为每个年份返回两部电影,而不是仅通过 GROUP BY release_year 返回的一部。

示例:

在组内排序

另一个关键的分析需求是对组内的元素进行排序。要实现此功能,可以使用 WITHIN GROUP ORDER BY ... {ASC|DESC} 子句。例如,我们可以获取每年评分最高的电影。需要注意的是,它与 ORDER BY 并行工作:

  • WITHIN GROUP ORDER BY组内 的结果进行排序

  • GROUP BY 则对 组本身 进行排序

这两者是完全独立运行的。

示例:

过滤组

HAVING expression 是一个用于过滤组的有用子句。WHERE 在分组之前应用,而 HAVING 则用于处理分组后的结果。例如,我们可以保留那些电影年均租赁费率高于 3 的年份。结果只返回了四个年份:

示例:

请注意,HAVING 不会影响 查询元信息中的 total_found

GROUPBY()

GROUPBY() 是一个返回当前组键的函数,在许多情况下非常有用,尤其是在您按 MVA(多值属性)分组按JSON 值分组时。

它也可以在 HAVING 中使用,例如,仅保留年份为 2000 和 2002 的组。

请注意,当您同时按多个字段分组时,不建议使用 GROUPBY()。尽管它仍然可以工作,但由于此时组键是字段值的复合体,可能不会按您预期的方式显示。

示例:

按 MVA(多值属性)分组

Manticore 支持按 MVA 分组。为了演示其工作原理,我们可以创建一个包含 MVA 字段 "sizes" 的表 "shoes",并向其中插入一些文档:

因此,我们有以下数据:

现在如果我们按 "sizes" 进行分组,它将处理所有的多值属性,并为每个值返回一个聚合结果,在这个例子中仅返回计数:

示例:

按 JSON 节点分组

如果您有一个 JSON 类型的字段,您可以按其中的任何节点进行分组。为了演示这一点,我们可以创建一个表 "products",并插入几个文档,每个文档在 "meta" JSON 字段中都有一个颜色信息:

这会生成以下结果:

要按颜色对产品进行分组,只需使用 GROUP BY meta.color,并在 SELECT 列表中使用 GROUPBY() 显示相应的分组键:

示例:

聚合函数

除了 COUNT(*) 返回每个组中的元素数量之外,您还可以使用其他各种聚合函数:

COUNT(DISTINCT field)

COUNT(*) 返回组中所有元素的数量,而 COUNT(DISTINCT field) 返回组中特定字段的唯一值数量,这可能与总数完全不同。例如,组中可能有 100 个元素,但某个字段的值相同。COUNT(DISTINCT field) 有助于确定这一点。为了演示这一点,我们创建一个包含学生姓名、年龄和专业的 "students" 表:

我们有以下数据:

在这个例子中,您可以看到如果我们按 major 分组并显示 COUNT(*)COUNT(DISTINCT age),就可以发现专业为 "cs" 的两名学生有两个不同的年龄,而专业为 "arts" 的两名学生只有一个唯一的年龄。

每个查询最多只能有一个 COUNT(DISTINCT)

默认情况下,计数是近似的

实际上,某些计数是精确的,而另一些是近似的。下面会详细说明。

Manticore 支持两种算法来计算唯一值的计数。一种是传统算法,使用大量内存,通常较慢。它收集 {group; value} 对,将其排序,并定期丢弃重复项。这种方法的好处是可以在普通表中保证精确计数。您可以通过将 distinct_precision_threshold 选项设置为 0 来启用此算法。

另一种算法(默认启用)将计数加载到哈希表中并返回其大小。如果哈希表变得过大,其内容会转移到 HyperLogLog 中。此时,计数变为近似值,因为 HyperLogLog 是一种概率算法。它的优势在于每组的最大内存使用是固定的,并且取决于 HyperLogLog 的精度设置。

distinct_precision_threshold 选项设置了确保计数精确的阈值。HyperLogLog 的精度设置以及从哈希表转换为 HyperLogLog 的阈值取决于此设置。谨慎使用此选项,因为将其值加倍将使计算计数所需的最大内存加倍。最大内存使用可以使用以下公式大致估算:64 * max_matches * distinct_precision_threshold。请注意,这只是最坏情况下的估算,在大多数情况下,计算计数会使用显著更少的内存。

在包含多个磁盘块的实时表或分布式表中,COUNT(DISTINCT) 的结果可能不准确,但对于由具有相同架构的本地普通表或实时表组成的分布式表,结果应是准确的。

示例:

GROUP_CONCAT(field)

通常,您可能希望更好地了解每个组的内容。可以使用 GROUP N BY,但这会返回您可能不希望在输出中显示的其他行。GROUP_CONCAT() 可以通过将特定字段的值连接在一起来丰富您的分组。例如,使用之前的例子,可以显示每个组中的所有年龄。

GROUP_CONCAT(field) 以逗号分隔的形式返回字段值列表。

示例:

SUM(), MIN(), MAX(), AVG()

当然,您还可以获得组内的总和、平均值、最小值和最大值。

示例:

分组精度

分组在固定内存中进行,内存的使用取决于 max_matches 设置。如果 max_matches 允许存储所有找到的组,结果将是 100% 准确的。然而,如果 max_matches 的值较低,结果的准确性将降低。

当涉及并行处理时,情况可能会变得更加复杂。当启用了 pseudo_sharding 和/或使用包含多个磁盘块的实时表时,每个块或伪分片的结果集不会超过 max_matches。这可能会导致合并不同线程的结果集时聚合和组计数不准确。为了解决这个问题,可以使用更大的 max_matches 值,或者禁用并行处理。

如果 Manticore 检测到 groupby 可能返回不准确的结果,它会尝试将 max_matches 增加到 max_matches_increase_threshold。该检测基于从辅助索引中获取的 groupby 属性的唯一值数量(如果存在)。

要确保使用实时表或 pseudo_sharding 时的精确聚合和/或组计数,可以启用 accurate_aggregation。这将尝试将 max_matches 增加到阈值,如果阈值不够高,Manticore 将禁用该查询的并行处理。

示例:

最后更新于