MongoDB:19-MongoDB-Map Reduce

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: MongoDB:19-MongoDB-Map Reduce


  1. Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE
  2. MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。


MapReduce 命令


  • 以下是MapReduce的基本语法:


  1. >db.collection.mapReduce(
  2.   function() {emit(key,value);},  //map 函数
  3.   function(key,values) {return reduceFunction},   //reduce 函数
  4.   {
  5.      out: collection,
  6.      query: document,
  7.      sort: document,
  8.      limit: number
  9.   }
  10. )




  1. db.collection.mapReduce(
  2.                         <map>,
  3.                         <reduce>,
  4.                         {
  5.                           out: <collection>,
  6.                           query: <document>,
  7.                           sort: <document>,
  8.                           limit: <number>,
  9.                           finalize: <function>,
  10.                           scope: <document>,
  11.                           jsMode: <boolean>,
  12.                           verbose: <boolean>,
  13.                           bypassDocumentValidation: <boolean>
  14.                         }
  15.                       )




  1. MapReduce要实现两个函数:Map和Reduce。
  2. Map函数调用emit(key,value)遍历一个或多个集合中所有的记录,进行分组(group by),
  3. 然后将key与value传给Reduce函数进行处理,输出结果。
  4.  
  5. 1)MapReduce使用自定义JavaScript函数执行map和reduce操作,所以是基于js引擎,单线程执行,效率不高,
  6.     比Aggregation复杂,适合用做后台统计等。
  7. 2)MapReduce支持分片操作,可以进行拆分,分发到不同的机器上执行(多服务器并行做数据集合处理),
  8.     然后再将不同的机器处理的结果汇集起 来,输出结果,。
  9. 3)MapReduce能执行单一聚合的所有操作count、distinct、group,
  10.     但group 在当数据量非常大的时候,处理能力就不太好,先筛选再分组,不支持 分片,对数据量有所限制,效率不高。


参数说明:


  1. 参数说明:
  2.    map:是JavaScript 函数,负责将每一个输入文档转换为零或多个文档,通过key进行分组,生成键值对序列,作为 reduce 函数参数。
  3.    reduce:是JavaScript 函数,对map操作的输出做合并的化简的操作(将key-values变成key-value,也就是把values数组变成一个单一的值value)。
  4.    out:reduce执行完,存放的集合,如果不指定集合,则使用默认的临时集合,在MapReduce的连接关闭后自动就被删除了。
  • out:{<action>:<collectionName>
  • [, db:<dbName>]
  • [, sharded:<boolean>]
  • [, nonAtomic:<boolean>]}
  1.    query:过滤的条件,对符合条件的文档执行map函数。(query。limit,sort可以随意组合)。
  2.    sort :对文档进行排序,sort和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制。
  3.    limit :发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)。
  4.  finalize:可以对reduce输出结果再一次修改,跟group的finalize一样,不过MapReduce没有group的4MB文档的输出限制。
  5.  scope:向map、reduce、finalize导入外部变量。
  6.  verbose:是否包括结果信息中的时间信息,默认为fasle。
  7.     "timing" : {
  8.                "mapTime" : 0,
  9.                "emitLoop" : 2,
  10.                "reduceTime" : 0,
  11.                "mode" : "mixed",
  12.                "total" : 0
  13.        }


以下实例在集合 orders 中查找 status:"A" 的数据,并根据 cust_id 来分组,并计算 amount 的总和。


图片.png





