使用聚合框架能夠對集合中的文檔進行變換和組合。基本上,能夠用多個構件建立一個管道(pipeline),用於對一連串的文檔進行處理。這些構件包括篩選(filtering)、投射(projecting)、分組(grouping)、排序(sorting)、限制(limiting)和跳過(skipping)。mongodb
db.driverLocation.aggregate( {"$match":{"areaCode":"350203"}}, {"$project":{"driverUuid":1,"uploadTime":1,"positionType":1}}, {"$group":{"_id":{"driverUuid":"$driverUuid","positionType":"$positionType"},"uploadTime":{"$first":{"$year":"$uploadTime"}},"count":{"$sum":1}}}, {"$sort":{"count":-1}}, {"$limit":100}, {"$skip":50} )
管道操做符是按照書寫的順序依次執行的,每一個操做符都會接受一連串的文檔,對這些文檔作一些類型轉換,最後將轉換後的文檔做爲結果傳遞給下一個操做符(對於最後一個管道操做符,是將結果返回給客戶端),稱爲流式工做方式。數組
大部分操做符的工做方式都是流式的,只要有新文檔進入,就能夠對新文檔進行處理,可是"$group" 和 "$sort" 必需要等收到全部的文檔以後,才能對文檔進行分組排序,而後才能將各個分組發送給管道中的下一個操做符。這意味着,在分片的狀況下,"$group" 或 "$sort"會先在每一個分片上執行,而後各個分片上的分組結果會被髮送到mongos再進行最後的統一分組,剩餘的管道工做也都是在mongos(而不是在分片)上運行的。app
不一樣的管道操做符能夠按任意順序組合在一塊兒使用,並且能夠被重複任意屢次。例如,能夠先作"$match",而後作"$group",而後再作"$match"(與以前的"$match"匹配不一樣的查詢條件)。框架
$fieldname"語法是爲了在聚合框架中引用fieldname字段。ide
用於對文檔集合進行篩選,以後就能夠在篩選獲得的文檔子集上作聚合。例如,若是想對Oregon(俄勒岡州,簡寫爲OR)的用戶作統計,就可使用{$match : {"state" :"OR"}}。"$match"可使用全部常規的查詢操做符("$gt"、"$lt"、"$in"等)。有一個例外須要注意:不能在"$match"中使用地理空間操做符。
一般,在實際使用中應該儘量將"$match"放在管道的前面位置。這樣作有兩個好處:一是能夠快速將不須要的文檔過濾掉,以減小管道的工做量;二是若是在投射和分組以前執行"$match",查詢可使用索引。函數
這個語法與查詢中的字段選擇器比較像:能夠經過指定 {"fieldname" : 1} 選擇須要投射的字段,或者經過指定 { "fieldname":0 } 排除不須要的字段。執行完這個"$project"操做以後,結果集中的每一個文檔都會以{"_id" : id, "fieldname" :"xxx"}這樣的形式表示。這些結果只會在內存中存在,不會被寫入磁盤。ui
還能夠對字段進行重命名:db.users.aggregate({"$project" : {"userId" : "$_id", "_id" : 0}}),在對字段進行重命名時,MongoDB並不會記錄字段的歷史名稱。編碼
若是選定了須要進行分組的字段,就能夠將選定的字段傳遞給"$group"函數的"_id"字段。對於上面的例子:咱們選擇了driverUuid 和 positionType 看成咱們分組的條件(固然只選擇一個字段也是能夠的)。分組事後,文檔的 driverUuid 和 positionType 組成的對象就變成了文檔的惟一標識(_id)。spa
"count":{"$sum":1} 是爲分組內每一個文檔的"count"字段加1。注意,新加入的文檔中並不會有"count"字段;這"$group"建立的一個新字段。 code
排序方向能夠是1(升序)和 -1(降序)。
能夠根據任何字段(或者多個字段)進行排序,與在普通查詢中的語法相同。若是要對大量的文檔進行排序,強烈建議在管道的第一階段進行排序,這時的排序操做可使用索引。不然,排序過程就會比較慢,並且會佔用大量內存。
$limit會接受一個數字n,返回結果集中的前n個文檔。
$skip也是接受一個數字n,丟棄結果集中的前n個文檔,將剩餘文檔做爲結果返回。在「普通」查詢中,若是須要跳過大量的數據,那麼這個操做符的效率會很低。在聚合中也是如此,由於它必需要先匹配到全部須要跳過的文檔,而後再將這些文檔丟棄。
能夠將數組中的每個值拆分爲單獨的文檔。
例如文檔:{ "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] }
聚合運算:db.inventory.aggregate( [ { $unwind : "$sizes" } ] )
結果:
{ "_id" : 1, "item" : "ABC1", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "L" }
Spring Data MongoDB 中使用聚合函數:
/** * db.driverLocation.aggregate( * {"$match":{"areaCode":"350203"}}, * {"$project":{"driverUuid":1,"uploadTime":1,"positionType":1}}, * {"$group":{"_id":{"driverUuid":"$driverUuid","positionType":"$positionType"},"uploadTime":{"$first":{"$year":"$uploadTime"}},"count":{"$sum":1}}}, * {"$sort":{"count":-1}}, * {"$limit":100}, * {"$skip":50} * ) */ @Test public void test04(){ //match Criteria criteria = Criteria.where("350203").is("350203"); AggregationOperation matchOperation = Aggregation.match(criteria); //project AggregationOperation projectionOperation = Aggregation.project("driverUuid", "uploadTime", "positionType"); //group AggregationOperation groupOperation = Aggregation.group("driverUuid", "positionType") .first(DateOperators.dateOf("uploadTime").year()).as("uploadTime") .count().as("count"); //sort Sort sort = new Sort(Sort.Direction.DESC, "count"); AggregationOperation sortOperation = Aggregation.sort(sort); //limit AggregationOperation limitOperation = Aggregation.limit(100L); //skip AggregationOperation skipOperation = Aggregation.skip(50L); Aggregation aggregation = Aggregation.newAggregation(matchOperation, projectionOperation, groupOperation, sortOperation, limitOperation, skipOperation); AggregationResults<Object> driverLocation = mongoOperations.aggregate(aggregation, "driverLocation", Object.class); List<Object> mappedResults = driverLocation.getMappedResults(); }
MongoDB提供了不少的操做符用來文檔聚合後字段間的運算或者分組內的統計,好比上文提到的$sum、$first、$year 等。MongoDB提供了包括分組操做符、數學操做符、日期操做符、字符串表達式 等等 一系列的操做符...
相似 SQL中分組後的操做,只適用於分組後的統計工做,不適用於單個文檔。
適用於單個文檔的運算。
適用於單個文檔的運算。
適用於單個文檔的運算,經過這些操做符,就能夠在聚合中使用更復雜的邏輯,能夠對不一樣數據執行不一樣的代碼,獲得不一樣的結果。
適用於單個文檔的運算,只能對日期類型的字段進行日期操做,不能對非日期類型字段作日期操做。
可參考:https://docs.mongodb.com/manual/reference/operator/aggregation/
應該儘可能在管道的開始階段(執行"$project"、"$group"或者"$unwind"操做以前)就將盡量多的文檔和字段過濾掉。管道若是不是直接從原先的集合中使用數據,那就沒法在篩選和排序中使用索引。若是可能,聚合管道會嘗試對操做進行排序,以便可以有效使用索引。
MongoDB不容許單一的聚合操做佔用過多的系統內存:若是MongoDB發現某個聚合操做佔用了20%以上的內存,這個操做就會直接輸出錯誤。容許將輸出結果利用管道放入一個集合中是爲了方便之後使用(這樣能夠將所需的內存減至最小)。
這篇文章主要摘錄自《MongoDB權威指南第二版》,Mongo系列的最後一篇文章了,最近學MongoDB學得頭都有點大了,準備換個方向學學了...共勉!