MongoDB學習筆記——聚合操作之聚合管道(Aggregation Pipeline)


MongoDB聚合管道

使用聚合管道可以對集合中的文檔進行變換和組合。 管道是由一個個功能節點組成的,這些節點用管道操作符來進行表示。聚合管道以一個集合中的所有文檔作為開始,然后這些文檔從一個操作節點流向下一個節點 ,每個操作節點對文檔做相應的操作。這些操作可能會創建新的文檔或者過濾掉一些不符合條件的文檔,在管道中可以對文檔進行重復操作。 管道表達式只可以操作當前管道中的文檔,不能訪問其他的文檔:表達式操作可以在內存中完成對文檔的轉換。

語法格式:

 

db.runCommand({ 

 aggregate: "<collection>", 

 pipeline: [ <stage>, <...> ], 

 explain: <boolean>, 

 allowDiskUse: <boolean>, 

 cursor: <document>, 

 bypassDocumentValidation: <boolean> 

 }) 

// 

 db.collection.aggregate([ <pipeline>, <...> ], options) 

參數說明:

操作符

描述

aggregate

要聚合的集合名稱

pipeline

管道操作符

explain

返回指定aggregate各個階段管道的執行計划信息

allowDiskUse

每個階段管道限制為100MB的內存,如果大於100MB的數據可以先寫入臨時文件。設置為true時,aggregate操作可時可以先將數據寫入對應數據目錄的子目錄中 的唯一並以_tmp結尾的文檔中。

cursor

指定游標的初始批批大小。光標的字段的值是一個與場batchSize文件。 }

bypassDocumentValidation

只有當你指定了$out操作符,使db.collection.aggregate繞過文檔驗證操作過程中。這讓您插入不符合驗證要求的文檔。

管道操作符:

管道操作符

描述

$project

數據投影,主要用於重命名、增加和刪除字段

$match

過濾操作,篩選符合條件文檔,作為下一階段的輸入 $match的語法和查詢表達式db.collection.find() 的語法相同 注意:1. 不能在$match操作符中使用$where 表達式操作符。 2. $match盡量出現在管道的前面,這樣可以提早過濾文檔,加快聚合速度。 3. 如果$match出現在最前面的話,可以使用索引來加快查詢。

$limit

限制經過管道的文檔數量 $limit的參數只能是一個正整數

$skip

從待操作集合開始的位置跳過文檔的數目 $skip參數也只能為一個正整數

$unwind

將數組分解為單個的元素,並與文檔的其余部分一同返回 注意:1.如果$unwind目標字段不存在,則整個文檔都會被忽略過濾掉 2.如果$unwind目標字段不是一個數組,則會報錯 3.如果$unwind目標字段數組為空,則該文檔也會被忽略過濾掉

$group

可以將文檔依據指定字段的不同值進行分組,如果選定了需要進行分組的字段,就可以將指定的字段傳遞給$group函數的_id字段 注意:1.$group的輸出是無序的。 2.$group操作默認實在內存中進行的,超過此限制會報錯,若要允許處理大型數據集,allowDiskUse 將選項設置為啟用 $group 操作真實寫入臨時文件。具體請參考官方文檔

$sort

對文檔按照指定字段排序 注意:1.如果將$sort放到管道前面的話可以利用索引,提高效率 2.在管道中如果$sort出現在$limit之前的話,$sort只會對前$limit個文檔進行操作,這樣在內存中也只會保留前$limit個文檔,從而可以極大的節省內存 3.$sort操作符默認在內存中進行,,超過此限制會報錯,若要允許處理大型數據集,allowDiskUse 將選項設置為啟用 $group 操作真實寫入臨時文件。具體請參考官方文檔

$geoNear

會返回一些坐標值,這些值以按照距離指定點距離由近到遠進行排序

$sample

從待操作的集合中隨機返回指定數量的文檔 注意:如果指定的數量 N 大於等於集合文檔總數的5%$sample 執行集合掃描,執行排序,然后選擇前 N 的文檔(受排序的內存限制) 如果 N 是小於 5%的集合中的文檔總數 如果使用 WiredTiger 存儲引擎,$sample 使用偽隨機游標在抽樣 N 文檔集合。 如果使用 MMAPv1 存儲引擎,$sample 使用 _id 索引隨機選擇 N 個文檔。

$lookup

用於與統一數據庫中其他集合之間進行join操作

$out

用戶將聚合的結果輸出到指定的集合,如果要使用$out則必須在整個管道操作的最后階段 如果指定的集合尚不存在,$out 操作會在當前數據庫中創建一個新的集合。集合不是可見的直到聚合完成。如果聚合失敗,MongoDB 不會創建集合。 如果集合指定的 $out 操作已經存在,然后完成后的聚合,$out 階段以原子方式以新的結果集合替換現有集合的,$out 操作不會更改任何存在於以前的集合的索引。如果聚合失敗$out 則不會對現有集合做任何更改。

$redact

字段所處的document結構的級別. $redact還有三個重要的參數: 1$$DESCEND 返回包含當前document級別的所有字段,並且會繼續判字段包含內嵌文檔,內嵌文檔的字段也會去判斷是否符合條件。 2$$PRUNE:返回不包含當前文檔或者內嵌文檔級別的所有字段,不會繼續檢測此級別的其他字段,即使這些字段的內嵌文檔持有相同的訪問級別。 3$$KEEP:返回包含當前文檔或內嵌文檔級別的所有字段,不再繼續檢測此級別的其他字段,即使這些字段的內嵌文檔中持有不同的訪問級別。

聚合管道操作實例

提取字段

 db.order.aggregate({ $project: { cust_id: 1, price: 1 } }); 

db.order.aggregate({ $project: { items: 1, items: { sku: 1 } } }); 

