zoukankan      html  css  js  c++  java
  • MongoDB 聚合Group(二)

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

    一、简介:

          上一篇我们对   db.collection.aggregate(pipeline, options)介绍,我们接下来介绍pipeline 参数和options参数的基础认识

      【pipeline 参数】

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

             $project:可以对输入文档进行添加新字段或删除现有的字段,可以自定哪些字段显示与不显示。

             $match :根据条件用于过滤数据,只输出符合条件的文档,如果放在pipeline前面,根据条件过滤数据,传输到下一个阶段管道,可以提高后续的数据处理效率。还可以放在out之前,对结果进行再一次过滤。

             $redact :字段所处的document结构的级别.

             $limit :用来限制MongoDB聚合管道返回的文档数

             $skip :在聚合管道中跳过指定数量的文档,并返回余下的文档。

             $unwind :将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。    

             $sample :随机选择从其输入指定数量的文档。如果是大于或等于5%的collection的文档,$sample进行收集扫描,进行排序,然后选择顶部的文件。因此,$sample 在收集阶段是受排序的内存限制。

                              语法:  { $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

     二、基础的操作

        【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】

           1、我们对上面的统计结果,只显示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】

         1、我们想通过滤订单中,想知道卖出的数量大于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 }  

         2、如果是放在$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:返回包含当前文档或内嵌文档级别的所有字段,不再继续检测此级别的其他字段,即使这些字段的内嵌文档中持有不同的访问级别。

      1、  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.  }  



        2、$$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" }  

        3、$$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绕过文档验证操作过程中。这让您插入不符合验证要求的文档。

  • 相关阅读:
    图片上传-下载-删除等图片管理的若干经验总结3-单一业务场景的完整解决方案
    图片上传-下载-删除等图片管理的若干经验总结2
    HDU 1195 Open the Lock
    HDU 1690 Bus System
    HDU 2647 Reward
    HDU 2680 Choose the best route
    HDU 1596 find the safest road
    POJ 1904 King's Quest
    CDOJ 889 Battle for Silver
    CDOJ 888 Absurdistan Roads
  • 原文地址:https://www.cnblogs.com/yanwei-wang/p/8628431.html
Copyright © 2011-2022 走看看