使用 MapReduce



  • 考虑以下文档结构存储用户的文章,文档存储了用户的 user_name 和文章的 status 字段:
  1. db.posts.insert([
  2. {
  3.   "post_text": "Spring",
  4.   "user_name": "fly",
  5.   "status":"active"
  6. },
  7. {
  8.   "post_text": "Struts2",
  9.   "user_name": "fly",
  10.   "status":"active"
  11. },
  12. {
  13.   "post_text": "Hibernate",
  14.   "user_name": "fly",
  15.   "status":"disabled"
  16. },
  17. {
  18.   "post_text": "Mybatis",
  19.   "user_name": "fly",
  20.   "status":"active"
  21. },
  22. {
  23.   "post_text": "Redis",
  24.   "user_name": "fly",
  25.   "status":"disabled"
  26. },
  27. {
  28.   "post_text": "Restful",
  29.   "user_name": "cc",
  30.   "status":"active"
  31. },
  32. {
  33.   "post_text": "Dubbo",
  34.   "user_name": "cc",
  35.   "status":"disabled"
  36. },
  37. {
  38.   "post_text": "SpringCloud",
  39.   "user_name": "cc",
  40.   "status":"active"
  41. },
  42. {
  43.   "post_text": "SpringBoot",
  44.   "user_name": "cc",
  45.   "status":"disabled"
  46. },
  47. {
  48.   "post_text": "MongoDB",
  49.   "user_name": "cc",
  50.   "status":"active"
  51. },
  52. {
  53.   "post_text": "MySQL",
  54.   "user_name": "cc",
  55.   "status":"active"
  56. }
  57. ])



  • 现在,我们将在 posts 集合中使用 mapReduce 函数来选取已发布的文章(status:"active"),并通过user_name分组,计算每个用户的文章数:



  1. >db.posts.mapReduce(
  2.   function() { emit(this.user_name,1); },
  3.   function(key, values) {return Array.sum(values)},
  4.      {  
  5.         query:{status:"active"},  
  6.         out:"post_total"
  7.      }
  8. )



  • 以上 mapReduce 输出结果为:



  1. /* 1 */
  2. {
  3.    "result" : "post_total",
  4. "timeMillis" : 409.0,
  5. "counts" : {
  6. "input" : 7,
  7. "emit" : 7,
  8. "reduce" : 2,
  9. "output" : 2
  10. },
  11.    "ok" : 1.0,
  12.    "_o" : {
  13.        "result" : "post_total",
  14.        "timeMillis" : 409,
  15.        "counts" : {
  16.            "input" : 7,
  17.            "emit" : 7,
  18.            "reduce" : 2,
  19.            "output" : 2
  20.        },
  21.        "ok" : 1.0
  22.    },
  23.    "_keys" : [
  24.        "result",
  25.        "timeMillis",
  26.        "counts",
  27.        "ok"
  28.    ],
  29.    "_db" : {
  30.        "_mongo" : {
  31.            "slaveOk" : true,
  32.            "host" : "localhost:27017",
  33.            "defaultDB" : "mongotest",
  34.            "authStatus" : {
  35.                "authRequired" : true,
  36.                "isMaster" : true,
  37.                "replSetGetStatus" : true
  38.            },
  39.            "_readMode" : "commands",
  40.            "_writeMode" : "commands"
  41.        },
  42.        "_name" : "mongotest"
  43.    },
  44.    "_coll" : {
  45.        "_mongo" : {
  46.            "slaveOk" : true,
  47.            "host" : "localhost:27017",
  48.            "defaultDB" : "mongotest",
  49.            "authStatus" : {
  50.                "authRequired" : true,
  51.                "isMaster" : true,
  52.                "replSetGetStatus" : true
  53.            },
  54.            "_readMode" : "commands",
  55.            "_writeMode" : "commands"
  56.        },
  57.        "_db" : {
  58.            "_mongo" : {
  59.                "slaveOk" : true,
  60.                "host" : "localhost:27017",
  61.                "defaultDB" : "mongotest",
  62.                "authStatus" : {
  63.                    "authRequired" : true,
  64.                    "isMaster" : true,
  65.                    "replSetGetStatus" : true
  66.                },
  67.                "_readMode" : "commands",
  68.                "_writeMode" : "commands"
  69.            },
  70.            "_name" : "mongotest"
  71.        },
  72.        "_shortName" : "post_total",
  73.        "_fullName" : "mongotest.post_total"
  74.    }
  75. }



  • 结果表明,共有 7个符合查询条件(status:"active")的文档, 在map函数中生成了 7个键值对文档,最后使用reduce函数将相同的键值分为 2 组。
  • 具体参数说明:



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


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


  1. >db.posts.mapReduce(
  2.   function() { emit(this.user_name,1); },
  3.   function(key, values) {return Array.sum(values)},
  4.      {  
  5.         query:{status:"active"},  
  6.         out:"post_total"
  7.      }
  8. ).find()


  • 以上查询显示如下结果,两个用户 tom 和 mark 有两个发布的文章:
  1. /* 1 */
  2. {
  3.    "_id" : "cc",
  4.    "value" : 4.0
  5. }
  6. /* 2 */
  7. {
  8.    "_id" : "fly",
  9.    "value" : 3.0
  10. }


  • 用类似的方式,MapReduce可以被用来构建大型复杂的聚合查询。
  • Map函数和Reduce函数可以使用 JavaScript 来实现,使得MapReduce的使用非常灵活和强大。