重命名字段

db.order.aggregate({ $project: { "orderid": '$_id', _id: 0, "custid": '$cust_id' } }); 

db.order.aggregate({ $project: { items: { '_sku': '$sku', sku: 1 } } }) 

新建文檔

db.order.aggregate({ 

 $project: { 

 price: 1, 

 details: { price: '$items.price' } 

 } 

}); 

注意:由於對字段進行重命名時,MongoDB並不會記錄字段的歷史名稱,所以如果針對原來的字段創建過索引,那么聚合管道在進行排序時無法在下面的排序操作時使用索引, 應當盡量在修改字段名稱之前使用排序

 db.order.aggregate([{ $project: { "orderid": '$_id', _id: 0, "custid": '$cust_id' } }, { $sort: { custid: -1 } }]) 

//建議寫法 

db.order.aggregate([{ $sort: { cust_id: -1 } }, { $project: { "orderid": '$_id', _id: 0, "custid": '$cust_id' } }]) 

{ $add: [ <expression1>, <expression2>, ... ] } 將多個數字或日期進行相加,只支持數字和時間格式,如果其中一個參數是日期,$add會將其他參數視為要添加到日期的毫秒

db.order.aggregate({ 

 $project: { 

 "add": { 

 "$add": ["$price", 1] 

 } 

 } 

}) 

{ $subtract: [ <expression1>, <expression2> ] } 接受兩個表達式作為參數,使用第一個表達式減去第二個表達式作為結果

db.order.aggregate({ 

 $project: { 

 "subtract": { 

 "$subtract": [{ "$add": ["$price", 12] }, 10] 

 } 

 } 

}) 

{ $multiply: [ <expression1>, <expression2>, ... ] } 接受一個或多個表達式,並將它們相乘

db.order.aggregate({ 

 $project: { 

 "multiply": { "$multiply": ["$price", 3] } 

 } 

}) 

{ $divide: [ <expression1>, <expression2> ] } 接受兩個表達式作為參數,使用第一個表達式除以第二個表達式的商作為結果

db.order.aggregate({ 

 $project: { 

 "divide": { "$divide": ["$price", 3] } 

 } 

}) 

更多數學表達式參考官方文檔 https://docs.mongodb.com/manual/reference/operator/aggregation-arithmetic/

{ $cond: { if: <boolean-expression>, then: <true-case>, else: <false-case-> } } { $cond: [ <boolean-expression>, <true-case>, <false-case> ] } 如果boolean-expressiontrue則執行true-case否則執行false-case

 

db.order.aggregate({ 

 $project: { 

 "price": { 

 "$cond": { if: { $gt: ["$price", 25] }, then: true, else: false } 

 } 

 } 

}); 

// 

db.order.aggregate({ 

 $project: { 

 "price": { 

 "$cond": [{ $gt: ["$price", 25] }, true, false] 

 } 

 } 

}); 

{ $ifNull: [ , ] } 如果expression null 則返回replacement-expression-if-null 否則返回expression的值

 

db.order.aggregate({ 

 $project: { 

 "total": { 

 "$ifNull": ["$total", 0] 

 } 

 } 

}) 

{ $and: [ <expression1>, <expression2>, ... ] } 如果所有表達式都返回true,則結果為true,否則為false 除了布爾類型false,以下幾種類型也為false,如:null,0,undefined ;其它值則為true,包括非0的值及數組

 //Example          Result 

{ $and: [1, "green"] } true 

{ $and: [] } true 

{ $and: [[null], [false], [0]] } true 

{ $and: [null, true] } false 

{ $and: [0, true] } false 

{ $or: [ <expression1>, <expression2>, ... ] } 只要有任意表達式為true,則結果為true,否則為false 除了布爾類型false,以下幾種類型也為false,如:null,0,undefined ;其它值則為true,包括非0的值及數組

 

//Example          Result 

{ $or: [ true, false ] }          true 

{ $or: [ [ false ], false ] }     true 

{ $or: [ null, 0, undefined ] }     false 

{ $or: [] } false 

{ $not: [ <expression> ] } expression取反 除了布爾類型false,以下幾種類型也為false,如:null,0,undefined ;其它值則為true,包括非0的值及數組

//total不存在為false,取反結果則為true 

db.order.aggregate({ 

 $project: { 

 not: { 

 $not: "$total" 

 } 

 } 

}) 

$match 過濾操作,篩選符合條件文檔,作為下一階段的輸入 $match的語法和查詢表達式db.collection.find() 的語法相同

 db.order.aggregate([{ $match: { cust_id: "1" } }, { 

 $project: { 

 "total": { 

 "$ifNull": ["$total", 0] 

 } 

 } 

}]) 

$out 用戶將聚合的結果輸出到指定的集合

db.order.aggregate([{ $match: { cust_id: "1" } }, { 

 $project: { 

 "total": { 

 "$ifNull": ["$total", 0] 

 } 

 } 

}, 

{ $out: "testaggregate" }]) 

$unwind 將數組分解為單個的元素,並與文檔的其余部分一同返回

 db.order.aggregate({$unwind:"$items"}) 

$group 對數據進行分組 $group的時候必須要指定一個_id域,同時也可以包含一些算術類型的表達式操作符

 db.order.aggregate([ 

 { 

 $unwind: "$items" 

 }, 

 { 

 $group: { 

 _id: "$cust_id", 

 qty: { 

 $sum: "$items.qty" 

 } 

 } 

 } 

]) 

關系型數據庫與MongoDB關於聚合的一些對比

關系型數據庫

Mongodb

WHERE

$match

GROUP BY

$group

HAVING

$match

SELECT

$project

ORDER BY

$sort

LIMIT

$limit

SUM()

$sum

COUNT()

$sum

join

$lookup


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM