MongoDB 聚合Group(二)

轉自:https://blog.csdn.net/congcong68/article/details/51620040

1、簡介:

      上一篇咱們對   db.collection.aggregate(pipeline, options)介紹,咱們接下來介紹pipeline 參數和options參數的基礎認識sql

 

  【pipeline 參數】mongodb

 

     pipeline 類型是Array  語法:db.collection.aggregate( [ { <stage> }, ... ] )shell

 

         $project:能夠對輸入文檔進行添加新字段或刪除現有的字段,能夠自定哪些字段顯示與不顯示。express

         $match :根據條件用於過濾數據,只輸出符合條件的文檔,若是放在pipeline前面,根據條件過濾數據,傳輸到下一個階段管道,能夠提升後續的數據處理效率。還能夠放在out以前,對結果進行再一次過濾。數組

         $redact :字段所處的document結構的級別.性能

         $limit :用來限制MongoDB聚合管道返回的文檔數spa

         $skip :在聚合管道中跳過指定數量的文檔,並返回餘下的文檔。.net

         $unwind :將文檔中的某一個數組類型字段拆分紅多條,每條包含數組中的一個值。    指針

         $sample :隨機選擇從其輸入指定數量的文檔。若是是大於或等於5%的collection的文檔,$sample進行收集掃描,進行排序,而後選擇頂部的文件。所以,$sample 在收集階段是受排序的內存限制。blog

                          語法:  { $sample: { size: <positive integer> } }

         $sort :將輸入文檔排序後輸出。

         $geoNear:用於地理位置數據分析。

         $out :必須爲pipeline最後一個階段管道,由於是將最後計算結果寫入到指定的collection中。

         $indexStats :返回數據集合的每一個索引的使用狀況。

                        語法:{ $indexStats: { } }

         $group : 將集合中的文檔分組,可用於統計結果,$group首先將數據根據key進行分組。

 

    咱們能夠經過Aggregation pipeline一些操做 跟sql用法同樣,咱們能很清晰的怎麼去使用

 

              pipeline                                         sql

              $project                                         select

              $match                                          where

              $match(先$group階段管道 而後在$match對統計的結果進行再一次的過濾)      having

                   $sort                                                           order by

                   $limit                                                          limit

 

 2、基礎的操做

 

    【pipeline簡單例子】        

        數據:   

 

[sql]  view plain  copy
 
  1. db.items.insert( [  
  2.   {  
  3.    "quantity" : 2,  
  4.    "price" : 5.0,  
  5.    "pnumber" : "p003",  
  6.   },{  
  7.    "quantity" : 2,  
  8.    "price" : 8.0,  
  9.    "pnumber" : "p002"  
  10.   },{  
  11.    "quantity" : 1,  
  12.    "price" : 4.0,  
  13.    "pnumber" : "p002"  
  14.   },{  
  15.    "quantity" : 2,  
  16.    "price" : 4.0,  
  17.    "pnumber" : "p001"  
  18.   },{  
  19.    "quantity" : 4,  
  20.    "price" : 10.0,  
  21.    "pnumber" : "p003"  
  22.   },{  
  23.    "quantity" : 10,  
  24.    "price" : 20.0,  
  25.    "pnumber" : "p001"  
  26.   },{  
  27.    "quantity" : 10,  
  28.    "price" : 20.0,  
  29.    "pnumber" : "p003"  
  30.   },{  
  31.    "quantity" : 5,  
  32.    "price" : 10.0,  
  33.    "pnumber" : "p002"  
  34.   }  
  35. ])       



 

 

 

 【 $project】

       一、咱們對上面的統計結果,只顯示count,不想_id ,能夠經過$project來操做,至關SQL的select  顯示咱們想要的字段:

 

[sql]  view plain  copy
 
  1. > db.items.aggregate([{$group:{_id:null,count:{$sum:1}}},{$project:{"_id":0,"count":1}}])  
  2.  { "count" : 8 }  

 

 

 

 【 $match】

     一、咱們想經過濾訂單中,想知道賣出的數量大於8的產品有哪些產品,至關於SQL:select sum(quantity) as total from  items  group by pnumber  having total>8   

 

[sql]  view plain  copy
 
  1. > db.items.aggregate([{$group:{_id:"$pnumber",total:{$sum:"$quantity"}}},{$match:{total:{$gt:8}}}])  
  2. "_id" : "p001", "total" : 12 }  
  3. "_id" : "p003", "total" : 16 }  

 

 

     二、若是是放在$group以前就是當作where來使用,咱們只統計pnumber =p001 產品賣出了多少個  select sum(quantity) as total from  items   where pnumber='p001'     

 

