MongoDB 3.4 学习笔记 (五):聚合框架

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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容