MongoDB 聚合操作
MongoDB 聚合操作
在MongoDB中,有两种方式计算聚合:Pipeline 和 MapReduce。Pipeline查询速度快于MapReduce,但是MapReduce的强大之处在于能够在多台Server上并行执行复杂的聚合逻辑。MongoDB不允许Pipeline的单个聚合操作占用过多的系统内存,如果一个聚合操作消耗20%以上的内存,那么MongoDB直接停止操作,并向客户端输出错误消息。
一,使用 Pipeline 方式计算聚合
Pipeline 方式使用db.collection.aggregate()函数进行聚合运算,运算速度较快,操作简单,但是,Pipeline方式有两个限制:单个聚合操作消耗的内存不能超过20%,聚合操作返回的结果集必须限制在16MB以内。
创建示例数据,在集合 foo中插入1000条doc,每个doc中有三个field:idx,name 和 age。
for(i=0;i<10000;i++) { db.foo.insert({"idx":i,name:"user "+i,age:i%90}); }
1,使用$match 管道符过滤collection中doc,使符合条件的doc进入pipeline,能够减少聚合操作消耗的内存,提高聚合的效率。
db.foo.aggregate({$match:{age:{$lte:25}}})
2,使用$project 管道符,使用doc中的部分field进入下级pipeline
db.foo.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,idx:1,"_id":0}} )
$project 管道符的作用是选择字段,重命名字段,派生字段。
2.1 选择字段
在$project 管道符中,field:1/0,表示选择/不选择 field;将无用的字段从pipeline中过滤掉,能够减少聚合操作对内存的消耗。
db.foo.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,idx:1,"_id":0}} )
2.2 对字段重命名,产生新的字段
引用符$,格式是:"$field",表示引用doc中 field 的值,如果要引用内嵌 doc中的字段,使用 "$field1.filed2",表示引用内嵌文档field1中的字段:field2的值。
示例,新建一个field:preIdx,其值和idx 字段的值是相同的。
db.foo.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":"$idx",idx:1,"_id":0}} )
2.3 派生字段
在$project中,对字段进行计算,根据doc中的字段值和表达式,派生一个新的字段。
示例,preIdx是根据当前doc的idx 减1 得到的
db.foo.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":{$subtract:["$idx",1]},idx:1,"_id":0}} )
在$project 执行算术运算的操作符:+($add),*($multiply),/($divide),%($mod),-($subtract)。
对于字符数据,$substr:[expr,start,length]用于求子字符串;$concat:[expr1,expr2,,,exprn],用于将表达式连接在一起;$toLower:expr 和 $toUpper:expr用于返回expr的小写或大写形式。
2.4 分组操作
使用$group将doc按照特定的字段的值进行分组,$group将分组字段的值相同的doc作为一个分组进行聚合计算。如果没有$group 管道符,那么所有doc作为一个分组。对每一个分组,都能根据业务逻辑需要计算特定的聚合值。分组操作和排序操作都是非流式的运算符,流式运算符是指:只要有新doc进入,就可以对doc进行处理,而非流式运算符是指:必须等收到所有的文档之后,才能对文档进行处理。分组运算符的处理方式是等接收到所有的doc之后,才能对doc进行分组,然后将各个分组发送给pipeline的下一个运算符进行处理。
示例,按照age进行分组,统计每个分组中的doc数量
db.foo.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":{$subtract:["$idx",1]},idx:1,"_id":0}} , {$group:{"_id":"$age",count:{$sum:1}}} )
如果分组字段有多个,按照 age 和 age2 进行分组,这样做仅仅是为了演示,在实际的产品环境中,可以使用更多的字段用来分组。
db.foo.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":{$subtract:["$idx",1]},idx:1,"_id":0}} , {$group:{"_id":{age:"$age",age2:"$age"},count:{$sum:1}}} )
对每个分组进行聚合运算,count字段是计算每个分组中doc的数量,idxTotal字段是计算每个分组中idx字段值的加和,idxMax字段是计算每个分组中idx字段值的最大值,idxFirst是计算每个分组中第一个idx 字段的值,不一定是最小的。
db.foo.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":{$subtract:["$idx",1]},idx:1,"_id":0}} , {$group:{"_id":{age:"$age",age2:"$age"},count:{$sum:1},idxTotal:{$sum:"$idx"}},idxMax:{$max:"$idx"},idxFirst:{$first:"$idx"}} } )
2.5,sort操作,limit操作 和 skip操作
对聚合操作的结果进行排序,然后跳过前10个doc,取剩余结果集的前10个doc。
db.foo.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":{$subtract:["$idx",1]},idx:1,"_id":0}} , {$group:{"_id":{age:"$age",age2:"$age"},count:{$sum:1},idxTotal:{$sum:"$idx"}},idxMax:{$max:"$idx"},idxFirst:{$first:"$idx"}} }, {$sort:{age:-1}}, {$skip:10}, {$limit:10} )
二,使用MapReduce 方式计算聚合
MapReduce 能够计算非常复杂的聚合逻辑,非常灵活,但是,MapReduce非常慢,不应该用于实时的数据分析中。MapReduce能够在多台Server上并行执行,每台Server只负责完成一部分wordload,最后将wordload发送到Master Server上合并,计算出最终的结果集,返回客户端。
MapReduce分为两个阶段:Map和Reduce,举个例子说明,有10节车厢,统计这10节车厢中男生和女生的数量。串行方式一节一节车厢的统计,直到统计完全部车厢中的人数:男50人,女40人。
使用MapReduce方式的思路是:每个车厢派一个人去统计,每个人返回一个doc,例如,keyN:{female:num1,male:num2},keyN是车厢编号,在同一时间,有10个人在同时工作,每个人只完成全部workload的10%,很快,返回10个doc,从Key1到Key10,只需要将这10个doc中 femal 和 male分别加和到一起,就是全部车厢的人数:男50人,女40人。
使用MapReduce方式计算聚合,主要分为三步:Map,Shuffle和Reduce,Map和Reduce需要显式定义,shuffle由MongoDB来实现。
- Map:将操作映射到每个doc,产生Key和Value,例如,Map一个doc,产生(female,{count:1}),female是Key,value是{count:1}
- Shuffle:按照Key进行分组,并将key相同的Value组合成数组,例如,产生(female,[{count:1},{count:1},{count:1},{count:1},,,,,])
- Reduce:把Value数组化简为单值,例如,产生(femal,{count:21})
使用MapReduce进行聚合运算的最佳方式是聚合运算的结果能够加到一起,例如,求最大值/最小值,sum,平均值(转换为计算每台Server的 总和sum1,sum2,,,sumN 与 num1,num2,,numN,平均值avg=(sum1+sum2+,,,+sumN)/(num1+num2+,,+numN))等。
示例,使用MapReduce模拟Count,统计集合中的doc的数量
step1,定义Map函数和reduce函数
对于每个doc,直接返回key 和 一个doc:{count:1}
map=function (){ for(var key in this) {emit(key,{count:1}); } }reduce=function (key,emits){ total=0; for(var i in emits){total+=emits[i].count; } return {"count":total}; }
step2,执行MapReduce运算
在集合 foo上执行MapReduce运算,返回mr 对象
mr=db.runCommand( { "mapreduce":"foo", "map":map, "reduce":reduce, out:"Count Doc" })
step3,查看MapReduce计算的结果
db[mr.result].find()
示例2,统计集合foo中不同age的数量
step1,定义Map 和 Reduce函数
Map函数的作用是对每个doc进行一次映射,返回age 和 {count:1};
经过Shuffle,每个age都有一个列表:[{count:1},{count:1},{count:1},{count:1},,,,,],有多少个不同的age,MongoDB都会调用多少次Reduce函数,每次调用时,Key值是不同的。
Reduce函数的作用:对MongoDB的一次调用,对age对应的列表进行聚合运算。
map=function () { emit(this.age,{count:1}); }reduce= function (key,emits) { total=0; for(var i in emits) {total+=emits[i].count; }return {"age":key,count:total}; }
step2,执行MapReduce聚合运算
mr=db.runCommand( { "mapreduce":"foo", "map":map, "reduce":reduce, out:"Count Doc" })
step3,查看聚合运算的结果
db[mr.result].find()
示例3,研究reduce函数的特性
reduce函数具有累加的特性,通过多次调用,能够产生最终的累加值,例如,以下reduce函数对于任意一个特定的key,reduce都能计算key的数量
reduce= function (key,emits) { total=0; for(var i in emits) {total+=emits[i].count; }return {"key":key,count:total}; }
调用示例:传递的Key是相同的,都是“x”,每个emits都是一个数组,反复调用reduce函数,最终获得key的累加值。
r1=reduce("x",[{count:1},{count:2}]) r2=reduce("x",[{count:3},{count:5}]) r3=reduce("x",[r1,r2])
参考doc:
Aggregation
MongoDB 聚合操作相关推荐
- Scrapy框架之MongoDB聚合操作
目录 MongoDB聚合操作 聚合操作的基本语法 常用的聚合操作 管道命令之$group 按照某个字段进行分组 详解 计算集合中某个字段的平均值 常用表达式 管道命令之$match 示例 管道命令之$ ...
- mongoDB聚合操作_aggregate()归纳
mongoDB聚合操作 文章目录 1.准备一组数据 2.$group 分组管道 2.1 统计单组 2.2 统计多组 3.$match 过滤管道 拓展 统计数据个数 4.$project 映射管道 5. ...
- mongodb聚合操作之group
group介绍 mongodb中提供了一个很实用的聚合函数group,这个有点类似于mysql的group by 的功能,但又不是mysql中group by 的功能,它更多是用来做数据的聚合操作使用 ...
- Spring Boot 整合——MongoDB整合3(MongoDB聚合操作)
文章前面 关于版本 依赖 版本 springboot 2.0.8.RELEASE mongodb 4.0.14 本内容只是为了介绍mongodb最基础的使用以及配置,作为一个知名的数据库,其存在相当多 ...
- MongoDB——聚合操作详解
聚合(Aggregation)为集合文档数据提供各种处理数据方法,并返回计算结果. MongoDB提供了3种方式来执行聚合命令: 聚合管道方法 map-reduce方法 单一目标聚合方法 1. 聚合管 ...
- MongoDB——聚合操作
一.聚合操作 1.1.聚合操作的概述 聚合操作处理数据记录并返回计算结果(诸如统计平均值,求和等). 聚合操作组值来自多个文档,可以对分组数据执行各种操作以返回单个结果. 聚合操作包含三类:单一作用聚 ...
- MongoDB 聚合操作之$group使用
https://blog.csdn.net/qq_18948359/article/details/88777066
- MongoDB使用详解_4_常用聚合操作详解
1.测试数据 emp集合 db.emp.insert([{_id: 1,name: "tom",age: 13,likes: ["basketball", &q ...
- 04 MongoDB各种查询操作 以及聚合操作总结
基础篇(能解决工作中80%的问题): MongoDB的概述.应用场景.下载方式.连接方式和发展历史等 MongoDB数据类型.重要概念以及shell常用指令 MongoDB文档的各种增加.更新.删除操 ...
最新文章
- 利用JNI技术在Android中调用C++代码
- Linux系统的启动引导过程
- ETH:Windows搭建ETH(区块链技术)利用Web端和小程序端两种方式调用ETH上的SC智能合约
- centos 6.5 32bit qemu 环境搭建
- mysql下拉框记忆,Mysql alter语句记忆分析
- IBASE read hierarchy
- c++ 读文件_第十六节:读文件,文件的创建,写文件,文件的读写以及鼠标键盘事件和图形绘制...
- centos7中,mysql连接报错:1130 - Host ‘118.111.111.111’ is not allowed to connect to this MariaDB server...
- 史上最全android分辨率
- 聊天室自动滚动效果实现
- (已更新)【全开源商城小程序源码】ThinkPHP 5.1+带后台商城源码程序+带详细安装使用文档
- 数据分析 超市条码_超市小程序做得比较好的功能点是什么?
- 视频编码中CBR和VBR的区别,CRF和CQP的区别
- 吉时利DMM6500数字万用表,更高的生产测试量和更低的测试成本
- 瀚高数据库安全配置要求
- 破除谷歌浏览器Google Chrome启动时打开hao123,2345等流氓网页
- 软件测试简历投递小技巧,百分百避免已读不回,成功入职增加30%
- 解决Java运行时错误For input string:问题
- Java I/O流(File、字节流、字符流、过滤流、对象流)详解
- 安装EA后win10提示系统资源不足,无法完成请求服务的解决方法
热门文章
- linux命令大全 笔试,Linux基础及常用命令(笔试面试必备)
- mysql ibatis 分页_MyBatis怎样实现MySQL动态分页?
- Window_Open详解
- leetcode算法题--石子游戏
- div css导航栏设计,CSS+DIV设计实例:超酷的竖排导航栏
- 数据库PHP绘制柱形图,php使用Jpgraph绘制柱形图的方法
- 软件工程(2019)第三次个人作业
- mac、linux 查看端口占用程序
- 百分点宣布完成C轮融资2500万美元 将进一步开放云平台应用
- ABP理论学习之授权(Authorization)