[sql]  view plain  copy
 
  1. > db.items.aggregate([{$match:{"pnumber":"p001"}},{$group:{_id:null,total:{$sum:"$quantity"}}}])  
  2. "_id" : null, "total" : 12 }  

 

 

 【$skip、 $limit、$sort】

  db.items.aggregate([{ $skip: 2 },{ $limit: 4 }])     與db.items.aggregate([{ $limit: 4 },{ $skip: 2 }])  這樣結果是不同的

 

[sql]  view plain  copy
 
  1. > db.items.aggregate([{ $skip: 2 },{ $limit: 4 }])  
  2. "_id" : ObjectId("574d937cfafef57ee4427ac4"), "quantity" : 1, "price" : 4, "pnumber" : "p002" }  
  3. "_id" : ObjectId("574d937cfafef57ee4427ac5"), "quantity" : 2, "price" : 4, "pnumber" : "p001" }  
  4. "_id" : ObjectId("574d937cfafef57ee4427ac6"), "quantity" : 4, "price" : 10, "pnumber" : "p003" }  
  5. "_id" : ObjectId("574d937cfafef57ee4427ac7"), "quantity" : 10, "price" : 20, "pnumber" : "p001" }  
  6. > db.items.aggregate([{ $limit: 4 },{ $skip: 2 }])  
  7. "_id" : ObjectId("574d937cfafef57ee4427ac4"), "quantity" : 1, "price" : 4, "pnumber" : "p002" }  
  8. "_id" : ObjectId("574d937cfafef57ee4427ac5"), "quantity" : 2, "price" : 4, "pnumber" : "p001" }  
  9.    

 

 

   $limit、$skip、$sort、$match可使用在階段管道,若是使用在$group以前能夠過濾掉一些數據,提升性能。

 

【$unwind】  

  將文檔中的某一個數組類型字段拆分紅多條,每條包含數組中的一個值。

   

[sql]  view plain  copy
 
  1. > db.items.aggregate([{$group:{_id:"$pnumber",quantitys:{$push:"$quantity"}}}])  
  2. "_id" : "p001", "quantitys" : [ 2, 10 ] }  
  3. "_id" : "p002", "quantitys" : [ 2, 1, 5 ] }  
  4. "_id" : "p003", "quantitys" : [ 2, 4, 10 ] }  
  5. > db.items.aggregate([{$group:{_id:"$pnumber",quantitys:{$push:"$quantity"}}},{$unwind:"$quantitys"}])  
  6. "_id" : "p001", "quantitys" : 2 }  
  7. "_id" : "p001", "quantitys" : 10 }  
  8. "_id" : "p002", "quantitys" : 2 }  
  9. "_id" : "p002", "quantitys" : 1 }  
  10. "_id" : "p002", "quantitys" : 5 }  
  11. "_id" : "p003", "quantitys" : 2 }  
  12. "_id" : "p003", "quantitys" : 4 }  
  13. "_id" : "p003", "quantitys" : 10 }  

 

 

【$out】

   必須爲pipeline最後一個階段管道,由於是將最後計算結果寫入到指定的collection中。

    

[sql]  view plain  copy
 
  1. "_id" : "p001", "quantitys" : 2 }  
  2. "_id" : "p001", "quantitys" : 10 }  
  3. "_id" : "p002", "quantitys" : 2 }  
  4. "_id" : "p002", "quantitys" : 1 }  
  5. "_id" : "p002", "quantitys" : 5 }  
  6. "_id" : "p003", "quantitys" : 2 }  
  7. "_id" : "p003", "quantitys" : 4 }  
  8. "_id" : "p003", "quantitys" : 10 }  

 

 

   把結果放在指定的集合中result

   

[sql]  view plain  copy
 
  1. > db.items.aggregate([{$group:{_id:"$pnumber",quantitys:{$push:"$quantity"}}},{$unwind:"$quantitys"},{$project:{"_id":0,"quantitys":1}},{$out:"result"}])  
  2. > db.result.find()  
  3. "_id" : ObjectId("57529143746e15e8aa207a29"), "quantitys" : 2 }  
  4. "_id" : ObjectId("57529143746e15e8aa207a2a"), "quantitys" : 10 }  
  5. "_id" : ObjectId("57529143746e15e8aa207a2b"), "quantitys" : 2 }  
  6. "_id" : ObjectId("57529143746e15e8aa207a2c"), "quantitys" : 1 }  
  7. "_id" : ObjectId("57529143746e15e8aa207a2d"), "quantitys" : 5 }  
  8. "_id" : ObjectId("57529143746e15e8aa207a2e"), "quantitys" : 2 }  
  9. "_id" : ObjectId("57529143746e15e8aa207a2f"), "quantitys" : 4 }  
  10. "_id" : ObjectId("57529143746e15e8aa207a30"), "quantitys" : 10 }  

 

