MongoDB 聚合
2015-08-24 10:50
609 查看
聚合框架
先看一个示例:有一个保存着杂志文章的集合,希望找出发表文章最多的前3名作者。那么按照以下步骤创建管道:
1:将每篇文章中的作者投射出来;
2:将作者按照名称排序,统计出每个名字出现的次数;
3:将作者按照名字出现的次数降序排列;
4:将返回的结果限制为前3个。
1:{"$project": {"author": 1}} --> {"_id": id, "author": authorName}
通过"fieldName": 1选择需要投射的字段,默认会投射"_id"字段。
2:{"$group": {"_id": "$author", "count": {"$sum": 1}}} --> {"_id": authorName, "count":articleCount}
指明分组字段"author",该操作执行完后,每个作者只对应一个结果文档,所以"author"就成了文档的唯一标识符("_id")。
3:{"$sort": {"$count": 1}}
4:{"$limit": 3}
> db.article.aggregate( ... {"$project": {"author": 1}}, ... {"$group": {"_id": "$author", "count": {"$sum": 1}}}, ... {"$sort": {"$count": 1}}, ... {"$limit": 3} ) { "result": [ { "_id": "Bob", "count": 490 }, { "_id": "Ellice", "count": 420 }, { "_id": "Nora", "count": 400 } ], "ok": 1 }如果管道没有给出预期的结果,可以按照需要进行调试,逐步添加管道操作符调试。
管道操作符
$match
$match:对于文档集合进行筛选,之后可以在筛选得到的文档子集上做聚合。注意:不能在"$match"上使用地理空间操作符。尽可能将$match放在管道的前面位置,这样做优点:过滤不需要的文档,以减少管道的工作量,在投射和分组之前执行$match,查询可以使用索引。
$project
$project:从文档中提取字段,还可以重命名字段,等做一些有意思的操作。// 将每个用户文档的"_id"在返回结果中重命名成userId: > db.article.aggregate( ... {"$project": {"userId": "$_id", "_id": 0}} ) { "result": [ { "userId": ObjectId("54fd0571e4b055a0030461fb") }, { "userId": ObjectId("54fd0571e4b055a0030461fc") }, ... ], "ok": 1 }注意:对字段进行重命名时,MongoDB并不会记录字段的历史名称。因此,如果在原字段名上有一个索引,被重命名之后,聚合框架之后就无法使用这个索引。因此应该尽量在修改字段名称之前使用索引。
// 聚合框架无法在sort操作中使用在originFieldName上创建的索引: > db.article.aggregate( ... {"$project": {"newFieldName": "$originFieldName"}}, ... {"$sort": {"newFieldName": 1}} )
1.管道表达式
在集合框架中,有几个表达式可以用来组合或进行任意深度的嵌套,以便创建复杂的表达式。
2.数学表达式
// 求总薪资: > db.employees.aggregate( ... { ... "$project": { ... "totalPay": { ... "$add": ["$salary", $bonus] ... } ... } ... } )
操作符 | 语法 | 描述 |
$add | [exp1[, exp2, ..., expN]] | 求和 |
$subtract | [exp1, exp2] | 求差 |
$multiply | [exp1[, exp2, ..., expN]] | 求积 |
$divide | [exp1, exp2] | 求商 |
$mod | [exp1, exp2] | 求余 |
// 计算每个雇员在公司的工作时间:
> db.employees.aggregate(
... {
... "$project": {
... "tenure": {
... "$subtract": [{"$year": new Date()}}, {"$year": "$hireDate"}]
... }
... }
... }
)
提取日期的表达式:"$year", "$month", "$week", "$dayOfMonth", "$dayOfWeek", "$dayOfYear", "$hour", "$minute", "$second"。
4.字符串表达式
// 生成j.doe@example.com格式的email地址,提取firstName第一个字符,并拼接lastName生成指定格式: > db.employees.aggregate( ... { ... "$project": { ... "email": { ... "$concat": [ ... { ... "$substr": ["$firstName", 0, 1], ... ".", ... "$lastName", ... "@example.com" ... } ... ] ... } ... } ... } )
操作符 | 语法 | 描述 |
$substr | [expr, startOffset, numToReturn] | 截取字符串,从startOffset字节开始的numToReturn个字节 |
$concat | [exp1[, exp2, ... , expN]] | 连接字符串 |
$toLower | expr | 转换称小写 |
$toUpper | expr | 转换称大写 |
// 为学生打分,出勤率占10%,日常测验占30%,期末考试占60%,(如果是老师最宠爱的学生,那么分数是100): > db.students.aggregate( ... { ... "$project": { ... "grade": { ... "$cond": [ ... "$teacherPet", ... 100, // if ... { // else ... "$add": [ ... "$multiply": [.1, "$attendanceAvg"], ... "$multiply": [.3, "$quizzAvg"], ... "$multiply": [.6, "$testAvg"] ... ] ... } ... ] ... } ... } ... } )
$cmp | [exp1, exp2] | 相等返回0,exp1<exp2返回负数,否则返回正数 |
$strcasecmp | [string1, string2] | 比较string1和string2,区分大小写,只对罗马字符有效 |
$eq/$ne/$gt/$gte/$le/$lte | [exp1, exp2] | 进行比较操作,返回比较的结果(true/false) |
$and | [exp1[, exp2, ... , expN]] | 与运算 |
$or | [exp1[, exp2, ... , expN]] | 或运算 |
$not | expr | 取反 |
$cond | [booleanExpr, trueExpr, falseExpr] | 如果booleanExpr值为true,返回trueExpr,否则返回falseExpr |
$ifNull | [expr, replacementExpr] | 如果expr是null,返回replacementExpr,否则返回expr |
$group
$group:将文档依据特定字段的不同值进行分组。1.分组操作符
对每一个分组进行计算,得到相应的结果。例:我们最先介绍的示例
2.算术操作符
$sum:value, 对于分组中的每一个文档,将value与计算结果相加。
$avg:value, 返回每个分组的平均值。
3.极值操作符
// 查找学生考试成绩的最高分和最低分: > db.scores.aggregate( ... { ... "$group": { ... "_id": "$grade", ... "lowestScore": {"$min": $score}, ... "highestScore": {"$max": $score} ... } ... } )如果数据是排过序的,那么$first和$last效率比$min和$max高,否则反之。
操作符 | 语法 | 描述 |
$max | expr | 返回分组内的最大值 |
$min | expr | 返回分组内的最小值 |
$first | expr | 返回分组的第一个值,忽略后面的所有值,只有排序之后(数据有序),才有意义 |
$last | expr | 返回分组的最后一个值,忽略前面所有值,只有排序之后(数据有序),才有意义 |
$addToSet:expr, 如果当前数组中不包含expr,那么将其添加到数组中。返回结果集每个元素最多只出现一次,而且元素的顺序时不确定的。
$push:expr, 不管expr是什么值,都将其添加到数组中,返回包含所有值的数组。
$unwind
$unwind:可以将数组中的每一个值拆分为单独的文档。// 拆分博客评论为单个文档 > db.bolgs.aggregate( ... {"$unwind": "$comments"} ) { "result": [ { // 博客公共部分 start "_id": ObjectId("54fd0571e4b055a0030461fb"), "author": "r", "post": "hello world!", // 博客公共部分 end "comments": { "author": "bob", "content": "good post" } }, { "_id": ObjectId("54fd0571e4b055a0030461fb"), "author": "r", "post": "hello world!", "comments": { "author": "bill", "content": "nice post" } }, ... ], "ok": 1 }
$sort
$sort:可以根据任何字段(或者多个字段)进行排序。$limit
$limit:接受一个数字n,返回结果集中的前n个文档。$skip
$skip:接受一个数字n,丢弃结果集中的前n个文档,将剩余文档作为结果返回。MongoDB 聚合管道
相关文章推荐
- mongodb多实例
- MongoDB的索引(三)
- linux下安装MongoDB,配置主从服务
- MongoDB的基本使用(二)
- Windows上安装使用MongoDB(一)
- mongoDB与sql语句对照表
- note.js之 Mongodb在Nodejs上的配置及session会话机制的实现
- win7 64位安装mongodb及管理工具mongoVUE1.6.9.0
- MongoDB之Replica Set+Sharding架构
- MongoDB学习六--MongoDB删除数据文档
- MongoDB
- Mongodb集群之副本集
- MongoDB 清除重复数据
- MongoDB查询简单语法
- MongoDB学习五--MongoDB修改数据文档
- nodejs+express+mongodb安装
- 用 mongodb + elasticsearch 实现中文检索
- mongoDB中的ID的生成原则
- MongoDB入门
- Mongodb语法总结