附录:参数详解

  • map函数


  1. 【map函数】
  2. map是JavaScript 函数,负责将每一个输入文档转换为零或多个文档,通过key进行分组,生成键值对序列,作为 reduce 函数参数。
  1. function() {
  2.       emit(key, value);
  3.  }

  1. key对文档进行分组,value是要统计的数据,value可以是JSON对象(emit只能容纳MongoDB的最大BSON文件大小的一半)。
  2. 我们对订单的详细统计每个产品类型卖出了多少个。


  • 订单数据    


  1. db.item.insert( [  
  2.  {  
  3.   "quantity" : 2,  
  4.   "price" : 5.0,  
  5.   "pnumber" : "p003"  
  6.  },{  
  7.   "quantity" : 2,  
  8.   "price" : 8.0,  
  9.   "pnumber" : "p002"  
  10.  },{  
  11.   "quantity" : 1,  
  12.   "price" : 4.0,  
  13.   "pnumber" : "p002"  
  14.  },{  
  15.   "quantity" : 2,  
  16.   "price" : 4.0,  
  17.   "pnumber" : "p001"  
  18.  },{  
  19.   "quantity" : 4,  
  20.   "price" : 10.0,  
  21.   "pnumber" : "p003"  
  22.  },{  
  23.   "quantity" : 10,  
  24.   "price" : 20.0,  
  25.   "pnumber" : "p001"  
  26.  },{  
  27.   "quantity" : 10,  
  28.   "price" : 20.0,  
  29.   "pnumber" : "p003"  
  30.  },{  
  31.   "quantity" : 5,  
  32.   "price" : 10.0,  
  33.   "pnumber" : "p002"  
  34.  }  
  35. ])  
  36. 我们先通过 pnumber进行分组,然后在对 quantity相加
  37. 相当于select pnumber,sum(quantity) from item   group by pnumber
  38. varmap= function() { emit(this.pnumber,this.quantity)}  
  39. var reduce=function(key,values){return {'pumber':key,'quantity':Array.sum(values)}}  
  40. db.item.mapReduce( map,  
  41.                   reduce,  
  42.                    { out: "map_reduce_data" }    
  43.                   )  
  44. db.map_reduce_data.find()  


  • 输出结果


  1. /* 1 */
  2. {
  3.    "_id" : "p001",
  4.    "value" : {
  5.        "pumber" : "p001",
  6.        "quantity" : 12.0
  7.    }
  8. }
  9. /* 2 */
  10. {
  11.    "_id" : "p002",
  12.    "value" : {
  13.        "pumber" : "p002",
  14.        "quantity" : 8.0
  15.    }
  16. }
  17. /* 3 */
  18. {
  19.    "_id" : "p003",
  20.    "value" : {
  21.        "pumber" : "p003",
  22.        "quantity" : 16.0
  23.    }
  24. }


  • query过滤的条件


  1. 对符合条件的文档将会执行map函数。(query。limit,sort可以随意组合),
  2. 我们对订单的详细的每次每种产品卖出的数量要大于5的并统计每个产品类型卖出了多少个。
  3. 我们先通过 pnumber进行分组,然后在对 quantity相加
  4. 相当于select pnumber,sum(quantity) from item where quantity>5   group by pnumber
  5. varmap= function() { emit(this.pnumber,this.quantity)}  
  6. var reduce=function(key,values){return {'pumber':key,'quantity':Array.sum(values)}}  
  7. db.item.mapReduce( map,                        
  8.                   reduce,                    
  9.                    { query:{'quantity':{$gt:5}},  
  10.                    out: "map_reduce_data" }      
  11. )  
  12. db.map_reduce_data.find()  
  13. /* 1 */
  14. {
  15.    "_id" : "p001",
  16.    "value" : 10.0
  17. }
  18. /* 2 */
  19. {
  20.    "_id" : "p003",
  21.    "value" : 10.0
  22. }


  • value是JSON对象


  1. value可以是JSON格式,我们对订单的详细统计每个产品类型出现次数。
  2. 我们先通过 pnumber进行分组,然后在对 quantity相加 相当于select pnumber,count(*) from item   group by pnumber。
  3. varmap= function() {emit(this.pnumber,{count:1});}  
  4. var reduce=function(key,values){  
  5.    var count=0;    
  6.    values.forEach(function(val){ count+=val.count;});    
  7.    return {'pumber':key,"count":count};  
  8.  }  
  9. db.item.mapReduce( map,  
  10.                    reduce,  
  11.                    { out: "map_reduce_data" }    
  12. )  
  13. db.map_reduce_data.find()  
  14. /* 1 */
  15. {
  16.    "_id" : "p001",
  17.    "value" : {
  18.        "pumber" : "p001",
  19.        "count" : 2.0
  20.    }
  21. }
  22. /* 2 */
  23. {
  24.    "_id" : "p002",
  25.    "value" : {
  26.        "pumber" : "p002",
  27.        "count" : 3.0
  28.    }
  29. }
  30. /* 3 */
  31. {
  32.    "_id" : "p003",
  33.    "value" : {
  34.        "pumber" : "p003",
  35.        "count" : 3.0
  36.    }
  37. }


  • emit多次的循环


  1. 可以对emit多次的循环,可以根据输入文档的项目字段中的元素的数量(键,值)多次调用:
  1. function() {
  2.        this.items.forEach(function(item){ emit(key, value); });
  3.     }
  • 数据:    
  1. db.orders.insert( [  
  2. {  
  3. "onumber" : "001",  
  4. "item" : [{  
  5.   "quantity" : 2,  
  6.   "price" : 5.0,  
  7.   "pnumber" : "p003"  
  8.  },{  
  9.   "quantity" : 2,  
  10.   "price" : 8.0,  
  11.   "pnumber" : "p002"  
  12.  }]  
  13. },{  
  14. "onumber" : "002",  
  15. "item" : [{  
  16.   "quantity" : 1,  
  17.   "price" : 4.0,  
  18.   "pnumber" : "p002"  
  19.  },{  
  20.   "quantity" : 2,  
  21.   "price" : 4.0,  
  22.   "pnumber" : "p001"  
  23.  },{  
  24.   "quantity" : 4,  
  25.   "price" : 10.0,  
  26.   "pnumber" : "p003"  
  27.  }]  
  28. },{  
  29. "onumber" : "003",  
  30. "item" : [{  
  31.   "quantity" : 10,  
  32.   "price" : 20.0,  
  33.   "pnumber" : "p001"  
  34.  },{  
  35.   "quantity" : 10,  
  36.   "price" : 20.0,  
  37.   "pnumber" : "p003"  
  38.  }]  
  39. },{  
  40. "onumber" : "004",  
  41. "item" : [{  
  42.   "quantity" : 5,  
  43.   "price" : 10.0,  
  44.   "pnumber" : "p002"  
  45.  }]  
  46. }  
  47. ])    
  48. 我们对统计订单中对应的产品销售了多少个,我们先通过 pnumber进行分组,然后在对 quantity相加。
  49. varmap= function() { this.item.forEach(function(it){ emit(it.pnumber,it.quantity); })}  
  50. var reduce=function(key,values){return {'pumber':key,'quantity':Array.sum(values)}}  
  51. db.orders.mapReduce( map,                        
  52.                     reduce,                    
  53.                     { out: "map_reduce_data" }    
  54. )
  • 输出结果:
  1. db.map_reduce_data.find()  
  2. /* 1 */
  3. {
  4.    "_id" : "p001",
  5.    "value" : {
  6.        "pumber" : "p001",
  7.        "quantity" : 12.0
  8.    }
  9. }
  10. /* 2 */
  11. {
  12.    "_id" : "p002",
  13.    "value" : {
  14.        "pumber" : "p002",
  15.        "quantity" : 8.0
  16.    }
  17. }
  18. /* 3 */
  19. {
  20.    "_id" : "p003",
  21.    "value" : {
  22.        "pumber" : "p003",
  23.        "quantity" : 16.0
  24.    }
  25. }
  26. > varmap= function() { this.item.forEach(function(it){ emit(it.pnumber,it.quantity); })}
  27. 也可以这样写
  28. > varmap= function() { for(var i=0;i<this.item.length;i++){ var it=item[i]; emit(it.pnumber,it.quantity); }}


  • reduce函数


  1.  reduce是JavaScript 函数,对map操作的输出做合并的化简的操作
  2. (将key-values变成key-value,也就是把values数组变成一个单一的值value)。  
  1.    function(key, values) {
  2.       ...
  3.       return result;
  4.    }
  1.  values:值参数是一个数组,返回对象的类型必须与由map函数发出的值的类型相同。
  2.  reduce:函数应该交换:即结果中元素的顺序不影响reduce函数的输出。
  1. reduce( key, [ A, B ] ) == reduce( key, [ B, A ] )    
  1. 对map操作的输出做合并的化简的操作(将key-values变成key-value,也就是把values数组变成一个单一的值value),
  2. 我们对订单的详细统计每个产品类型卖出了多少个和每种产品出现次数。
  3. 我们先通过 pnumber进行分组,然后在对 quantity相加
  4. 相当于select pnumber,count(*),sum(quantity) from item   group by pnumber
  5. varmap= function() {  
  6.     var value={count:1, quantity:this.quantity};  
  7.    emit(this.pnumber,value);  
  8.  }  
  9. var reduce=function(key,values){  
  10.                     varreducedVal= { count: 0, quantity: 0 };  
  11.                     for (vari=0; i < values.length; i++) {  
  12.                         reducedVal.count += values[i].count;  
  13.                         reducedVal.quantity += values[i].quantity;  
  14.                     }  
  15.                     return reducedVal;  
  16. }  
  17. db.item.mapReduce( map,                        
  18.                      reduce,                    
  19.                      { out: "map_reduce_data" }      
  20. ).find()  


  • 输出结果:


  1. /* 1 */
  2. {
  3.    "_id" : "p001",
  4.    "value" : {
  5.        "count" : 2.0,
  6.        "quantity" : 12.0
  7.    }
  8. }
  9. /* 2 */
  10. {
  11.    "_id" : "p002",
  12.    "value" : {
  13.        "count" : 3.0,
  14.        "quantity" : 8.0
  15.    }
  16. }
  17. /* 3 */
  18. {
  19.    "_id" : "p003",
  20.    "value" : {
  21.        "count" : 3.0,
  22.        "quantity" : 16.0
  23.    }
  24. }