【$redact】

     語法:{ $redact: <expression> }

   $redact 跟$cond結合使用,並在$cond裏面使用了if 、then、else表達式,if-else缺一不可,$redact還有三個重要的參數:

     1)$$DESCEND: 返回包含當前document級別的全部字段,而且會繼續判字段包含內嵌文檔,內嵌文檔的字段也會去判斷是否符合條件。

     2)$$PRUNE:返回不包含當前文檔或者內嵌文檔級別的全部字段,不會繼續檢測此級別的其餘字段,即便這些字段的內嵌文檔持有相同的訪問級別。

     3)$$KEEP:返回包含當前文檔或內嵌文檔級別的全部字段,再也不繼續檢測此級別的其餘字段,即便這些字段的內嵌文檔中持有不一樣的訪問級別。

 

 

  一、  level=1則值爲爲$$DESCEND,不然爲$$PRUNE,從頂部開始掃描下去,執行$$DESCEND包含當前document級別的全部fields。當前級別字段的內嵌文檔將會被繼續檢測。

 

[sql]  view plain  copy
 
  1. db.redact.insert(  
  2.   {  
  3.   _id: 1,  
  4.   level: 1,  
  5.   status: "A",  
  6.   acct_id: "xyz123",  
  7.   cc: [{  
  8.     level: 1,  
  9.     type: "yy",  
  10.     num: 000000000000,  
  11.     exp_date: ISODate("2015-11-01T00:00:00.000Z"),  
  12.     billing_addr: {  
  13.       level: 5,  
  14.       addr1: "123 ABC Street",  
  15.       city: "Some City"  
  16.     }  
  17.   },{  
  18.      level: 3,  
  19.     type: "yy",  
  20.     num: 000000000000,  
  21.     exp_date: ISODate("2015-11-01T00:00:00.000Z"),  
  22.     billing_addr: {  
  23.       level: 1,  
  24.       addr1: "123 ABC Street",  
  25.       city: "Some City"  
  26.     }  
  27. }]  
  28. })  
  29.    
  30. db.redact.aggregate(  
  31.   [  
  32.     { $match: { status: "A" } },  
  33.     {  
  34.       $redact: {  
  35.         $cond: {  
  36.           if: { $eq: [ "$level", 1] },  
  37.           then: "$$DESCEND",  
  38.           else: "$$PRUNE"  
  39.         }  
  40.       }  
  41.     }  
  42.   ]  
  43. );  
  44.   
  45. {  
  46.   "_id" : 1,  
  47.   "level" : 1,  
  48.   "status" : "A",  
  49.   "acct_id" : "xyz123",  
  50.   "cc" : [  
  51.            { "level" : 1,  
  52.      "type" : "yy",  
  53.  "num" : 0,  
  54.  "exp_date" : ISODate("2015-11-01T00:00:00Z")  
  55. }  
  56.    ]  
  57.  }  



 

    二、$$PRUNE:不包含當前文檔或者內嵌文檔級別的全部字段,不會繼續檢測此級別的其餘字段,即便這些字段的內嵌文檔持有相同的訪問級別。連等級的字段都不顯示,也不會去掃描等級字段包含下級。

 

 

[sql]  view plain  copy
 
  1. db.redact.insert(  
  2.   {  
  3.   _id: 1,  
  4.   level: 1,  
  5.   status: "A",  
  6.   acct_id: "xyz123",  
  7.   cc: {  
  8.     level: 3,  
  9.     type: "yy",  
  10.     num: 000000000000,  
  11.     exp_date: ISODate("2015-11-01T00:00:00.000Z"),  
  12.     billing_addr: {  
  13.       level: 1,  
  14.       addr1: "123 ABC Street",  
  15.       city: "Some City"  
  16.     }  
  17. }  
  18.  }  
  19. )  
  20. db.redact.aggregate(  
  21.   [  
  22.     { $match: { status: "A" } },  
  23.     {  
  24.       $redact: {  
  25.         $cond: {  
  26.           if: { $eq: [ "$level", 3] },  
  27.           then: "$$PRUNE",  
  28.           else: "$$DESCEND"  
  29.         }  
  30.       }  
  31.     }  
  32.   ]  
  33. );  
  34. "_id" : 1, "level" : 1, "status" : "A", "acct_id" : "xyz123" }  

 

 

    三、$$KEEP:返回包含當前文檔或內嵌文檔級別的全部字段,再也不繼續檢測此級別的其餘字段,即便這些字段的內嵌文檔中持有不一樣的訪問級別。

 

