一般查询可以通过 find() 方法,但如果是比较复杂的查询或者数据统计的话,find() 方法可能就无能为力,这时需要聚合(aggregate)。
聚合操作处理数据文档并返回计算结果。聚合操作将来自多个文档的值分组在一起,可以对分组的数据执行各种操作以返回单个结果。
MongoDB 提供了三种执行聚合的方法:
聚合管道可以对数据文档进行变换和组合。聚合管道是基于数据流概念,数据进入管道经过一个或多个 stage,每个 stage 对数据进行操作(筛选,投射,分组,排序,限制或跳过)后输出最终结果。
聚合管道语法
db.collection.aggregate(pipeline, options)
注:聚合管道可以对分片集合进行操作
db.collection.aggregate( [ { <stage> },... ] )
MongoDB 聚合管道由多个 stage 阶段组成。每个 stage 阶段在文档通过管道时转换文档。管道阶段可以在管道中出现多次。
db.collection.aggregate([]) 是聚合管道查询使用的方法,参数是数组,每个数组元素就是一个stage,stage 中运用操作符对数据进行处理后再交由下一个stage,直到没有下个stage,就输出最终的结果,而数据的处理则是通过使用管道操作符。
mongoDB 有 4 类操作符用于文档的操作(操作符以 $ 开头)
在 aggregate 中每个 stage 可以使用的操作符叫做管道操作符
db.collection.aggregate( [ { 阶段操作符:表述 }, { 阶段操作符:表述 }, ... ] )
阶段操作符 | 操作符名称 | 说明 |
---|---|---|
$count | 统计操作符 | 用于统计文档的数量 |
$group | 分组操作符 | 用于对文档集合进行分组 |
$limit | 限制操作符 | 用于限制返回文档的数量 |
$match | 匹配操作符 | 用于对文档集合进行筛选 |
$out | 输出操作符 | 将聚合管道的结果文档写入集合。要使用 $out阶段,它必须是管道中的最后一个阶段。 |
$project | 投射操作符 | 用于重构每一个文档的字段,可以提取字段,重命名字段,甚至可以对原有字段进行操作后新增字段 |
$skip | 跳过操作符 | 用于跳过指定数量的文档 |
$sort | 排序操作符 | 用于根据一个或多个字段对文档进行排序 |
$unwind | 拆分操作符 | 用于将数组中的每一个值拆分为单独的文档 |
$lookup | 连接操作符 | 用于连接同一个数据库中另一个集合,并获取指定的文档,类似于populate |
$addFields | 字段操作符 | 用于给聚合管道的结果文档添加字段 |
$match 语法
匹配操作符,用于对文档集合进行筛选
{ $match: { <query> } }
$project 语法
用于重构每一个文档的字段,可以提取字段,重命名字段,甚至可以对原有字段进行操作后新增字段
{ $project: { <specification(s)> } }
specification 的规则如下:
规则 | 描述 |
---|---|
<字段名>: 1 or true | 选择需要显示什么字段 |
_id: 0 or false | 不显示 _id (默认显示) |
<字段名>: 表达式 | 使用表达式,可以用于重命名字段,或对其值进行操作,或新增字段 |
<字段名>: 0 or false | 选择需要不返回什么字段,注意:当使用这种用法时,就不要用上面的方法 |
$addFields 语法
在聚合管道结果添加一些字段信息或者修改字段信息
{ $addField: <document> }
$skip 语法
用于跳过指定数量的文档
{ $skip: <positive integer> }
$limit 语法
用于限制返回文档的数量
{ $limit: <positive integer> }
$count 语法
{ $count: <string> }
$sort 语法
用于根据一个或多个字段对文档进行排序
{ $sort: { <field1>: <sort order>,<field2>: <sort order> ... } }
$out 语法
{ $out: "<output-collection>" }
$unwind 语法
用于将数组中的每一个值拆分为单独的文档
{ $unwind: { path: <field path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays:<boolean> } }
$lookup 语法
用于连接同一个数据库中另一个集合,并获取指定的文档
{ $lookup: { from: <collection to join>, localField: <field from the input documents>, foreignField: <field from the documents of the "from" collection>, as: <output array field> } }
字段 | 描述 |
---|---|
from | 需要关联的集合名 |
localField | 本集合中需要查找的字段 |
foreignField | 另外一个集合中需要关联的字段 |
as | 输出的字段名 |
$group 语法
用于对文档集合进行分组
{ $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } }
表达式(Expression)
表达式可以包括字段路径和系统变量、文本、表达式对象和表达式操作符。表达式可以嵌套。
{ <field1>: <expression1>, ... }
字段路径表达式:$
"$<field>” 等价于 “$$CURRENT.<field>”,其中 CURRENT 是一个系统变量,在大多数阶段默认为当前对象的根.
聚合表达式使用字段路径访问输入文档中的字段。若要指定字段路径,请使用前缀为$符号字段名的字符串,如果字段在嵌入的文档中,则使用点语法。
系统变量表达式:$$系统变量
$$CURRENT 表示当前文档
$$ROOT 整个文档
字面量表达式:返回不需要解析的值。用于聚合管道可解释为表达式的值
表达式对象
表达式操作符
表达式操作符
表达式操作符主要用于在管道中构建表达式时使用,使用类似于函数那样需要参数,主要用于 $project 操作符中,用于构建表达式,使用方法一般如下:
{ <operator>: [ <argument1>, <argument2> ... ] } # 或 { <operator>: <argument> }
累加器操作符 | 说明 |
---|---|
$avg | 返回数值的平均值(忽略非数字值) |
$max | 返回每个组的最高表达式值 |
$min | 返回每个组的最低表达式值 |
$push | 返回每个组的表达式值组成的数组 |
$sum | 返回数值的和(忽略了非数字值) |
$avg 语法
{ $avg: [ <expression1>, <expression2>... ] }
$max 语法
{ $max: <expression> } { $max: [ <expression1>, <expression2>... ] }
$push 语法
{ $push: <expression> }
$sum 语法
{ $sum: <expression> }
聚合管道操作有一个优化阶段,该阶段试图重塑管道以提高性能
$project or $addFields) 和 $match 顺序优化
$sort 和 $match 顺序优化
$skip 和 $limit 顺序优化
$project 和 ($skip or $limit) 顺序优化
当 $sort 紧邻 $limit 时,优化器可以将 $limit 合并到 $sort 中。这允许 $sort 操作在进行过程中只维护顶部的 n 个结果,其中 n 是指定的限制,MongoDB 只需要在内存中存储 n 个条目。
当一个 $limit 紧接另一个 $limit 之后,这两个阶段可以合并为一个 $limit,其中 $limit 值是两个初始 $limit 值中较小的一个。
当一个 $skip 紧接着另一个 $skip 时,这两个阶段可以合并为单个 $skip,其中 $skip 量是两个初始 $skip 量的总和。
当一个 $match 紧跟在另一个 $match 之后时,这两个阶段可以合并为一个结合了条件和 $and 的单个 $match
$sort 和 $skip 和 $limit 联合优化
$limit 和 $skip 和 $limit 和 $skip 联合优化