MongoDB中聚合(aggregate) 操作將來自多個document的value組合在一起,並通過對分組資料進行各種操作處理,並返回計算後的資料結果,主要用於處理資料(諸如統計平均值,求和等)。MongoDB提供三種方式去執行聚合操作:聚合管道(aggregation pipeline)、Map-Reduce函式以及單一的聚合命令(count、distinct、group)。
1. 聚合管道(aggregation pipeline)
1.1聚合管道
聚合管道是由aggregation framework將文件進入一個由多個階段(stage)組成的管道,可以對每個階段的管道進行分組、過濾等功能,然後經過一系列的處理,輸出相應的聚合結果。如圖所示:
聚合管道操作:
db.orders.aggregate([
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } }
])
複製程式碼
- $match階段:通過status欄位過濾出符合條件的Document(即是Status等於“A”的Document);
- ** $group 階段:按cust_id欄位對Document進行分組,以計算每個唯一cust_id的金額總和。**
1.2 管道
管道在Unix和Linux中一般用於將當前命令的輸出結果作為下一個命令的引數,MongoDB的聚合管道將MongoDB文件在一個管道處理完畢後將結果傳遞給下一個管道處理。管道操作是可以重複的。
最基本的管道功能提供過濾器filter,其操作類似於查詢和文件轉換,可以修改輸出文件的形式。
其他管道操作提供了按特定欄位或欄位對文件進行分組和排序的工具,以及用於聚合陣列內容(包括文件陣列)的工具。 此外,管道階段可以使用運算子執行任務,例如計算平均值或連線字串。總結如下:
管道操作符
常用管道 | 解析 |
---|---|
$group | 將collection中的document分組,可用於統計結果 |
$match | 過濾資料,只輸出符合結果的文件 |
$project | 修改輸入文件的結構(例如重新命名,增加、刪除欄位,建立結算結果等) |
$sort | 將結果進行排序後輸出 |
$limit | 限制管道輸出的結果個數 |
$skip | 跳過制定數量的結果,並且返回剩下的結果 |
$unwind | 將陣列型別的欄位進行拆分 |
表示式操作符
常用表示式 | 含義 |
---|---|
$sum | 計算總和,{$sum: 1}表示返回總和×1的值(即總和的數量),使用{$sum: '$制定欄位'}也能直接獲取制定欄位的值的總和 |
$avg | 求平均值 |
$min | 求min值 |
$max | 求max值 |
$push | 將結果文件中插入值到一個陣列中 |
$first | 根據文件的排序獲取第一個文件資料 |
$last | 同理,獲取最後一個資料 |
為了便於理解,將常見的mongo的聚合操作和MySql的查詢做類比:
MongoDB聚合操作 | MySql操作/函式 |
---|---|
$match | where |
$group | group by |
$match | having |
$project | select |
$sort | order by |
$limit | limit |
$sum | sum() |
$lookup | join |
1.3 Aggregation Pipeline 優化
- 聚合管道可以確定它是否僅需要文件中的欄位的子集來獲得結果。 如果是這樣,管道將只使用那些必需的欄位,減少通過管道的資料量
- 管道序列優化化
管道序列優化化:
1).使用$projector/$addFields+$match 序列優化:當Aggregation Pipeline中有多個$projectior/$addFields階段和$match 階段時,會先執行有依賴的$projector/$addFields階段,然後會新建立的$match階段執行,如下,
{ $addFields: {
maxTime: { $max: "$times" },
minTime: { $min: "$times" }
} },
{ $project: {
_id: 1, name: 1, times: 1, maxTime: 1, minTime: 1,
avgTime: { $avg: ["$maxTime", "$minTime"] }
} },
{ $match: {
name: "Joe Schmoe",
maxTime: { $lt: 20 },
minTime: { $gt: 5 },
avgTime: { $gt: 7 }
} }
複製程式碼
優化執行:
{ $match: { name: "Joe Schmoe" } },
{ $addFields: {
maxTime: { $max: "$times" },
minTime: { $min: "$times" }
} },
{ $match: { maxTime: { $lt: 20 }, minTime: { $gt: 5 } } },
{ $project: {
_id: 1, name: 1, times: 1, maxTime: 1, minTime: 1,
avgTime: { $avg: ["$maxTime", "$minTime"] }
} },
{ $match: { avgTime: { $gt: 7 } } }
複製程式碼
2). $sort + $match 以及$project + $skip,當$sort/$project跟在$match/$skip之後時,會先執行$match/$skip後再執行$sort/$project,$sort以達到最小化需排列的物件數,$skip約束,如下:
{ $sort: { age : -1 } },
{ $match: { score: 'A' } }
{ $project: { status: 1, name: 1 } },
{ $skip: 5 }
複製程式碼
優化執行:
{ $match: { score: 'A' } },
{ $sort: { age : -1 } }
{ $skip: 5 },
{ $project: { status: 1, name: 1 } }
複製程式碼
3). $redact+$match序列優化,當$redact後有$match時,可能會新創一個$match階段進行優化,如下,
{ $redact: { $cond: { if: { $eq: [ "$level", 5 ] }, then: "$$PRUNE", else: "$$DESCEND" } } },
{ $match: { year: 2014, category: { $ne: "Z" } } }
複製程式碼
優化執行:
{ $match: { year: 2014 } },
{ $redact: { $cond: { if: { $eq: [ "$level", 5 ] }, then: "$$PRUNE", else: "$$DESCEND" } } },
{ $match: { year: 2014, category: { $ne: "Z" } } }
複製程式碼
還有很多管道序列優化可以檢視《官方文件-Aggregation Pipeline Optimization》。
1.4 Aggregation Pipeline以及分片(Sharded)collections
如果管道以$match精確分片 key開始的後,所有管道會在匹配的分片上進行。對於需執行在多分片中的聚合(aggregation)操作,如果不不需要在主分片進行的,這些操作後的結果會路由到隨機分片中進行合併結果,避免過載該主分片的資料庫。$out和$look階段必須在主分片資料庫執行。
2. Map-Reduce函式
MongoDB還提供map-reduce操作來執行聚合。 通常,map-reduce操作有兩個階段:一個map階段,它處理每個文件併為每個輸入文件發出一個或多個物件,以及reduce階段組合map操作的輸出。 可選地,map-reduce可以具有最終化階段以對結果進行最終修改。 與其他聚合操作一樣,map-reduce可以指定查詢條件以選擇輸入文件以及排序和限制結果。
Map-reduce使用自定義JavaScript函式來執行對映和減少操作,以及可選的finalize操作。 雖然自定義JavaScript與聚合管道相比提供了極大的靈活性,但通常,map-reduce比聚合管道效率更低,更復雜。模式如下:
3. 單一的聚合命令
MongoDB還提供了,db.collection.estimatedDocumentCount(),db.collection.count()和db.collection.distinct() 所有這些單一的聚合命令。 雖然這些操作提供了對常見聚合過程的簡單訪問操作,但它們缺乏聚合管道和map-reduce的靈活性和功能。模型如下
總結
可使用MongoDB中聚合操作用於資料處理,可以適應於一些資料分析等,聚合的典型應用包括銷售資料的業務報表,比如將各地區的資料分組後計算銷售總和、財務報表等。最後想要更加深入理解還需要自己去實踐。
最後可關注公眾號,一起學習,每天會分享乾貨,還有學習視訊乾貨領取!