MongoDB MapReduce 使用(二)

一  MapReduce 在分片集合上的使用

      Map-reduce可以在分片集合上使用,而且可以将分片集合作为输入和输出。
      当一个分片集合作为其输入的时候,mongos进程会自动的向每个分片来分发map和reduce 作业,然后mongos进程会等待所有分片完成自己的作业。
      如果map reduce 的out字段上有sharded值的话,mongodb会将结果集合进行分片,并选择_id作为其片键。

二  编写MapReduce程序
    
       所有的map-reduce函数都是用JavaScript书写,然后在mongod实例进程上运行。
       Map-Reduce是进行大容量数据处理的范式,对于Map-Reduce操作,MongoDB提供了MapReduce的数据库命令。 在进行map-reduce操作的时候,MongoDB会将满足查询条件的文档进行map所定义的操作,map函数会产生( emit)键值型的数据。
      如果某个键所对应的值有多个的话,会进行reduce的操作,最后将结果保存到一个集合中。通过定义一个finalize函数可以对reduce的结果做进一步的处理,比如:进行投影或者规范化输出、进一步的计算等。

看下面例子:
   1. 首先在orders集合中插入5条待检索数据
   >db.order.insert({ "cust_id" : "1", "ord_date" : new Date("2013-11-13T16:00:00Z"), "status" : "A", "price" : 25, "items" : [ { "sku" : "mmm", "qty" : 5, "price" : 2.5 }, { "sku" : "nnn", "qty" : 5, "price" : 2.5 } ] })

>db.order.find()
{ "_id" : ObjectId("528312e716b20807b2152db5"), "cust_id" : "1", "ord_date" : ISODate("2013-11-13T16:00:00Z"), "status" : "A", "price" : 25, "items" : [      {      "sku" : "mmm",      "qty" : 5,      "price" : 2.5 },      {      "sku" : "nnn",      "qty" : 5,      "price" : 2.5 } ] }
{ "_id" : ObjectId("528312f716b20807b2152db6"), "cust_id" : "2", "ord_date" : ISODate("2013-11-13T16:00:00Z"), "status" : "A", "price" : 25, "items" : [      {      "sku" : "mmm",      "qty" : 5,      "price" : 2.5 },      {      "sku" : "nnn",      "qty" : 5,      "price" : 2.5 } ] }
{ "_id" : ObjectId("5283130816b20807b2152db7"), "cust_id" : "3", "ord_date" : ISODate("2013-11-13T16:00:00Z"), "status" : "A", "price" : 25, "items" : [      {      "sku" : "mmm",      "qty" : 5,      "price" : 2.5 },      {      "sku" : "nnn",      "qty" : 5,      "price" : 2.5 } ] }
{ "_id" : ObjectId("5283132c16b20807b2152db8"), "cust_id" : "3", "ord_date" : ISODate("2013-11-13T16:00:00Z"), "status" : "A", "price" : 30, "items" : [      {      "sku" : "mmm",      "qty" : 6,      "price" : 2.5 },      {      "sku" : "nnn",      "qty" : 6,      "price" : 2.5 } ] }
{ "_id" : ObjectId("5283134d16b20807b2152db9"), "cust_id" : "2", "ord_date" : ISODate("2013-11-13T16:00:00Z"), "status" : "A", "price" : 20, "items" : [      {      "sku" : "mmm",      "qty" : 4,      "price" : 2.5 },      {      "sku" : "nnn",      "qty" : 4,      "price" : 2.5 } ] }


  2.统计每个顾客消费总额
  a.编写map函数
 > var mapFunc = function() {
... emit(this.cust_id,this.price);
... };
  b.编写reduce函数
> var reduceFunc = function(key,values) {
... return Array.sum(values);
... };
c.执行mapreduce
> db.runCommand({mapreduce:'order',map:mapFunc,reduce:reduceFunc,out:{replace:'map_reduce_result'}})
{
     "result" : "map_reduce_result",
     "timeMillis" : 35,
     "counts" : {
          "input" : 5,
          "emit" : 5,
          "reduce" : 2,
          "output" : 3
     },
     "ok" : 1
}
也可以这样
> db.order.mapReduce(mapFunc,reduceFunc,{out:{replace:'map_result_result'}})
{
     "result" : "map_result_result",
     "timeMillis" : 19,
     "counts" : {
          "input" : 5,
          "emit" : 5,
          "reduce" : 2,
          "output" : 3
     },
     "ok" : 1,
}

 注意:db.XXXmapReduce是对数据库命令db.runCommand的一个包装,前者实际上是调用后者来实现的