参考来源: http://www.runoob.com/mongodb/mongodb-map-reduce.html

参考来源:http://blog.csdn.net/congcong68/article/details/51471460



相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
9月前
|
JavaScript 前端开发
解释 JavaScript 中的`map()`、`filter()`和`reduce()`方法的用途。
解释 JavaScript 中的`map()`、`filter()`和`reduce()`方法的用途。
81 1
|
9月前
|
开发者 Python
Python中的函数式编程:理解map、filter和reduce
【2月更文挑战第13天】 本文深入探讨了Python中函数式编程的三个主要工具:map、filter和reduce。我们将详细解释这些函数的工作原理,并通过实例来展示它们如何使代码更简洁、更易读。我们还将讨论一些常见的误解和陷阱,以及如何避免它们。无论你是Python新手还是有经验的开发者,本文都将帮助你更好地理解和使用这些强大的函数。
|
9月前
|
分布式计算 JavaScript 前端开发
JS中数组22种常用API总结,slice、splice、map、reduce、shift、filter、indexOf......
JS中数组22种常用API总结,slice、splice、map、reduce、shift、filter、indexOf......
106 0
|
5月前
|
索引
ES5常见的数组方法:forEach ,map ,filter ,some ,every ,reduce (除了forEach,其他都有回调,都有return)
ES5常见的数组方法:forEach ,map ,filter ,some ,every ,reduce (除了forEach,其他都有回调,都有return)
|
8月前
|
Python
高阶函数如`map`, `filter`, `reduce`和`functools.partial`在Python中用于函数操作
【6月更文挑战第20天】高阶函数如`map`, `filter`, `reduce`和`functools.partial`在Python中用于函数操作。装饰器如`@timer`接收或返回函数,用于扩展功能,如记录执行时间。`timer`装饰器通过包裹函数并计算执行间隙展示时间消耗,如`my_function(2)`执行耗时2秒。
44 3
|
3月前
|
存储 分布式计算 NoSQL
MongoDB Map Reduce
10月更文挑战第23天
49 1
|
5月前
|
JavaScript 前端开发
js map和reduce
js map和reduce
|
7月前
|
人工智能 算法 大数据
算法金 | 推导式、生成器、向量化、map、filter、reduce、itertools,再见 for 循环
这篇内容介绍了编程中避免使用 for 循环的一些方法,特别是针对 Python 语言。它强调了 for 循环在处理大数据或复杂逻辑时可能导致的性能、可读性和复杂度问题。
69 6
算法金 | 推导式、生成器、向量化、map、filter、reduce、itertools,再见 for 循环
|
6月前
|
分布式计算 Python
【python笔记】高阶函数map、filter、reduce
【python笔记】高阶函数map、filter、reduce
|
7月前
|
JavaScript API
js【最佳实践】遍历数组的八种方法(含数组遍历 API 的对比)for,forEach,for of,map,filter,reduce,every,some
js【最佳实践】遍历数组的八种方法(含数组遍历 API 的对比)for,forEach,for of,map,filter,reduce,every,some
137 1