(续)MongoDb之MapReduce -- 聚合详解

一、MongoDB聚合函数说明

max()与min()

通常总是需要找到给定集合里的某个字段的最大值与最小值。使用sql的数据库则给我们提供了max()与min()函数,但是Mongodb并没有提供这样的函数,所以我们必须自己实现。如果要找到某个字段的最大值,可以按照该字段降序排序,并限制结果集为一个文档;同时,按照相反顺序排序则会获取到该字段的最小值。因此,在monogoDB中的max()与min()则需要对某字段进行排序并限制返回文档数为一即可。(利用sort进行排序-1代表降序,1代表升序。)
    #返回最大值 
    db.test_test.find().sort({rand:-1}).limit(1)
    #返回最小值
    db.test_test.find().sort({rand:-1}).limit(1)
   注意:在实际生产环境中,利用sort排序的时候最好对排序字段加上索引,如果数据量不大,索引与否影响不大,如果数据量大的话,索引的存在对效率影响将是巨大的。
  • distinct

    MongoDB的distinct命令是获取特定字段中不同值列表的最简单工具。该命令既适用于单键,也适用于数组键。distinct默认覆盖整个集合,但也可以通过查询选择器进行约束。如下
    **db.test_test.distinct("rand");**

    这个比较简单。如果希望操作集合的子集,那么我们可以传入一个查询选择器作为第二个参数。如下:

    **db.test_test.distinct("rand", {number:5});**
    
    注意:在实用性的方面,distinct与group有一个很大的限制:它们返回的结果集不能超过16MB。16MB的限制并不是这些命令本身所强加的的阈值,这是所有的初始查询结果集大小。distinct与group是以命令的方式实现的,也就是对特殊$cmd集合的查询,它们赖以生成的查询受制于该限制。如果distinct与group处理不了你的聚合的结果集,那么只能用map-reduce代替了,它的结果可以保存在集合中而非内联(inline)返回。
    

group

group和distinct一样,也是数据库命令,因此它的结果集也受制于16MB的响应限制。而且,为了减少内存消耗,group不会处理多于10000个唯一键。如果聚合操作在此范围内,选择使用group会比使用map-reduce效率高些。group最少需要三个参数。第一个参数是key,定义如何对数据进行分组。第二个参数是一个对结果集做聚合的Javascript函数,叫reduce函数。第三个分组参数是reduce函数的初始文档。
    result = db.test_test.group({
        key: {number:true},
        initial: {rand:0, count:0},
        reduce: function(obj, prev){
            prev.count += 1;
            prev.rand += obj.rand;
        }
    })

PHP中的group简单应用:

    # use all fields
    $keys = array();
    # set intial values
    $initial = array("count" => 0);
    # JavaScript function to perform
    $reduce = "function (obj, prev) { prev.count++; }";
    # only use documents where the "a" field is greater than 1
    $condition = array("a" => array( '$gt' => 1));
    $g = $collection->group($keys, $initial, $reduce, $condition);

group选项解释:

  • key:描述分组字段的文档。键的样式为:{rand:true, number:true}。除非使用keyf,否则key选项是必需的。

  • keyf:这是一个JavaScript函数,应用于文档之上,为该文档生成一个键,当用于分组的键需要计算时,这个函数非常有用。如:function(doc){return

  • {day:doc.created_at.getDay();}}。如果没有指定标准的key的话,则keyf是必需的。

  • initial:作为聚合结果初始文档。reduce函数第一次运行时,该初始文档会作为聚合器的第一个值,通常会包含所有要聚合的键。例:{rand:0, count:0}

  • reduce:用于执行聚合的JavaScript函数。该函数接受两个参数:正初迭代的当前文档和用于存储聚合结果的聚合器文档。如上例的应用,注意:reduce函数并不返回任何内容,它只不过是修改聚合器对象,该参数是必需的。

  • cond:过滤要聚合文档的查询选择器,不多解释了。

  • finalize:在返回结果集之前应用于每个结果文档的JavaScript函数。该函数支持对分组操作的结果进行后置处理。

map-reduce

在添加map-reduce之前,group是MongoDB唯一的聚合器。map-reduce是后来出于一些原因加入的。首先,MapReduce风格的操作正在成为主流(很多开发者是在谷歌那篇著名的关于分布工计算的论文初次看到MapReduce的。其中的思想后来成了Hadoop的基础,而Hadoop是一个使用分布式MapReduce处理大数据的开源框架)。实际原因,对大数据集进行迭代,尤其是在分片配置中,需要有分布式的聚合器,而MapReduce恰恰提供把需的内容。

PHP中的map-reduce的应用:

 $query = array('qq_bind_info.openid'=>array('$exists'=>1));
 $map = new MongoCode('function(){emit(this.qq_bind_info.openid, {uid:this._id, step:1})}');
 $reduce = new MongoCode("function(key, vals){".
        "var user_list = new Array();" . 
        "var open_id = new Array();" . 
        "var sum = 0;" . 
        "vals.forEach(function(value){" .
        "sum += value.step;" .
        "open_id.push(key);" .
        "user_list.push(value.uid);" .
        "});" .  
        "return {userlist:user_list, openid:open_id, sum:sum};}");
 $data = $db->command(array(
            'mapreduce' => 'user_profile',
            'map' => $map, 
            'reduce' => $reduce,
            'query' => $query,
            'out' => 'user_profile_temp'
            ));
 print_r($data);

map-reduce选项解释:

  • map:应用于每个文档之上的JavaScript函数。该函数必须调用emit()来选择要聚合的键和值。在函数上下文中,this的值指向当前文档。例如:function(){emit(this.qq_bind_info.openid,{uid:this._id, step:1})}

  • reduce:一个JavaScript函数,接受一个键和一个值列表。该函数对返回值的结构有严格要求,必须总是与values数组所提供的结构一致。如上$reduce的MongoCode中的function。

  • query:用于过滤处理的集合的查询选择器。与group的con参数相同。 sort:对查询的排序。

  • limit:一个整数,指定了查询和排序的条数。

  • out:这个参数决定了如何返回输出内容。如果需要将输出作为命令的返回结果,则需要传入{inline:true}。注意:这个仅适用于结果集在16MB以内的情况。另一个选择是将结果放到一个集合里,则out参数则是字符串,即集合的名称。

  • finalize:一个JavaScript函数,在reduce阶段完成后会应用于每个返回的文档上。

  • scope:该文档指定了map、reduce和finalize函数可全局访问的变量的值。

  • verbose:一个布尔值,为true时,在命令返回文档中会包含对map-reduce任务执行时间的统计信息。

注意:在out参数为存入到新的集合时,会有一个问题:如果最近有运行过类似的map-reduce,那么可能会覆盖现有数据。因此,还有两个集合输出选项:一个用于合并结果和老数据;另一个对数据进行reduce处理。合并的场景中,使用{merge:’collectionName’},新结果会覆盖拥有相同键的现有项。如果使用{reduce:’collectionName’},会调用reduce函数根据新值来处理现有的键值

    原文作者:MapReduce
    原文地址: https://segmentfault.com/a/1190000000535398
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