d.查看结果
> show collections
map_reduce_result
order
system.indexes
> db.map_reduce_result.find()
{ "_id" : "1", "value" : 25 }
{ "_id" : "2", "value" : 45 }
{ "_id" : "3", "value" : 55 }


3.计算每种商品的购买总数和平均每次购买数量

a.编写map函数
> var mapFunc = 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);
...   }};
b.编写reduce函数
> var reduceFunc = function(key,values) {
...   
...    reduceVal = {count:0,qty:0};
...
...    for(var idx=0;idx<values.length;idx++) {
...    reduceVal.count += values[idx].count;
...     reduceVal.qty  += values[idx].qty;
...    }
...    return reduceVal;
...  };
c.编写finalize函数
> var finalizeFunc = function(key,reduceVal) {
…      reduce Val.avg = reduceVal.qty/reduceVal.count;
...      return reduceVal;
...   }
d.执行mapreduce函数
db.order.mapReduce(mapFunc,reduceFunc,
  {
     out:  {merge:"map_reduce_result_2"},
     query:{ ord_date:{$gt:new Date('01/01/2000')}},
     finalize:finalizeFunc
  }
)
{
     "result" : "map_reduce_result_2",
     "timeMillis" : 20,
     "counts" : {
          "input" : 5,
          "emit" : 10,
          "reduce" : 2,
          "output" : 2
     },
     "ok" : 1,
}
分析:
      从执行结果可以发现,有5个文档执行MapReduce的操作,Map操作发生了10次(每个文档产生了两次,印证了上一节所说的一个文档可以进行一次或多次Map操作),Reduce操作发生了2次,最终输出了2个文档

同样也可以使用runCommand来执行MapReduce操作:
db.runCommand({mapreduce:'order’,
                                        map:mapFunc,
                                    reduce:reduceFunc,
                                          out:{replace:'map_reduce_result_2'},
                                      query:{ord_date:{$gt:new Date('01/01/2000')}},
                                    finalize:finalizeFunc})

d.查看结果
> db.map_reduce_result_2.find()
{ "_id" : "mmm", "value" : { "count" : 5, "qty" : 25, "avg" : 5 } }
{ "_id" : "nnn", "value" : { "count" : 5, "qty" : 25, "avg" : 5 } }


注意:
        1.reduce函数返回对象的键值结构必须要和map函数传出对象值的键值类型相同, 这样才能保证多次调用Reduce 函数成为可能。否则,会出现奇怪的很难调试的错误。 例如在第二个例子中reduce返回的对象的类型为 “ {count:0,qty:0}” ,它和map函数的传出对象的值类型是 “var value =  { count:1, qty:this.items[idx].qty }”类型是一致的。
        
         2.使用finalize函数可以进一步对Value的值进行操作, 例如可以这样修改上面例子中的finalizeFunc:
      > var finalizeFunc = function(key,reduceVal) {
…      reduceVal.avg = reduceVal.qty/reduceVal.count;
…      reduceVal.tip   = “ MapReduce”;         
...      return reduceVal;
…   }
 此时的结果显示为:
> db.map_reduce_result_2.find()
{ "_id" : "mmm", "value" : { "count" : 5, "qty" : 25, "avg" : 5,”tip”: ”MapReduce" } }
{ "_id" : "nnn", "value" : { "count" : 5, "qty" : 25, "avg" : 5 ,”tip”: "MapReduce"} }
       
      3.进行MapReduce时可以使用qurey对文档进行过滤,只对过滤后的文档进行MapReduce操作,提高MapReduce 执行效率。

       4.如果map后的文档中的键只有一个值的时候,不会进行reduce 操作。

       5.Reduce 返回的值不能是一个数组,通常是一个对象或者一个数值。
     

三 总结

      只要理解MapReduce原理,了解MapReduce聚合过程,那么编写MapReduce函数也就没那么困难了。
     和聚管道相比,使用map-reduce提供了很大的灵活性,我们可以编写自己的逻辑map和reduce函数,实现特定的功能。但是一般来说,写map-reduce操作 比聚合管道要复杂,而且效率也没有聚合管道高。
    
    下面一节会详细介绍聚合管道的使用(Aggregation Pipeline )。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值