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({ |
本文转载自: 掘金