zoukankan      html  css  js  c++  java
  • MongoDB Map Reduce

      Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。

    MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。

    以下是MapReduce的基本语法:

    db.runCommand(
      { mapreduce : <collection>,
        map : <mapfunction>,
        reduce : <reducefunction>,
        out : <see output options below>
        [, query : <query filter object>]
        [, sort : <sorts the input objects using this key. Useful for optimization, like sorting by the emit key for fewer reduces>]
        [, limit : <number of objects to return from collection, not supported with sharding>]
        [, keeptemp: <true|false>]
        [, finalize : <finalizefunction>]
        [, scope : <object where fields go into javascript global scope >]
        [, jsMode : true]
        [, verbose : true]
        }
      );
     

     

      使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value), 遍历 collection 中所有的记录, 将key 与 value 传递给 Reduce 函数进行处理。

    Map 函数必须调用 emit(key, value) 返回键值对。

    参数说明:

    • map :映射函数 (生成键值对序列,作为 reduce 函数参数)。
    • reduce 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。
    • out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
    • query 一个筛选条件,只有满足条件的文档才会调用map函数。(query。limit,sort可以随意组合)
    • sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
    • limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)
    • keeptemp:是否保留临时集合

    • finalize:最终处理函数(对reduce返回结果执行最终整理后存入结果集合,再进行一些数据“修剪”性质的处理)
    • scope:向map、reduce、finalize导入外部变量

    • verbose:显示详细的时间统计信息
    插入测试数据:

      for i in xrange(1000):
        rID=math.floor(random.random()*10);
        price = round(random.random()*10,2);
        if rID < 4:
          db.test.insert({"_id":i,"user":"Joe","product":rID,"price":price});
        elif rID>=4 and rID<7:
          db.test.insert({"_id":i,"user":"Josh","product":rID,"price":price});
        else:
          db.test.insert({"_id":i,"user":"Ken","product":rID,"price":price});

      结果数据为: 

      { "_id" : 0, "price" : 5.9, "product" : 9, "user" : "Ken" }
      { "_id" : 1, "price" : 7.59, "product" : 7, "user" : "Ken" }
      { "_id" : 2, "price" : 4.72, "product" : 0, "user" : "Joe" }
      { "_id" : 3, "price" : 1.35, "product" : 1, "user" : "Joe" }
      { "_id" : 4, "price" : 2.31, "product" : 0, "user" : "Joe" }
      { "_id" : 5, "price" : 5.29, "product" : 5, "user" : "Josh" }
      { "_id" : 6, "price" : 3.34, "product" : 1, "user" : "Joe" }
      { "_id" : 7, "price" : 7.2, "product" : 4, "user" : "Josh" }
      { "_id" : 8, "price" : 8.1, "product" : 6, "user" : "Josh" }
      { "_id" : 9, "price" : 2.57, "product" : 3, "user" : "Joe" }
      { "_id" : 10, "price" : 0.54, "product" : 2, "user" : "Joe" }
      { "_id" : 11, "price" : 0.66, "product" : 1, "user" : "Joe" }
      { "_id" : 12, "price" : 5.51, "product" : 1, "user" : "Joe" }
      { "_id" : 13, "price" : 3.74, "product" : 6, "user" : "Josh" }
      { "_id" : 14, "price" : 4.82, "product" : 0, "user" : "Joe" }
      { "_id" : 15, "price" : 9.79, "product" : 3, "user" : "Joe" }
      { "_id" : 16, "price" : 9.6, "product" : 5, "user" : "Josh" }
      { "_id" : 17, "price" : 4.06, "product" : 7, "user" : "Ken" }
      { "_id" : 18, "price" : 1.37, "product" : 5, "user" : "Josh" }
      { "_id" : 19, "price" : 6.77, "product" : 9, "user" : "Ken" }

    测试1、每个用户各购买了多少个产品?
    用SQL语句实现为:select user,count(product) from test group by user

    //MapReduce实现

      map=function (){
        emit(this.user,{count:1})
      }

      reduce = function (key, values){
        var total = 0;
        for (var i = 0; i < values.length; i++)
        {
          total += values[i].count;
        }
        return {count:total};
        }

      result = db.test.mapReduce(map,reduce,{out: 're'})
    执行结果:
      

      

      结果表明,共有1000个符合查询条件的文档, 在map函数中生成了1000个键值对文档,最后使用reduce函数将相同的键值分为两组。

      具体参数说明:

    • result:储存结果的collection的名字,这是个临时集合,MapReduce的连接关闭后自动就被删除了。
    • timeMillis:执行花费的时间,毫秒为单位
    • input:满足条件被发送到map函数的文档个数
    • emit:在map函数中emit被调用的次数,也就是所有集合中的数据总量
    • ouput:结果集合中的文档个数(count对调试非常有帮助)
    • ok:是否成功,成功为1
    • err:如果失败,这里可以有失败原因,不过从经验上来看,原因比较模糊,作用不大

      使用 find 操作符来查看 mapReduce 的查询结果:

      查询结果:
      

      

      2、每个用户不同的产品购买了多少个?(复合Key做re)

      SQL实现:select user,product,count(*) from test group by user,product

      MapReduce 实现:

      

      map = function (){
        emit({user:this.user,product:this.product},{count:1})
      }

      reduce = function (key, values){
        var total = 0;
        for (var i = 0; i < values.length; i++) 
        {
          total += values[i].count;
        }
        return {count:total};
        }

      执行:result = db.test.mapReduce(map,reduce,{out: 're2'})

           

      查询结果re2:

      

     3. 每个用户购买的产品数量,总金额是多少?(复合Reduce结果处理)

      SQL实现为:select user,count(product),sum(price) from test group by user

      MapReduce实现:

      

      map=function (){
        emit(this.user,{amount:this.price,count:1})
      }

      

      reduce = function (key, values){
         var res={amount:0,count:0};
         for (var i = 0; i < values.length; i++)
         {
          res.count += values[i].count;
          res.amount += values[i].amount;
         }
         res.count = Math.round(res.count,2);
         res.amount = Math.round(res.amount,2);
         return res;
      }

      执行:db.test.mapReduce(map,reduce,{out:"re3"})

      

      查询re3:

      

    4、在3中返回的amount的float精度需要改成两位小数,还需要得到商品的平均价格。(使用Finalize处理reduce结果集)

      SQL实现:select user,count(sku),sum(price),round(sum(price)/count(sku),2) as avgPrice from test group by user

      MapReduce实现:

      执行:db.test.mapReduce(map,reduce,{out:'re4'})

      

      查询结果 re4:

          

      map=function (){
        emit(this.user,{amount:this.price,count:1,avgPrice:0})
      }

      reduce = function (key, values){
         var res={amount:0,count:0};
         for (var i = 0; i < values.length; i++)
         {
          res.count += values[i].count;
          res.amount += values[i].amount;
         }
         return res;
      }


      finalizeFun = function (key,reduceResult){
        reduceResult.amount=(reduceResult.amount).toFixed(2);
        reduceResult.avgPrice=(reduceResult.amount/reduceResult.count).toFixed(2);
        return reduceResult;
      }

     执行: db.test.mapReduce(map,reduce,{out:"mr4",finalize:finalizeFun})

          

      查询结果:

      

        5. 统计单价大于6的SKU,每个用户的购买数量.(筛选数据子集做MR)

      SQL实现:select user,count(product) from test where price >6 group by user

      MapReduce实现:

     map=function (){
        emit(this.user,{count:1}) 
      }

      reduce = function (key, values){
        var total = 0;
        for (var i = 0; i < values.length; i++) 
        {
          total += values[i].count;
        }
        return {count:total};
        }

      db.test.mapReduce(map,reduce,{query:{price:{"$gt":6}},out:"re5"})

      

      查询结果:

          

  • 相关阅读:
    sqlite数据库的基本操作
    java-正则表达式判断移动联通电信手机号
    基金新手常识
    Android 心跳包心跳连接 如何实现android和服务器长连接呢?推送消息的原理
    java 设计模式之模板方法
    Android 自定义view实现水波纹效果
    Android中pendingIntent的深入理解
    CentOS 7.X下 -- 配置nginx正向代理支持https
    自动化运维工具saltstack04 -- 之jinja模板
    自动化运维工具saltstack03 -- 之SaltStack的数据系统
  • 原文地址:https://www.cnblogs.com/shaosks/p/5684906.html
Copyright © 2011-2022 走看看