mongo的聚合操作和mysql的查询类比
SQL 操作/函数 | mongodb聚合操作 |
---|---|
where | $match |
group by | $group |
having | $match |
select | $project |
order by | $sort |
limit | $limit |
sum() | $sum |
count() | $sum |
join | $lookup |
下面一些例子和sql做对比
下面是使用数据库的一个基本结构
1 | json复制代码{ |
先来一些操作案例
1 | sql复制代码select sum(total) from orders |
1 | sql复制代码select count(1) from orders |
1 | sql复制代码select count(1) from orders group by status |
1 | sql复制代码select count(1) from orders group by status having count(1) > 20000 |
1 | sql复制代码select count(1) total |
1 | sql复制代码select * |
1 | sql复制代码select count(*) from orders where month(orderDate) >= 3 group by month(orderDate) |
MongoDB聚合(Aggregate)
MongoDB
中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果
一个aggregate
由多个阶段(Stage)组成。上一阶段产生的结果会作为下一阶段的输入,所以也会被形象的称为流水线(Pipeline)。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。
这里我们介绍一下聚合框架中常用的几个操作:
$project
:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。$match
:用于过滤数据,只输出符合条件的文档。$match
使用MongoDB
的标准查询操作。$limit
:用来限制MongoDB
聚合管道返回的文档数。$skip
:在聚合管道中跳过指定数量的文档,并返回余下的文档。$unwind
:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。$group
:将集合中的文档分组,可用于统计结果。$sort
:将输入文档排序后输出。$geoNear
:输出接近某一地理位置的有序文档。
下面是一个aggregate
的基本处理流程
db.collection.aggregate()
可以用多个构件创建一个管道,对于一连串的文档进行处理。这些构件包括:筛选操作的match
、映射操作的project
、分组操作的group
、排序操作的sort
、限制操作的limit
、和跳过操作的skip
。db.collection.aggregate()
使用了MongoDB
内置的原生操作,聚合效率非常高,支持类似于SQL Group By
操作的功能。- 每个阶段管道限制为
100MB
的内存。如果一个节点管道超过这个极限,MongoDB
将产生一个错误。为了能够在处理大型数据集,可以设置allowDiskUse
为true来在聚合管道节点把数据写入临时文件。这样就可以解决100MB
的内存的限制。 db.collection.aggregate()
可以作用在分片集合,但结果不能输在分片集合,MapReduce
可以 作用在分片集合,结果也可以输在分片集合。db.collection.aggregate()
方法可以返回一个指针(cursor),数据放在内存中,直接操作。跟Mongo shell
一样指针操作。db.collection.aggregate()
输出的结果只能保存在一个文档中,BSON Document
大小限制为16M
。可以通过返回指针解决,版本2.6中后面:db.collect.aggregate()
方法返回一个指针,可以返回任何结果集的大小。
$count
返回文档统计数
先看一些非聚合操作中的count
使用方法
1 | sql复制代码#对应查询出来的是orders这个集合中的所有数据总和 |
使用聚合操作中$count
来汇总行数
1 | sql复制代码#使用聚合查出来自Malaysia这个国家的订单总和,并且返回给counts字段 |
除此以外可以灵活使用group
+$sum
来实现$count
1 | sql复制代码#对应查询出来的是orders这个集合中的所有数据总和,并且返回给counts字段 |
$group
按照指定表达式对文档进行分组
$group
使用的基本语法:
1 | json复制代码{ $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } } |
_id
+表达式用来做分组条件,也就是_id
后面的内容与sql
中group by
后面的表达式的用途相同_id
后面的 字段+accumulator
操作符与sql
中做完group by
后在select
后面的的聚合函数用途相同,例如:sum()
、avg()
、max()
、min()
例如:
1 | sql复制代码db.orders.aggregate({$group:{_id:"$country",total:{$sum:"$total"}}}) |
$group
阶段的内存限制为100M
。默认情况下,如果stage
超过此限制,$group
将产生错误。但是,要允许处理大型数据集,请将allowDiskUse
选项设置为true
以启用$group
操作以写入临时文件。
名称 | 描述 | 类比sql |
---|---|---|
$avg | 计算均值 | avg |
$first | 返回每组第一个文档,如果有排序,按照排序,如果没有按照默认的存储的顺序的第一个文档。 | limit 0,1 |
$last | 返回每组最后一个文档,如果有排序,按照排序,如果没有按照默认的存储的顺序的最后个文档。 | - |
$max | 根据分组,获取集合中所有文档对应值得最大值。 | max |
$min | 根据分组,获取集合中所有文档对应值得最小值。 | min |
$push | 将指定的表达式的值添加到一个数组中。 | - |
$addToSet | 将表达式的值添加到一个集合中(无重复值,无序)。 | - |
$sum | 计算总和 | sum |
$stdDevPop | 返回输入值的总体标准偏差(population standard deviation) | - |
$stdDevSamp | 返回输入值的样本标准偏差(the sample standard deviation) | - |
下面我们按照以上文档依次用一下每一个表达式
$avg
计算平均值
1 | json复制代码--计算每个国家的每个订单的平均消费 |
$first
返回第一个文档
1 | json复制代码--根据国家分组,每组第一笔订单的订单商品列表 |
$last
返回最后一个文档
1 | json复制代码--根据每个国家分组,每笔最后一个订单的orderDate |
$max
和$min
:最大值和最小值
1 | json复制代码--根据年月分组,查出每组第一笔订单时间和最后一组订单时间 |
$push
将指定值添加到一个数组当中可以push到一个已经存在的数组当中,如果不存在会创建这样一个数组
1 | json复制代码--根据城市、年、月分组,将每组的下单时间push到一个新的 orderDates 数组当中 |
$addToSet
将指定值添加到一个集合当中集合是无序的并且会去重
1 | json复制代码--根据月份分组,将每个月都下单过的国家都添加到 countrySet 中去 |
$sum
计算总和
1 | json复制代码--根据月份分组,获取每组的收入总和 sumTotal |
$stdDevPop
返回输入值的总体标准偏差
1 | json复制代码--根据月份分组,计算总体准偏差计算 |
$stdDevSamp
返回输入值的样本标准偏差
1 | json复制代码--根据月份分组,计算样本准偏差计算 |
$match
接受一个指定查询条件的文档。查询语法与读操作查询语法相同。
基本的语法{ $match: { <query> } }
在实际应用中尽可能将$match
放在管道的前面位置。这样有两个好处:
- 可以快速将不需要的文档过滤掉,以减少管道的工作量
- 如果再投射和分组之前执行
$match
,查询可以使用索引。
1 | json复制代码--类似于in查询 |
1 | json复制代码--范围查询 |
$expr
使用聚合表达式
1 | json复制代码--查询3月份以及往后的数据 - 根据月份分组 - sum订单数 |
$mod
使用取模运算符
1 | json复制代码--查询total属性后面是00结尾的订单 |
$regex
使用正则表达式匹配
1 | json复制代码--以184开头的手机号的订单数量 |
$unwind
将数组拆分成单独的文档
格式
1 | lua复制代码{ |
includeArrayIndex
:可选,一个新字段的名称用于存放元素的数组索引。该名称不能以$开头。
preserveNullAndEmptyArrays
:可选,默认为false,若为true,如果path没有对应的字段或者对应的数组size为0,则$unwind
输出文档,默认false不输出。
1 | json复制代码--筛选一条数据,将数组拆分 |
$project
从文档中指定想要的字段和不想要的字段
格式
{ $project: { <specification(s)> } }
1 | shell复制代码specifications有以下形式: |
1 | json复制代码--如果有一个属性为或几个属性为1,那么只显示这一个或几个属性 + _id |
$cond
-if-then-else
的使用相当于SQL
中的case-when-then-else
$$REMOVE
是在满足这个条件的时候移除这个属性
1 | json复制代码--不是7月的文档,移除这个属性 |
映射到一个属性包含多个属性
1 | json复制代码--使用substr截取第一个字母,使用strLenCP取name的长度 |
将多个属性的值映射到一个数组当中
1 | json复制代码db.orders.aggregate({ |
$limit
限制条数,获取前n条数据
1 | json复制代码db.orders.aggregate({ |
$skip
跳过前n行数据查询
1 | json复制代码--查询第2、3条 |
$sort
对文档进行排序 升序:1 降序:-1
1 | json复制代码--用名字排序 |
$sortByCount
根据某个字段分组计算 count 值然后按照这个值降序排序
1 | json复制代码db.orders.aggregate({ |
本文转载自: 掘金