1. MongoDB 聚合
1.1. 聚合框架
使用聚合框架需要定义一个聚合管道,管道中的每一步输出都是下一步的输入。聚合管道阶段操作有:
-
$project:确定输出文档的字段(投影) -
$match:选择被处理的文档 -
$limit:限制传入到下一步的文档数量 -
$skip:略过确定数量的文档 -
$unwind:将数组字段扩展生成多个输出文档 -
$group:根据特定字段分组文档 -
$sort:对文档排序 -
$out:将管道结果输出到集合 -
$redact:对特定数据的访问控制
使用 match, group, sort 定义一个聚合框架:
db.products.aggregate( [ {$match: …}, {$group: …}, {$sort: …} ] )

SQL 与聚合框架的对比:
| SQL 命令 | 聚合框架操作 |
|---|---|
| SELECT | $project, $group functions: $sum, $min, $avg, etc. |
| FROM | db.collectionName.aggregate(...) |
| JOIN | $unwind |
| WHERE | $match |
| GROUP BY | $group |
| HAVING | $match |
1.2. 聚合管道阶段操作符
1.2.1. $project
投影操作符主要是查询的投影功能:
db.users.aggregate([
{$match: {username: 'kbanker',
hashed_password: 'bd1cfa194c3a603e7186780824b04419'}},
{$project: {first_name:1, last_name:1}}
])
1.2.2. $group
$group 操作符主要用于多文档的数据聚合,也是聚合管道最常用的操作符,该操作符提供了多种统计功能,如max , min , average 。
// 统计月销售信息
> db.orders.aggregate([
... {$match: {purchase_data: {$gte: new Date(2010, 0, 1)}}},
... {$group: {
... _id: {year : {$year :'$purchase_data'},
... month : {$month :'$purchase_data'}},
... count: {$sum:1},
... total: {$sum:'$sub_total'}}},
... {$sort: {_id:-1}}
... ]);
// 输出文档
{ "_id" : { "year" : 2014, "month" : 11 },
"count" : 1, "total" : 4897 }
{ "_id" : { "year" : 2014, "month" : 8 },
"count" : 2, "total" : 11093 }
{ "_id" : { "year" : 2014, "month" : 4 },
"count" : 1, "total" : 4897 }
$group 操作符提供的函数:
| 函数 | 功能 |
|---|---|
| $addToSet | 向数组添加一个非重复元素 |
| $first | 组的第一个值,在进行了 $sort 后才有意义 |
| $last | 组的最后一个值,在进行了 $sort 后才有意义 |
| $max | 组中某字段最大值 |
| $min | 组中某字段最小值 |
| $avg | 组中某字段平均值 |
| $push | 向数组添加一个元素,元素值可重复 |
| $sum | 组中所有值的和 |
1.2.3. $match , $sort , $skip , $limit
标题的操作符分别对应选择特定文档、排序文档、略过文档的数量和限制文档数量。
page_number = 1
product = db.products.findOne({'slug': 'wheelbarrow-9092'})
reviews = db.reviews.aggregate([
{$match: {'product_id': product['_id']}},
{$skip : (page_number - 1) * 12},
{$limit: 12},
{$sort: {'helpful_votes': -1}}
]).toArray();
1.2.4. $unwind
该操作符通过展开数组为每一个数组元素生成一个输出文档:
// 没有展开
> db.products.findOne({},{category_ids:1})
{
"_id" : ObjectId("4c4b1476238d3b4dd5003981"),
"category_ids" : [
ObjectId("6a5b1476238d3b4dd5000048"),
ObjectId("6a5b1476238d3b4dd5000049")
]
}
// 展开数组
> db.products.aggregate([
... {$project : {category_ids:1}},
... {$unwind : '$category_ids'},
... {$limit : 2}
... ]);
{ "_id" : ObjectId("4c4b1476238d3b4dd5003981"),
"category_ids" : ObjectId("6a5b1476238d3b4dd5000048") }
{ "_id" : ObjectId("4c4b1476238d3b4dd5003981"),
"category_ids" : ObjectId("6a5b1476238d3b4dd5000049") }
1.2.5. $out
将管道输出的文档保存到集合中,该操作符必须是管道的最后一个环节。
1.3. 聚合管道函数操作符
1.3.1. 字符串函数
| 函数 | 功能 |
|---|---|
| $concat | 连接函数 |
| $strcasecmp | 比较函数,大小写不敏感 |
| $substr | 字符串子串 |
| $toLower | 转换为小写 |
| $toUpper | 转换为大写 |
1.3.2. 算术运算函数
| 函数 | 功能 |
|---|---|
| $add | 和 |
| $divide | 除 |
| $mod | 余数 |
| $multiply | 乘 |
| $subtract | 减 |
1.3.3. 日期函数
| 函数 | 功能 |
|---|---|
| $dayOfYear | 一年中的某一天 (1...366) |
| $dayOfMonth | 一月中的某一天 (1...31) |
| $dayOfWeek | 一星期中的某一天 (1...7 , 1是星期天) |
| $year | 日期的年份 |
| $month | 日期的月份 |
| $week | 日期的周数 (0...53) |
| $hour | 日期的小时 |
| $minute | 日期的分钟 |
| $second | 日期的秒数 |
| $millisecond | 日期的毫秒数 (0...999) |
1.3.4. 逻辑函数
| 函数 | 功能 |
|---|---|
| $and | 与 |
| $cmp | 比较 |
| $cond | 条件 (if: {}, then: , else: ) |
| $eq | 相等 |
| $gt | 大于 |
| $gte | 大于等于 |
| $ifNull | 替代 Null |
| $lt | 小于 |
| $lte | 小于等于 |
| $ne | 不等于 |
| $not | 非 |
| $or | 或 |
1.3.5. 集合函数
| 函数 | 功能 |
|---|---|
| $setEquals | 集合相等 |
| $setIntersection | 交集 |
| $setDifference | 差集 |
| $setUnion | 并集 |
| $setIsSubset | 子集 |
| $anyElementTrue | 集合中任一值 |
| $allElementsTrue | 集合中所有值 |
1.3.6. 其他函数
| 函数 | 功能 |
|---|---|
| $meta | 文本搜索信息 |
| $size | 数组的大小 |
| $map | 数组每一个成员的操作 |
| $let | 表达式范围中定义变量 |
| $literal | 不执行返回表达式 |
1.4. 聚合管道的性能
影响管道性能的因素:
- 尽量早的减少文档的数量和大小
- 在
$match和$sort阶段使用索引。 - 在管道中使用了除
$match和$sort外的阶段操作后,就不能使用索引了。(打乱了索引键值对)
1.4.1. 聚合管道的选项参数
-
explain(): 返回管道运行的详细信息 -
allowDiskUse:适合处理大型数据。MongoDB 的内存限制是 100MB -
cursor:每次返回文档的数量-
cursor.hasNext():确定是否有下一组 -
cursor.next():返回下一组文档
-
options = { explain:true, allowDiskUse:true, cursor: { batchSize: n } }
db.collection.aggregate(pipeline,options)