聚合(aggregate)框架提供一种方法来计算汇总值,虽然映射化简是强大的,但它往往比简单的汇总任务更困难,如字段值总和或平均值。MongoDB的聚合框架实现sum()、avg()、group by等聚合操作。通过聚合框架,还可对返回的结果进行处理,实现一些特殊需求,例如数据过滤、别名显示、增加字段、提取子字段等。
1、聚合框架的基础
管道和表达式
1.1管道:
集合管道从一个 管道运算符 流运文档到下一个的方式来处理文档。管道与unix管道类似,实质就是把扫描的数据输入聚合进程,进行一些过滤、分组、求和等操作,这些操作是通过管道操作符完成的。在管道中,管道运算符可以重复。所有的管道操作符都是处理文档流的,如果操作符扫描 collection 管道将起作用,并且将所有匹配的文档插入管道的”顶部”,管道里操作符转换通过管道的每个文档。管道操作符不需要为每一个输入文档生成一个输出文档:操作符也可能会产生新的文档或过滤文档。管道不能运算以下类型的值: Binary, Symbol, MinKey, MaxKey, DBRef, Code, 以及 CodeWScope。
管道运算符:$project、$match、$limit、$skip、$unwind、$group、$sort
$project:用于设定数据的筛选字段,就像我们SQL中select需要的字段一样。例子:
db.runCommand({ aggregate : "article", pipeline : [
{ $match : { author : "dave" } },
{ $project : {
_id : 0,
author : 1,
tags : 1
}}
]});
上面就是将所有author为dave的记录的author和tags两个字段取出来。(_id:0 表示去掉默认会返回的_id字段)。利用find()功能实现:
> db.article.find({ author : "dave" }, { _id : 0, author : 1, tags : 1);
$match:$match的作用是过滤数据,通过设置一个条件,将数据进行筛选过滤。例子:
db.runCommand({ aggregate : "article", pipeline : [
{ $match : { author : "dave" } }
]});
这相当于将article这个collection中的记录进行筛选,筛选条件是author属性值为dave,其作用其实相当于普通的find命令,如:
> db.article.find({ author : "dave" });
与find不同,find的结果是直接作为最终数据返回,而$match只是pipeline中的一环,它筛选的结果数据可以再进行下一级的统计操作。
$limit:限制了的文档数量看一下由从当前位置开始的给定数。例子:
>db.mycol.find({},{"title":1,_id:0}).limit(2)
{"title":"MongoDB Overview"}
{"title":"NoSQL Overview"}
>
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。例子:
>db.mycol.find({},{"title":1,_id:0}).limit(1).skip(1)
{"title":"NoSQL Overview"}
>
$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。例子:
比如你使用下面命令添加一条记录:
db.article.save( {
title : "this is your title" ,
author : "dave" ,
posted : new Date(4121381470000) ,
pageViews : 7 ,
tags : [ "fun" , "nasty" ] ,
comments : [
{ author :"barbara" , text : "this is interesting" } ,
{ author :"jenny" , text : "i like to play pinball", votes: 10 }
],
other : { bar : 14 }
});
这里面tags字段就是一个array。下面我们在这个字段上应用$unwind操作
db.runCommand({ aggregate : "article", pipeline : [
{ $unwind : "$tags" }
]});
$group:按某一个key将key值相同的多条数据组织成一条。例子:
db.runCommand({ aggregate : "article", pipeline : [
{ $unwind : "$tags" },
{ $group : {
_id : "$tags",
count : { $sum : 1 },
authors : { $addToSet : "$author" }
}}
]});
$sort:将输入文档排序后输出
1.2表达式:
表达式基于对输入文件进行计算而生成输出文件. 聚合框架定义表达式中使用的一种文件格式,使用前缀。表达式是无状态的,并只在被集合进程看到时才计算。管道里的 表达式只能操作当前文档,无法整合其他文档中的数据。 表达式:$sum、$avg、$min、$max、$push、$addToSet、$first、$last
$sum:总结从集合中的所有文件所定义的值。例子:
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
$avg:从所有文档集合中所有给定值计算的平均。
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
$min:获取集合中的所有文件中的相应值最小。
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
$max:获取集合中的所有文件中的相应值的最大。
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
$push:值插入到一个数组生成文档中。
db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
$addToSet:值插入到一个数组中所得到的文档,但不会创建重复。
db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
$first:根据分组从源文档中获取的第一个文档。通常情况下,这才有意义,连同以前的一些应用 “$sort”-stage。
db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
$last:根据分组从源文档中获取最后的文档。通常,这才有意义,连同以前的一些应用 “$sort”-stage。
db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])
2、聚合使用
在 mongo 壳或者 数据库命令 aggregate 里使用 aggregate() 包装器调用 聚合`。通常在集合对象里调用 :method:`~db.collection.aggregate() 来决定集合 管道 输入文档。:method:~db.collection.aggregate() 方法的参数指定一系列 管道算子,其中每个操作符可以有许多操作数。
数据:
{
_id: ObjectId(7df78ad8902c)
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by_user: 'w3cschool.cc',
url: 'http://www.w3cschool.cc',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
},
{
_id: ObjectId(7df78ad8902d)
title: 'NoSQL Overview',
description: 'No sql database is very fast',
by_user: 'w3cschool.cc',
url: 'http://www.w3cschool.cc',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 10
},
{
_id: ObjectId(7df78ad8902e)
title: 'Neo4j Overview',
description: 'Neo4j is no sql database',
by_user: 'Neo4j',
url: 'http://www.neo4j.com',
tags: ['neo4j', 'database', 'NoSQL'],
likes: 750
},
使用及结果:
> db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
{
"result" : [
{
"_id" : "w3cschool.cc",
"num_tutorial" : 2
},
{
"_id" : "Neo4j",
"num_tutorial" : 1
}
],
"ok" : 1
}
>
$push 和$addToSet 区别
$addToSet同样可以向数组中添加元素,与$push不同的是$addToSet会保证元素的唯一性,利用$unwind可以在聚合中实现distinct的功能,这里是stackoverflow 上的一篇介绍,点击。