Aggregation
聚合(aggregate)指令可以操作一個集合中的文檔,將統計或處理部分的域, 再經過加工后返回到客戶端,這些工作都是在服務器端完成的。如果是使用pipeline,通常就是按順序將整個集合傳進去,然后進行加工處理,但是可以通過一些操作符在傳遞整個集合之前進行篩選。pipline還可以得到index的支持。
Aggregation Pipeline 管道
Pipline Optimization 優化操作
在執行聚合的時候,其中有一個優化的階段,通常就是通過調整操作符之間的相對順序來達到優化的目的,比如下面這些典型的例子。
當在aggregate時使用了$sort + $match 的時候,match在sort之前可能會減少一些集合的排序工作:
{ $match: { status: 'A' } },{ $sort: { age : -1 } }
當在aggregate時使用了$skip + $limit的時候,limit在skip之前可能會減少一些工作量。考慮如下的順序:
{ $skip: 10 },{ $limit: 5 }
先忽略了前10個,再取出5個。在執行skip操作的時候,被操作的是被選中的所有文檔,最終我們才要了其中的5個。再考慮如下的順序:
{ $limit: 15 },{ $skip: 10 }
先限制返回的文檔最多只有15個,然后再操作這15個文檔,進行skip。相對來說,比上面的方式要快了。
優化器還會執行一些相對比較復雜的優化操作。舉個例子,考慮如下的操作順序:
{ $redact: { $cond: { if: { $eq: [ "$level", 5 ] }, then: "$$PRUNE", else: "$$DESCEND" } } }, { $match: { year: 2014, category: { $ne: "Z" } } }
先進行編輯,再進行匹配。這兩個操作似乎是有依賴的,因為編輯了之后可能會有一些field值的變動,會影響后面match的結果。為了優化操作,可以先執行一部分的匹配,減少選中的文檔,再進行redact操作就可以減少了一些的時間啦。所以,優化之后的樣子可能是這樣的:
{ $match: { year: 2014 } }, { $redact: { $cond: { if: { $eq: [ "$level", 5 ] }, then: "$$PRUNE", else: "$$DESCEND" } } }, { $match: { year: 2014, category: { $ne: "Z" } } }
Pipeline Limits 操作限制
自2.6開始,aggregate命令就可以返回一個cursor或者將結果存儲于一個集合中。每個文檔的大小是符合BSON大小限制的(16MB),若超過了這個限制就會產生error。這個限制僅針對于返回的文檔,而pipeline執行的過程中可能會超過這個限制。
如果沒有指定要返回一個cursor或者是將結果存儲于一個集合中,那么MongoDB默認返回一個包含結果的文檔,此時如果超出BSON大小限制的話就會產生錯誤。
pipeline stage(翻譯為階段?)的內存限制是100MB,一旦超過限制就會出錯。那要是文件比較大呢?可以考慮allowDiskUse選項,它會在必要時將臨時數據存儲到磁盤中去。
Map-Reduce 函數支持
mapreduce提供了一些aggregation pipline不支持的操作,使用起來更加靈活。它的樣子就跟一個函數一樣,返回的是文檔集合。
mapreduce操作可以將操作結果寫進集合中或者僅僅返回結果,操作過程中可以執行一連續的map、reduce等操作,對上一次的輸出進行輸入。
Map-Reduce exampes 例子
假設有一個orders集合,每個文檔表示一個訂單,文檔大致如下所示:
{
_id: ObjectId("50a8240b927d5d8b5891743c"),
cust_id: "abc123",
ord_date: new Date("Oct 04, 2012"),
status: 'A',
price: 25,
items: [ { sku: "mmm", qty: 5, price: 2.5 },
{ sku: "nnn", qty: 5, price: 2.5 } ]
}
現在要查詢每個用戶一共支付了多少錢,這需要對每個訂單根據用戶id進行分類,再對其中的price
域,進行求和