MongoDB实践八(聚合)
MongoDB之聚合
聚合操作:
- db.collection.aggregate()
- db.<collection>.aggregate(<pipeline>,options)
- <pipeline> 文档定义了操作中使用的聚合管道阶段和聚合操作符
- <options>文档声明了一些聚合操作的参数
- 字段路径表达式:
- $<field> 使用 $ 来指示字段路径
- $<field>.<sub-field> - 使用 $ 和 . 来指示内嵌文档字段路径
- 系统变量表达式
- $$<variable> 使用$$来指示系统变量
- $$CURRENT 指示管道中当前操作的文档
- 常量表达式
- $literal : <value> 指示常量<value>
- $literal : "$name" - 指示这里常量字符串"$name" 这里的$被当做常量处理,而不是字段路径表达式
- 聚合管道阶段
- $project 对输入文档进行再次投影(用来灵活控制输出文档的格式)
- db.accounts.aggregate([{$project:{_id:0,balance:1,clientName:"$name.firstName"}}]) //name字段中的firstName 赋值给了clientName
- db.accounts.aggregate([{$project:{_id:0,balance:1,nameArray:["$name.firstName","$name.middleName","$name.lastName"]}}])
- $match 对输入文档进行筛选
- db.accounts.aggregate([{$match:{"name.firstName":"alice"}},{$project:{"_id":0,balance:1,"client.name":"$name.firstName"}}])
- $limit 筛选出管道内前N篇文档
- $skip 跳出管道前N篇文档
- db.accounts.aggregate([{$match:{"name":"alice"}},{$skip:1},{$limit:1}])
- $unwind 展开输入文档中的数组字段
- db.accounts.aggregate([{$unwind:{path:"$currency"}}])
- db.accounts.aggregate([{$unwind:{path:"$currency",includeArrayIndex:"ccyIndex"}}])
- includeArrayIndex 展开数组时添加元素位置 赋值给 ccyIndex
- 展开数组时保留空数组或不存在数组的文档
- db.accounts.aggregate([{$unwind:{path:"$currency",preserveNullAndEmptyArrays:true}}])
- $sort 对输入文档进行排序
- db.accounts.aggregate([{$sort:{balance:1,"name.lastName":-1}}])
- $lookup 对输入文档进行查询操作 相当于MYSQL 联表操作 localField = foreignField 即ON, from 即 join, as即字段别名
- 1、db.accounts.aggregate([{$lookup:{from:"forex",localField:"currency",foreignField:"ccy",as:"forexData"}}]).pretty()
- 使用复杂条件进行查询
- > $lookup:{
... from:<collection to join>,
... localField:<field from the input documents>,
... foreignField:<field from the documents of the "from" collection>,
... as:<output array field>
... };
- 不关联查询
- db.accounts.aggregate([{$lookup:{from:"forex",pipeline:[{$match:{date:new Date('2018-12-21')}}],as:'forexData'}}]).pretty()
- 关联查询
- db.accounts.aggregate([{$lookup:{ from:"forex",let:{bal:"$balance"},pipeline:[{$match: {$expr: {$and:[{$eq:["$date",new Date('2018-12-21')]},{$gt:["$$bal",100]} ] }}}],as :"forexData"}}]);
- db.accounts.aggregate([{$lookup:{from:"forex",let:{bal:"$balance"},pipeline:[{$match:{$expr:{$and:[{$eq:["$date",new Date("2018-12-21")]},{$gt:["$$bal":{$gt,100}]}]}}}],as:"forexData"}}])
- > $lookup:{
... from:<collection to join>,
... let:{<val_1>:<expression>, ..., <var_n>:<expression> }, //可选参数,对查询集合中的文档使用聚合阶段进行处理时,如果需要参考管道文档中的字段则必须用let参数对字段进行声明
... pipeline:[ <pipeline to execute on the collection to join >], //对查询集合中的文档使用聚合阶段进行处理
... as:<output array field>
... }
- $group 对输入文档进行分组
- 使用聚合操作符创建数组字段
- db.transactions.aggregate([ { $group:{ _id: "$currency", symbols:{$push:"$symbol"} } } ]);
- db.transactions.aggregate([ { $group:{ _id:"$currency" } }]);
- > db.transactions.aggregate([
... {
... $group: {
... _id: "$currency", // _id 换成null 可以统计该集合全部文档
... totalQty:{$sum:"$qty"},
... totalNotional:{$sum:{$multiply:["$price","$qty"]}},
... avgPrice:{$avg:"$price"},
... count:{$sum:1},
... maxNotional:{$max:{$multiply:["$price","$qty"]}},
... minNotional:{$min:{$multiply:["$price","$qty"]}}
... }
... }
... ])
- > $group: {
... _id:<expression>,
... <field1> : { <accumulator1> : <expression1> },
... ...
... }
- $out 将管道中的文档输出
- “将聚合管道中的文档写入一个新集合”
- db.transactions.aggregate([ { $group:{ _id: "$currency",symbols:{$push:"$symbol"} } },{$out:"output"} ]);
- 将聚合管道中的文档写入一个已存在的集合
- db.transactions.aggregate([ { $group:{ _id:"$symbol", totalNotional:{$sum:{$multiply:["$price","$qty"]}}} }, { $out:"output" } ]);
- 如果聚合管道操作遇到错误,管道阶段不会创建新集合或是覆盖已存在的集合内容
db.<collection>.aggregate(<pipeline>,<options>)
- allowDiskUse: <boolean>
- 每个聚合管道阶段使用的内存不能超过100MB
- 如果数据量较大,为了防止聚合管道阶段超出内存上线并且抛出错误,可以启用allowDiskUse选项
- allowDiskUse 启用之后,聚合阶段可以在内存容量不足时,将操作数据写入临时文件中
- 临时文件会被写入dbPath下的_tmp文件夹,dbPath的默认值为 /data/db
聚合操作的优化
- 聚合阶段顺序优化
- $match + $project $match先执行
- > db.transactions.aggregate([
... {
... $project:{
... _id:0,symbol:1,currency:1,
... notional:{$multiply:["$price","$qty"]}
... }
... },
... {
... $match:{
... currency:"USD",
... notional:{$gt:1000}
... }
... }
... ]);
{ "symbol" : "AMZN", "currency" : "USD", "notional" : 1377.5 }
优化后:db.transactions.aggregate([ { $match:{ currency:"USD", } }, { $project:{ _id:0,symbol:1,currency:1,notional:{$multiply:["$price","$qty"]} } }, {$match:{notional:{$gt:1000}}} ]);
- $sort + $match $match阶段会在$sort阶段之前运行
- db.transactions.aggregate([ { $match:{ currency:"USD" } }, { $sort:{ price:1 }} ]);
- $project + $skip $skip阶段会在$project阶段之前运行
- $sort + $limit
- 如果两者之间没有夹杂着会改变文档数量的聚合阶段(例如$match),$sort和$limit阶段可以合并
- $lookup + $unwind
- 连续排列在一起的$lookup 和$unwind阶段,如果$unwind应用再$lookup阶段创建的as字段上,则两者可以合并
- > db.accounts.aggregate(
... [
... {
... $lookup:{
... from:"forex",
... localField:"currency",
... foreignField:"ccy",
... as :"forexData"
... }
... },
... {
... $unwind:"$forexData"
... }
... ]);
Nice to see you all!