[sql]  view plain  copy
 
  1. db.redact.insert(  
  2.   {  
  3.   _id: 1,  
  4.   level: 1,  
  5.   status: "A",  
  6.   acct_id: "xyz123",  
  7.   cc: {  
  8.     level: 2,  
  9.     type: "yy",  
  10.     num: 000000000000,  
  11.     exp_date: ISODate("2015-11-01T00:00:00.000Z"),  
  12.     billing_addr: {  
  13.       level:3,  
  14.       addr1: "123 ABC Street",  
  15.       city: "Some City"  
  16.     }  
  17. }  
  18.  }  
  19. )  
  20.   
  21. db.redact.aggregate(  
  22.   [  
  23.     { $match: { status: "A" } },  
  24.     {  
  25.       $redact: {  
  26.         $cond: {  
  27.           if: { $eq: [ "$level", 1] },  
  28.           then: "$$KEEP",  
  29.           else: "$$PRUNE"  
  30.         }  
  31.       }  
  32.     }  
  33.   ]  
  34. );  
  35.    
  36. "_id" : 1, "level" : 1, "status" : "A", "acct_id" : "xyz123", "cc" : { "level" : 2, "type" : "yy", "num" : 0, "exp_date" : ISODate("2015-11-01T00:00:00Z"), "billing_addr" : { "level" : 3, "addr1" : "123 ABC Street", "city" : "Some City" } } }  

 

 

【 options參數】

    explain:返回指定aggregate各個階段管道的執行計劃信息。

    他操做返回一個遊標,包含aggregate執行計劃詳細信息。

 

[sql]  view plain  copy
 
  1. db.items.aggregate([{$group:{_id:"$pnumber",total:{$sum:"$quantity"}}},{$group:{_id:null,max:{$max:"$total"}}}],{explain:true})  
  2.   
  3.   
  4.     "stages" : [  
  5.             {  
  6.                     "$cursor" : {  
  7.                             "query" : {  
  8.   
  9.                             },  
  10.                             "fields" : {  
  11.                                     "pnumber" : 1,  
  12.                                     "quantity" : 1,  
  13.                                     "_id" : 0  
  14.                             },  
  15.                             "plan" : {  
  16.                                     "cursor" : "BasicCursor",  
  17.                                     "isMultiKey" : false,  
  18.                                     "scanAndOrder" : false,  
  19.                                     "allPlans" : [  
  20.                                             {  
  21.                                                     "cursor" : "BasicCursor"  
  22.   
  23.                                                     "isMultiKey" : false,  
  24.                                                     "scanAndOrder" : false  
  25.                                             }  
  26.                                     ]  
  27.                             }  
  28.                     }  
  29.             },  
  30.             {  
  31.                     "$group" : {  
  32.                             "_id" : "$pnumber",  
  33.                             "total" : {  
  34.                                     "$sum" : "$quantity"  
  35.                             }  
  36.                     }  
  37.             },  
  38.             {  
  39.                     "$group" : {  
  40.                             "_id" : {  
  41.                                     "$const" : null  
  42.                             },  
  43.                             "max" : {  
  44.                                     "$max" : "$total"  
  45.                             }  
  46.                     }  
  47.             }  
  48.     ],  
  49.     "ok" : 1  

 

 

      allowDiskUse:每一個階段管道限制爲100MB的內存,若是大於100MB的數據能夠先寫入臨時文件。設置爲true時,aggregate操做可時能夠先將數據寫入對應數據目錄的子目錄中

            的惟一併以_tmp結尾的文檔中。

 

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

                    語法cursor: { batchSize: <int> }

                             var cursor=db.items.aggregate([{$group:{_id:"$pnumber",total:{$sum:"$quantity"}}},{ $limit: 2 }],{cursor: { batchSize: 1 }})

                   版本2.6以後:DB.collect.aggregate()方法返回一個指針,能夠返回任何結果集的大小。沒有指針時返回全部的結果在一個單一的集合,並將結果集限制爲16字節大小的

                  mongodb shell  設置遊標大小cursor.batchSize(size) 一次返回多少條,遊標提供了不少方法:

 

              cursor.hasNext()

              cursor.next()

              cursor.toArray()

              cursor.forEach()

              cursor.map()

              cursor.objsLeftInBatch()

              cursor.itcount()

              cursor.pretty()

     

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

相關文章
相關標籤/搜索