MongoDB 聚合管道

MongoDB 聚合管道

为什么需要聚合

一般查询可以通过 find() 方法,但如果是比较复杂的查询或者数据统计的话,find() 方法可能就无能为力,这时需要聚合(aggregate)。

聚合操作处理数据文档并返回计算结果。聚合操作将来自多个文档的值分组在一起,可以对分组的数据执行各种操作以返回单个结果。

MongoDB 提供了三种执行聚合的方法:

  • 聚合管道
  • map- reduce 函数
  • 单一目的聚合方法
什么是聚合管道(aggregation pipeline)

聚合管道可以对数据文档进行变换和组合。聚合管道是基于数据流概念,数据进入管道经过一个或多个 stage,每个 stage 对数据进行操作(筛选,投射,分组,排序,限制或跳过)后输出最终结果。

聚合管道语法

db.collection.aggregate(pipeline, options)
  • pipeline:数组类型

注:聚合管道可以对分片集合进行操作

Pipeline 管道
db.collection.aggregate( [ { <stage> },... ] )

MongoDB 聚合管道由多个 stage 阶段组成。每个 stage 阶段在文档通过管道时转换文档。管道阶段可以在管道中出现多次。

聚合管道原理

db.collection.aggregate([]) 是聚合管道查询使用的方法,参数是数组,每个数组元素就是一个stage,stage 中运用操作符对数据进行处理后再交由下一个stage,直到没有下个stage,就输出最终的结果,而数据的处理则是通过使用管道操作符。

什么是管道操作符

mongoDB 有 4 类操作符用于文档的操作(操作符以 $ 开头)

  • 查询操作符
  • 更新操作符
  • 管道操作符(聚合管道中的操作符)
  • 查询修饰符

在 aggregate 中每个 stage 可以使用的操作符叫做管道操作符

管道操作符分类
  • 阶段操作符(Stage Operators)
  • 表达式操作符(Expression Operators)
  • 累加器(Accumulators)
阶段操作符
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> } 
  • string:是输出字段的名称,该字段的值为 count。string 必须是非空字符串,不能以 $ 开头,也不能包含 .点字符

$sort 语法

用于根据一个或多个字段对文档进行排序

{ $sort: { <field1>: <sort order>,<field2>: <sort order> ... } }
  • 1:升序
  • -1:降序

$out 语法

{ $out: "<output-collection>" }

$unwind 语法

用于将数组中的每一个值拆分为单独的文档

{
	$unwind:
	{
		path: <field path>, includeArrayIndex: <string>, 
		preserveNullAndEmptyArrays:<boolean>
	}
 }
  • path:字符串类型,数组字段的字段路径,若要指定字段路径,需要在字段名称前加上 $ 符号并将其括在引号内
  • includeArrayIndex:可选项,用于保存元素的数组索引的新字段的名称。名称不能以 $ 符号开头
  • preserveNullAndEmptyArrays:
    • 如果为 true,如果路径为空、没有数组字段或数组为空,则 $unwind 输出文档
    • 如果为 false,如果路径为空、没有数组字段或数组为空,则 $unwind 不输出文档(默认)

$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> }, ... } }
  • _id:是必须的,用作分组的依据条件

表达式(Expression)

表达式可以包括字段路径和系统变量、文本、表达式对象和表达式操作符。表达式可以嵌套。

{ <field1>: <expression1>, ... }
  • 字段路径表达式:$

  • "$<field>” 等价于 “$$CURRENT.<field>”,其中 CURRENT 是一个系统变量,在大多数阶段默认为当前对象的根.

  • 聚合表达式使用字段路径访问输入文档中的字段。若要指定字段路径,请使用前缀为$符号字段名的字符串,如果字段在嵌入的文档中,则使用点语法。

  • 系统变量表达式:$$系统变量

    • $$CURRENT 表示当前文档

    • $$ROOT 整个文档

  • 字面量表达式:返回不需要解析的值。用于聚合管道可解释为表达式的值

  • 表达式对象

  • 表达式操作符

表达式操作符

表达式操作符主要用于在管道中构建表达式时使用,使用类似于函数那样需要参数,主要用于 $project 操作符中,用于构建表达式,使用方法一般如下:

{ <operator>: [ <argument1>, <argument2> ... ] }
# 或
{ <operator>: <argument> }
表达式操作符分类
  • 布尔值操作符(Boolean Operators)
  • 集合操作符(Set Operators)
  • 比较操作符(Comparison Operators)
  • 数学操作符(Arithmetic Operators)
  • 字符串操作符(String Operators)
  • 文本搜索操作符(Text Search Operators)
  • 数组操作符(Array Operators)
  • 变量操作符(Variable Operators)
  • 字面量操作符(Literal Operators)
  • 日期操作符(Date Operators)
  • 条件操作符(Conditional Operators)
  • 数据类型操作符(Data Type Operators)
累加器(Accumulators)
累加器操作符 说明
$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 联合优化

当 $sort 紧邻 $limit 时,优化器可以将 $limit 合并到 $sort 中。这允许 $sort 操作在进行过程中只维护顶部的 n 个结果,其中 n 是指定的限制,MongoDB 只需要在内存中存储 n 个条目。

  • $limit 和 $limit 联合优化

当一个 $limit 紧接另一个 $limit 之后,这两个阶段可以合并为一个 $limit,其中 $limit 值是两个初始 $limit 值中较小的一个。

  • $skip 和 $skip 联合优化

当一个 $skip 紧接着另一个 $skip 时,这两个阶段可以合并为单个 $skip,其中 $skip 量是两个初始 $skip 量的总和。

  • $match 和 $match 联合优化

当一个 $match 紧跟在另一个 $match 之后时,这两个阶段可以合并为一个结合了条件和 $and 的单个 $match

  • $sort 和 $skip 和 $limit 联合优化

  • $limit 和 $skip 和 $limit 和 $skip 联合优化

posted @ 2021-11-17 18:25  嘛了对就念来过倒  阅读(178)  评论(0编辑  收藏  举报