Loading... # MongoDb 的 Map-Reduce 使用 (Doctrine) > 使用场景, Collection 上根据某个字段进行统计,如 Mysql 的 group by > > (1)MapReduce使用自定义 JavaScript 函数执行 map 和 reduce 操作,所以是基于js引擎,单线程执行,效率不高,比 Aggregation 复杂,适合用做后台统计等。 > > (2)MapReduce 支持分片操作,可以进行拆分,分发到不同的机器上执行(多服务器并行做数据集合处理),然后再将不同的机器处理的结果汇集起来输出结果。 > > (3)MapReduce能执行单一聚合的所有操作 count、distinct、group,但 group 在当数据量非常大的时候,处理能力就不太好,先筛选再分组,不支持 分片,对数据量有所限制,效率不高。 [MongoDb 官方(带图片很好理解)](https://docs.mongodb.com/manual/core/map-reduce/) [MongoODM Doctrine (Doctrine的例子,只有一个用例)](https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/map-reduce.html#map-reduce) [Example](https://segmentfault.com/a/1190000012319667) [Example](https://blog.csdn.net/fly910905/article/details/78361045) ## JsonMongo 数据: ```data {"_id" : ObjectId("59fa71d71fd59c3b2cd908d7"),"name" : "鲁迅","book" : "呐喊","price" : 38.0,"publisher" : "人民文学出版社"} {"_id" : ObjectId("59fa71d71fd59c3b2cd908d8"),"name" : "曹雪芹","book" : "红楼梦","price" : 22.0,"publisher" : "人民文学出版社"} {"_id" : ObjectId("59fa71d71fd59c3b2cd908d9"),"name" : "钱钟书","book" : "宋诗选注","price" : 99.0,"publisher" : "人民文学出版社"} {"_id" : ObjectId("59fa71d71fd59c3b2cd908da"),"name" : "钱钟书","book" : "谈艺录","price" : 66.0,"publisher" : "三联书店"} {"_id" : ObjectId("59fa71d71fd59c3b2cd908db"),"name" : "鲁迅","book" : "彷徨","price" : 55.0,"publisher" : "花城出版社"} ``` ```javascript var map=function(){emit(this.name,this.price)} var reduce=function(key,value){return Array.sum(value)} var options={out:"totalPrice"} db.sang_books.mapReduce(map,reduce,options); db.totalPrice.find() ``` > emit 函数主要用来实现分组,接收两个参数,第一个参数表示分组的字段,第二个参数表示要统计的数据, > reduce来做具体的数据处理操作,接收两个参数,对应 emit 方法的两个参数,这里使用了Array中的sum函数对price字段进行自加处理。 > option out:"totalPrice" 将查询出来的所有数据输出到 totalPrice 这个 Collection ```data { "_id" : "曹雪芹", "value" : 22.0 } { "_id" : "钱钟书", "value" : 165.0 } { "_id" : "鲁迅", "value" : 93.0 } ``` * 比如查询每个人售价在¥40以上的书: ```javascript var map=function(){emit(this.name,this.book)} var reduce=function(key,value){return value.join(',')} var options={query:{price:{$gt:40}},out:"books"} db.sang_books.mapReduce(map,reduce,options); db.books.find() ``` > 这里的 options 带有 query,可以刷选数据。 * 使用 finalize 操作 ```javascript var f1 = function(key,reduceValue){var obj={};obj.author=key;obj.books=reduceValue; return obj} var map=function(){emit(this.name,this.book)} var reduce=function(key,value){return value.join(',')} db.runCommand({mapreduce:'sang_books',map,reduce,out:"books",finalize:f1}) db.books.find() ``` > 这里使用了 options 上的 finalize,对最后的数据进行处理 result ```data { "_id" : "曹雪芹", "value" : { "author" : "曹雪芹", "books" : "红楼梦" } } { "_id" : "钱钟书", "value" : { "author" : "钱钟书", "books" : "宋诗选注,谈艺录" } } { "_id" : "鲁迅", "value" : { "author" : "鲁迅", "books" : "呐喊,彷徨" } } ``` * 若统计数量 map 可以这样写 ```var map=function(){emit(this.name,1)}``` * options 可以有多个参数,上面使用 ```data { out: <collection>, query: <document>, sort: <document>, limit: <number>, finalize: <function>, scope: <document>, jsMode: <boolean>, verbose: <boolean>, bypassDocumentValidation: <boolean> } ``` ## Doctrine * 根据 collection Product 统计出 brand 的出现次数 ```php $qb = $this->dm->createQueryBuilder(Product::class) ->field('tags.type') ->notEqual('brand') ->map($this->getMapFunction()) ->reduce($this->getReduceFunction()); ->mapReduceOptions([ 'out'=>'brand_map_reduce_result' //output to collection ]); $results = $qb->getQuery()->execute(); private function getMapFunction() { return 'function() { emit(this.brand,1); }'; } private function getReduceFunction() { return 'function(key, values) { return Array.sum(values) }'; } ``` > 以上代码是统计有多少商品同品牌,然后输出到 *brand_map_reduce_result* 表 * 根据 Collection 下有个 tags 的数组,统计出每个 tag 的出现次数 ```php $qb = $this->dm->createQueryBuilder(Product::class) ->map($this->getMapFunction()) ->reduce($this->getReduceFunction()); ->mapReduceOptions([ 'out' => 'map_reduce_tag_result' //output to collection ]); private function getMapFunction() { return 'function() { for (var idx = 0; idx < this.original_tags.length; idx++) { var value = 1; emit(this.original_tags[idx], value); } }'; } private function getReduceFunction() { return 'function(key, values) { return Array.sum(values)}'; } ``` © Allow specification reprint Support Appreciate the author AliPayWeChat Like If you think my article is useful to you, please feel free to appreciate