MongoDB中使用MapReduce来进行聚合操作
来源:互联网 发布:2015java面试宝典下载 编辑:程序博客网 时间:2024/05/16 01:48
在mongoDB的MapReduce操作中,map函数产生一些列中间数据,这些中间数据是key/value的集合。reduce函数收集具有相同中间key值的value值,合并这些value值,形成一个较小的value值的集合。
一个MongDB的MapReduce执行的过程如下所示。
在这个MapReduce操作中,首先通过query筛选出了一部分的数据,然后对着一部分的数据进行map操作,输出了一些列中间值,这些中间值的key是cust_id,value是amount。最后对中间数据做reduce操作,产生了最终的结果。最终的结果是统计出了每一个cust_id对应的value值的总和。
在mongoDB中MapReduce有两种写法,一种是使用db.runCommand来执行MapReduce,一种是db.集合名.mapReduce()。两种方法效果一样。我们从第一种说起。
使用runCommand方式来执行一个MapReduce函数通常的模板如下所示:
db.runCommand( { mapReduce: <collection>, map: <function>, reduce: <function>, finalize: <function>, out: <output>, query: <document>, sort: <document>, limit: <number>, scope: <document>, jsMode: <boolean>, verbose: <boolean>, bypassDocumentValidation: <boolean>, } )
对上面的每一个选项做一个说明:
map Function
map函数将输入数据映射为一系列的key-value键。
map函数通常的样子就像下面这样:
function() { ...//一些列处理 emit(key, value);}
以下是map函数中的一些使用注意事项:
在map函数中,如果要引用当前文档自身,可以使用this。
在任何情况下map函数不应该访问数据库。
map函数不应该对其他函数有副作用。
在一个map函数中可以任意多次调用emit函数来输出具有key/value形式的中间数据。
现在我们有一个集合goods。数据如下:
{ "_id" : ObjectId("5a02451e8f215943b6d22a78"), "gnum" : "001", "shelfnum" : "e1", "price" : "10" }{ "_id" : ObjectId("5a02451e8f215943b6d22a79"), "gnum" : "002", "shelfnum" : "e2", "price" : "20.3"}{ "_id" : ObjectId("5a0245538f215943b6d22a7a"), "gnum" : "003", "shelfnum" : "e1", "price" : "7.5" }{ "_id" : ObjectId("5a02456d8f215943b6d22a7b"), "gnum" : "004", "shelfnum" : "e2", "price" : "8.8" }{ "_id" : ObjectId("5a02457f8f215943b6d22a7c"), "gnum" : "005", "shelfnum" : "e3", "price" : "6.6" }
有如下map函数:
var map=function() { emit(this.shelfnum, this.price);}
emit函数将文档中的shelfnum作为key,gnum作为value生成了一些列键值对。经过map函数,产生了下面的中间数据:
{"e1":["10","7.5"]}{"e2":["20.3","8.8"]}{"e3":"6.6"}
reduce Fuction
reduce函数大概看起来就想下面的样子:
function(key, values) { ...//一系列处理过程 return result;}
reduce函数的注意点:
reduce函数不应该连接数据库。
reduce函数不应该影响外部系统的运行。
如果一个key只有一个value,那么MongoDB不回调用reduce函数。
对于同一个key,reduce函数可能会执行多次。
继续上面的例子
var reduce=function(key,values){ totalsum=0; for(i=0;i<values.length;i++){ totalsum+=parseFloat(values[i]); } return totalsum;}
此时当执行MapReduce命令时,就会求出来每一个shelfnum对应商品价格的总和。
db.runCommand({ mapReduce:"goods",//指定对哪个集合做操作 map:map, //指定map函数 reduce:reduce, //指定reduce函数 out:"goodsResult" //指定输出结果到goodResult集合中 })
返回结果如下:
{ "result" : "goodsResult", "timeMillis" : 280, "counts" : { "input" : 5, "emit" : 5, "reduce" : 2, "output" : 3 }, "ok" : 1}
然后查询goodsResult集合就可以拿到我们计算出来的结果了。
db.goodsResult.find(){ "_id" : "e1", "value" : 17.5 }{ "_id" : "e2", "value" : 29.1 }{ "_id" : "e3", "value" : "6.6" }
在MongoDB中, db.collection.mapReduce() 是db.runCommand的一个替代方案,可以直接对某个结合执行MapReduce操作。
orders集合中有如下结构的数据:
{ _id: ObjectId("50a8240b927d5d8b5891743c"), cust_id: "abc123", ord_date: new Date("Oct 04, 2012"), status: 'A', price: 25, items: [ { sku: "mmm", qty: 5, price: 2.5 }, { sku: "nnn", qty: 5, price: 2.5 } ]}
我们要计算每一个顾客(cust_id)总的消费金额(price)。
定义map函数:
var mapFunction1 = function() { emit(this.cust_id, this.price); };
定义reduce函数:
var reduceFunction1 = function(keyCustId, valuesPrices) { return Array.sum(valuesPrices); };
执行MapReduce操作:
db.orders.mapReduce( mapFunction1, reduceFunction1, { out: "map_reduce_example" } )
这样就计算出了每一个顾客总的消费金额。
另外一个示例。
var mapFunction2 = function() { for (var idx = 0; idx < this.items.length; idx++) { var key = this.items[idx].sku; var value = { count: 1, qty: this.items[idx].qty }; emit(key, value); } };
var reduceFunction2 = function(keySKU, countObjVals) { reducedVal = { count: 0, qty: 0 }; for (var idx = 0; idx < countObjVals.length; idx++) { reducedVal.count += countObjVals[idx].count; reducedVal.qty += countObjVals[idx].qty; } return reducedVal; };
var finalizeFunction2 = function (key, reducedVal) { reducedVal.avg = reducedVal.qty/reducedVal.count; return reducedVal; };
db.orders.mapReduce( mapFunction2, reduceFunction2, { out: { merge: "map_reduce_example" }, query: { ord_date: { $gt: new Date('01/01/2012') } }, finalize: finalizeFunction2 } )
上面这个例子中,map函数产生了类似{"mmm":[{count:1,qty: 5},...]}
这样的中间数据。
reduce函数将每一个键值对中的键做了操作,计算出了count的总数和qty的总数。
finalize函数用reduce函数计算出的qty的总数除以count的总数,这样就计算出了每一个items的平均qty。当然这一步也可以直接在reduce函数里面做,省略finalize函数。
关于out的一些讨论
我们经常会遇到这样的问题,对一个集合做了MapReduce运算之后,这个集合进行了更新,此时我只想对新增的数据做MapReduce操作,而不想对整个集合再做一次MapReduce操作。并且操作后的结果需要加入到原来的集合中。这个时候我们可以通过query语句来过滤掉已经做过运算的数据,比如最常用的通过时间来过滤。在输出数据到集合时,通过out来指定是覆盖原来的集合还是将新结果合并到原来的结果。
我们上面的写法out: <collectionName>
是直接生成一个新的集合,并且将结果输出到这个集合中。
完整的out看起来像下面这样:
out: { <action>: <collectionName> [, db: <dbName>] [, sharded: <boolean> ] [, nonAtomic: <boolean> ] }
其中action可以指定一下几个值:
• replace 替换原来的集合。 • merge 跟已经存在的集合进行合并,如果新旧集合有相同的key,用新的数据覆盖旧的数据。 • reduce 跟已经存在的集合进行合并,如果新旧集合有相同的key,对新旧文档执行reduce操作产生新的结果。
db:可选,数据库的名称。默认和输入集合使用同一个数据库。
sharded:boolean。可选。数据库有分片时使用。
nonAtomic:boolean。可选。只有在merge或者reduce时有效。默认是false。map-reduce操作的后续步骤会对数据库加锁。其他客户端无法访问到out集合中的数据。如果指定为true,在map-reduce操作的后续过程中,其他客户端就可以访问到out集合中的数据。
- MongoDB中使用MapReduce来进行聚合操作
- 使用mongoDB pipeline进行聚合操作
- php7中使用mongoDB的聚合操作对数据进行分组求和统计操作
- Mongodb中数据聚合之MapReduce
- 使用MongoVUE对MongoDB 进行MapReduce操作步骤
- mongodb中使用mapreduce
- mongodb mapreduce 聚合查询
- MongoDB:MapReduce聚合
- 在MongoDB中实现聚合函数(mapreduce)
- 学习MongoDB--(6-2):聚合(MapReduce使用)
- 学习MongoDB--(6-2):聚合(MapReduce使用)
- MongoDB中聚合(aggregate)的使用
- Hibernate使用Projections进行聚合操作
- Hibernate使用Projections进行聚合操作
- 7.4.2 使用聚合操作进行计算
- Hibernate使用Projections进行聚合操作
- 7.4.2 使用聚合操作进行计算
- MongoDB中的聚合操作
- 单引号和双引号
- 类和对象
- 习题6.2(1)
- 第十周项目3中序线索化二叉树的算法验证
- zookeeper与dubbo-admin配置説明
- MongoDB中使用MapReduce来进行聚合操作
- 10月2日--11月12日(1小时,剩2887小时)
- [datapump] ORA-39034: Table TABLE_DATA:<table Name> does not exist.
- gdbus demo程序简介
- SDL 实现马赛克
- 有序链表的插入
- python -零基础
- 11.7-
- Communications Among Activities and Fragments