zoukankan      html  css  js  c++  java
  • MongoDB MapReduce

    目前发现mapreduce的用法有两种:

    一:计数以及实现聚合函数统计数据

    二:对数据进行分组简化或者构造自己想要的格式

    三:根据条件进行数据筛选

    现在普遍的用法是第一种,对于第二种用法我们会分析一些格式怎么构造。

    效果图 

    mapreduce原理:

    参考资料:http://www.csdn.net/article/2013-01-07/2813477-confused-about-mapreduce(十张图带你了解mapreduce)

                       http://www.mongovue.com/2010/11/03/yet-another-mongodb-map-reduce-tutorial/   (原理)

                      http://www.infoq.com/cn/articles/implementing-aggregation-functions-in-mongodb(聚合函数)

     
    1. 1.db.runCommand(     
    2. 2.{     
    3. 3.    mapreduce : <collection>,     
    4. 4.    map : <mapfunction>,     
    5. 5.    reduce : <reducefunction>     
    6. 6.    [, query : <query filter object>]     
    7. 7.    [, sort : <sort the query.  useful   optimization>] for    
    8. 8.    [, limit : <number of objects to   from collection>] return    
    9. 9.    [, out : <output-collection name>]     
    10. 10.    [, keeptemp: < | >] true false    
    11. 11.    [, finalize : <finalizefunction>]     
    12. 12.    [, scope : <object where fields go into javascript global scope >]     
    13. 13.    [, verbose :  ] true    
    14. 14.});    


    参数说明:

        

    •  mapreduce: 要操作的目标集合。
    •  map: 映射函数 (生成键值对序列,作为 reduce 函数参数)。
    •  reduce: 统计函数。
    •  query: 目标记录过滤。
    •  sort: 目标记录排序。
    •  limit: 限制目标记录数量。
    •  out: 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
    •  keeptemp: 是否保留临时集合。
    •  finalize: 最终处理函数 (对 reduce 返回结果进行最终整理后存入结果集合)。
    •  scope: 向 map、reduce、finalize 导入外部变量。
    •  verbose: 显示详细的时间统计信息

     MapReduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。这样做的好处是可以在任务被分解后,可以通过大量机器进行并行计算,减少整个操作的时间。

    我们可以把mapreduce与sql对比理解:

    emit函数是非常重要的,他的作用是将一条数据放入数据分组集合,这个分组是以emit的第一个参数为key的。你可以这样理解,当你在所有需要计算的行执行完了map函数,你就得到了一组key-values对。基本key是emit中的key,values是每次emit函数的第二个参数组成的集合。

    现在我们的任务就是将这一个key-values变在key-value,也就是把这一个集合变成一个单一的值。这个操作就是Reduce。

    例如:我们有巴西和澳大利亚的信息,我们按照城市来分组就要用emit函数,用城市名当key

    这样我们就得到了一组key-values对,则第一次values中装的就是巴西组的信息,第二次values中装的是澳大利亚组的信息。values数组中则装的是每个城市的信息。具体有哪些信息取决于我们的emit函数的第二个参数发送了哪些信息。

    在reduced和finalize里我们就能进行相应的统计计算或者结构重构等操作。

    mapreduce例子

    首先我们插入数据

     
    1. db.c.insert({country:"中国" province:"广东省" city:"广州" temprature:"12°C" weather:"晴"});  
     
    1. db.c.insert({country:"中国" province:"广东省" city:"深圳" temprature:"15°C" weather:"晴"});  
     
    1. db.c.insert({country:"中国" province:"广东省" city:"珠海" temprature:"7°C" weather:"多云"});  
     
    1. db.c.insert({country:"中国" province:"贵州省" city:"贵阳" temprature:"4°C" weather:"晴"});  
     
    1. db.c.insert({country:"中国" province:"贵州省" city:"遵义" temprature:"2°C" weather:"晴"});  
     
    1. db.c.insert({country:"中国" province:"云南省" city:"昆明" temprature:"24°C" weather:"多云"});  
     
    1. db.c.insert({country:"中国" province:"云南省" city:"丽江" temprature:"22°C" weather:"晴"});  
     
    1. db.c.insert({country:"澳大利亚" province:"新南威尔士州" city:"悉尼" temprature:"15°C" weather:"晴"});  
     
    1. db.c.insert({country:"澳大利亚" province:"维多利亚州" city:"墨尔本" temprature:"20°C" weather:"晴"});  

    PS:由于MongoDB中没有提供给shell的“批量插入方法”,不过各个语言的驱动driver有提供批量的方法。或者对于相同的数据可以用for循环插入。在这里我直接用mongovue直接分9次插入。

    插入完成后一共有9条数据:

    用法一:

    1.计数的例子:

    一:求所有记录的总条数

    db.runCommand({ mapreduce: "c", 
     map : function Map() {
     
     
     emit(
      1,     // how to group
      {count: 1} // associated data point (document)
     ); 
     
     
    },
     reduce : function Reduce(key, values) {

          Total=0;


     values.forEach(function(val) {
       Total += val.count; 
     });

     return Total; 
     

     

    },
     finalize : function Finalize(key, reduced) {
     

     return reduced;
    },
     out : { inline : 1 }
     });

    结果:

    分析:map函数中emit函数分组,因为我们要算所有记录的总条数,所有这里就不分组了。key可以用任意的数值。这样就只有一个小组。在reduced里求总数。返回。 可以得到一共有9条记录

    二:求分组后小组的记录条数(这里我们用国家来分组)

    db.runCommand({ mapreduce: "c", 
     map : function Map() {
     
     
     emit(
      this.country,     // how to group
      {count: 1} // associated data point (document)
     ); 
     
     
    },
     reduce : function Reduce(key, values) {

          Total=0;


     values.forEach(function(val) {
       Total += val.count; 
     });

     return Total; 
     

     

    },
     finalize : function Finalize(key, reduced) {
     

     return reduced;
    },
     out : { inline : 1 }
     });

    结果:

    分析:map函数中emit函数分组,用country当key。有两个小组:中国和澳大利亚。在reduced里求每一个小组的总数。返回。 可以得到一共有中国有7条记录,澳大利亚有2条记录

    2.实现聚合函数:

    求平均:

    求总和:

    求最大:

    用法二:

    1.分组去除重复的运用(分组的key可自由拼接组合)

    如果我们要去除重复,只要找出它们的共同点,再用emit函数的分组功能来分组就能实现了。分组的key可以多个标签的值构造。

    例如计数例子中的数据,我们每个省只想要一条记录,其他的去掉:

    db.runCommand({ mapreduce: "c", 
     map : function Map() {
     
     
     emit(
      this.province,    

      {country:this.country,province:this.province,city:this.city,temprature:this.temprature,weather:this.weather} 

     ); 
     
     
    },
     reduce : function Reduce(key, values) {

         var reduced={country:{},province:{},city:{},temprature:{},weather:{}};
      
    reduced.country=values[0].country;
    reduced.province=values[0].province;
    reduced.city=values[0].city;
    reduced.temprature=values[0].temprature;
    reduced.weather=values[0].weather;

     return reduced; 
     

    },
     finalize : function Finalize(key, reduced) {
     

     return reduced;
    },
     out : { inline : 1 }
     });

    结果:

    分析:map函数中我们用省province来分组,一共有四个小组。则每一次values数组代表一个省的数据。values数组中对应则是每一个城市的数据。

    这里我们只取values[0]里的数据。

    2.分组后把同一组的数据放在同一条文档中(foreach--数组.push)

    如果我们经过省分组后,但是又想把每一个省其中的城市的数据都取出来,而不是只取一个城市。那可以用如下方法:

    db.runCommand({ mapreduce: "c", 
     map : function Map() {
     
     
     emit(
      this.province,     // how to group
      {country:this.country,province:this.province,city:this.city,temprature:this.temprature,weather:this.weather} // associated data point (document)
     ); 
     
     
    },
     reduce : function Reduce(key, values) {

         var reduced={country:{},province:{},city:[],temprature:[],weather:[]};
      
    values.forEach(function(val) {
     reduced.country=val.country;
     reduced.province=val.province;
     reduced.city.push(val.city);
     reduced.temprature.push(val.temprature);
      reduced.weather.push(val.weather);
     });

     return reduced;

    },
     finalize : function Finalize(key, reduced) {
     

     return reduced;
    },
     out : { inline : 1 }
     });

    结果:

    3.用提取的数据当key名

    从上面的例子我们可以知道文档的标签都是我们一开始就定义的,那能不能用从数据库中提取的数据来当标签呢?

    例子如下:

    db.runCommand({ mapreduce: "c", 
     map : function Map() {
     
     
     emit(
      this.province,     // how to group
      {country:this.country,province:this.province,city:this.city,temprature:this.temprature,weather:this.weather} // associated data point (document)
     ); 
     
     
    },
     reduce : function Reduce(key, values) {


     var reduced={};
     
     values.forEach(function(val) {

     reduced[val.country+val.province+val.city+"_temprature"]=(val.temprature);
     reduced[val.country+val.province+val.city+"_weather"]=(val.weather);
     
      });

     return reduced;
    },
     finalize : function Finalize(key, reduced) {
     

     return reduced;
    },
     out : { inline : 1 }
     });

    结果:

    4.嵌套文档的格式构造(构造数组中的数组)

    db.runCommand({ mapreduce: "c", 
     map : function Map() {
    emit(this.province, {country:this.country,province:this.province,city:this.city,temprature:this.temprature,weather:this.weather}
    )
    },
     reduce : function Reduce(key, values) {
      var result=[];
       
      

                
       
        for(c=0;c<values.length;c++)
     {
               result[c]={country:values[c].country,province:values[c].province,data:[]};
         
          for(i=0;i<values.length;i++)
       {
        result[c].data[i]={nature:[]};
       
        result[c].data[i].nature[0]={city:values[c].city};
        result[c].data[i].nature[1]={temprature:values[c].temprature};
        result[c].data[i].nature[2]={weather:values[c].weather};
       
       
       }
       
       
       
         }
       
       return result[0];
    },
     finalize : function Finalize(key, reduced) {
     /*  
     
     // Make final updates or calculations
     reduced.avgAge = reduced.age / reduced.count;
     
     */

     return reduced;
    },
     out : { inline : 1 }
     });

    结果:

     分析:从结果可以看出已经构造出了数组里的数组这种结构,关键的构造方法部分已经用蓝颜色标出。

    三:根据条件进行筛选

     
    1. db.runCommand({ mapreduce: "data",   
    2.  map : function Map() {  
    3.    
    4.   
    5.       
    6.     if(this.location=="青岛")  
    7.     {  
    8.       
    9.     emit(  
    10.         "result",                   // how to group  
    11.         this    // associated data point (document)  
    12.     );   
    13.     }  
    14.   
    15. },  
    16.  reduce : function Reduce(key, values) {  
    17.   
    18.   
    19.     var reduced = {city:[]}; // initialize a doc (same format as emitted value)  
    20.   
    21.     values.forEach(function(val) {  
    22.       
    23.         reduced.city.push(val);   
    24.     });  
    25.   
    26.     return reduced;   
    27.       
    28.       
    29.       
    30.   
    31. },  
    32.  finalize : function Finalize(key, reduced) {  
    33.     /*    
    34.       
    35.     // Make final updates or calculations  
    36.     reduced.avgAge = reduced.age / reduced.count;  
    37.       
    38.     */  
    39.   
    40.     return reduced;  
    41. },  
    42.  out : { inline : 1 }  
    43.  });  



    疑问:

    1.对一个集合做mapreduce时 能引用到另一个集合的数据么? 用DBf能不能做到?

    2.分组后还能更改_id么 拼接分组后 想改成标准_Id(可在emit时用new ObjectId()创建新_id,但不再具有分组功能)

    例如:

    用省份作为key分组后,_id就是显示的省份,能不能让它变成一个正常的id如下格式的id

    3.提取的数据能声明成为全局变量么? 即在map时只对第一条文档,提出数据,然后在第二条文档时能使用第一条的数据。类似于在scope里声明[now_date:new data();],now_date就能在全局使用,可以对map,reduce,finalize函数 以及对每一条文档处理时都能使用。但这个变量我需要从数据中提取。

    4.第一组文档要与第二组文档的数据进行比较,怎么实现(感觉mapreduce处理的流程是处理完一条文档后开始第二条文档)

    我们知道比如用国家分组。 reduce : function Reduce(key, values)中values数组中第一次只包含了中国的数据,values[0]里装的是中国的第一个城市数据,values[1]装的是中国的第二个城市数据,。。。处理完中国的数据后,才进行第二次reduce : function Reduce(key, values) values里包含了澳大利亚的数据,values[0]中装的是澳大利亚第一个城市的数据。。。 

  • 相关阅读:
    Ubuntu 使用OpenCV 利用Makefile运行
    YOLOV3——PyTorch训练TensorFlowLite部署模型转换
    PyTorch——学习笔记
    opencv调用摄像头分辨率问题
    Dlib与Opencv中图像格式转换(matrix-Mat)
    Windows下 VS2015 + Dlib + CUDA环境搭建
    OpenCV——银行卡识别
    QT入门
    LeetCode——最长连续回文串
    数值分析--第四章--特征值特征向量计算(乘幂法)
  • 原文地址:https://www.cnblogs.com/lyq4/p/3766463.html
Copyright © 2011-2022 走看看