四,聚合操作

一,聚合操作

将数据经过计算,映射,汇总等操作后返回最终处理数据。
mongo有三种方式进行聚合操作:
1,Aggregation Pipeline
2,Map-Reduce
3,Single Purpose Aggregation Operations

先下载一份测试数据,https://gitee.com/geektime-geekbang/geektime-mongodb-course/raw/master/aggregation/dump.tar.gz
解压后执行mongorestore(新版mongo需要自行下载mongo tools),可以得到一个mock数据

image.png

看下order数据结构

db.orders.findOne({})
{
        "_id" : ObjectId("5dbe7a545368f69de2b4d36e"),
        "street" : "493 Hilll Curve",
        "city" : "Champlinberg",
        "state" : "Texas",
        "country" : "Malaysia",
        "zip" : "24344-1715",
        "phone" : "425.956.7743 x4621",
        "name" : "Destinee Schneider",
        "userId" : 3573,
        "orderDate" : ISODate("2019-03-26T03:20:08.805Z"),
        "status" : "created",
        "shippingFee" : NumberDecimal("8.00"),
        "orderLines" : [
                {
                        "product" : "Refined Fresh Tuna",
                        "sku" : "2057",
                        "qty" : 25,
                        "price" : NumberDecimal("56.00"),
                        "cost" : NumberDecimal("46.48")
                },
                {
                        "product" : "Refined Concrete Ball",
                        "sku" : "1738",
                        "qty" : 61,
                        "price" : NumberDecimal("47.00"),
                        "cost" : NumberDecimal("47")
                },
                {
                        "product" : "Rustic Granite Towels",
                        "sku" : "500",
                        "qty" : 62,
                        "price" : NumberDecimal("74.00"),
                        "cost" : NumberDecimal("62.16")
                },
                {
                        "product" : "Refined Rubber Salad",
                        "sku" : "1400",
                        "qty" : 73,
                        "price" : NumberDecimal("93.00"),
                        "cost" : NumberDecimal("87.42")
                },
                {
                        "product" : "Intelligent Wooden Towels",
                        "sku" : "5674",
                        "qty" : 72,
                        "price" : NumberDecimal("84.00"),
                        "cost" : NumberDecimal("68.88")
                },
                {
                        "product" : "Refined Steel Bacon",
                        "sku" : "5009",
                        "qty" : 8,
                        "price" : NumberDecimal("53.00"),
                        "cost" : NumberDecimal("50.35")
                }
        ],
        "total" : NumberDecimal("407")
}

二,Aggregation Pipeline

db.collection.aggregate( [ { <stage> }, ... ] )

aggregate需要传入stage的数组作为管道,数据从上一个stage流出,经过当前stage处理,然后传递给下一个stage进行处理,当所有stage处理完之后得到最终数据。

2.1$match操作

通过标准的mongo查询语句过滤文档流,例如:

//id匹配
db.orders.aggregate([{"$match":{"_id":ObjectId("5dbe7a545368f69de2b4d36e")}}])

//total>0的
db.orders.aggregate([{"$match":{"total":{"$gt":NumberDecimal("0")}}}])

//要么userId=3573,要么country为United States of America
db.orders.aggregate(
    {"$match":{"$or":[
        {"userId":3573},
        {"country":"United States of America"}
    ]}}
)

2.2$project操作

投影,用来决定文档字段是否要返回

//只返回userId和country
db.orders.aggregate(
    [
        {"$match":{"$or":[
            {"userId":3573},
            {"country":"United States of America"}
        ]}},
        {"$project":{"userId":1,"country":1,"_id":0}}
    ]
)

2.3$group操作

{
  $group:
    {
      _id: <expression>, // Group By Expression
      <field1>: { <accumulator1> : <expression1> },
      ...
    }
 }

group操作会根据_id字段的表达式来进行分组

//根据userId汇总,查看每个用户产生的订单数
db.orders.aggregate(
    [{
        "$group":{
            "_id":"$userId",
            "count":{$sum:1}
        }
    }]
)

2.4$limit, $sort,$skip

db.orders.aggregate(
    [
        {
            "$group":{
                "_id":"$userId",
                "count":{$sum:1}
            }
        },
        {"$sort":{"count":1}},
        {"$skip":15},
        {"$limit":10}
    ]
)

2.5$unwind

用于展开文档的数组,path的值代表要展开文档的哪个字段,includeArrayIndex的值代表处理之后的文档用includeArrayIndex的值作为字段保存数组位置,preserveNullAndEmptyArrays为true时,空数组不会填充数组字段

{
  $unwind:
    {
      path: <field path>,
      includeArrayIndex: <string>,
      preserveNullAndEmptyArrays: <boolean>
    }
}
汇总_id为5dbe7a545368f69de2b4d36e的订单总花费
db.orders.aggregate(
    [
        {"$match":{"_id":ObjectId("5dbe7a545368f69de2b4d36e")}},
        {"$unwind":{"path":"$orderLines"}},
        {"$group":{"_id":"_id","totalCost":{"$sum":"$orderLines.cost"},"userId":{"$first":"$userId"}}}
    ]
)

2.6$lookup

左外连接

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}
//往users表插入一条数据
db.users.insertOne({"userId":3573,"userName":"alex"})

//查出这个订单的总花费及user
db.orders.aggregate(
    [
        {"$match":{"_id":ObjectId("5dbe7a545368f69de2b4d36e")}},
        {"$unwind":{"path":"$orderLines"}},
        {"$group":{"_id":"_id","totalCost":{"$sum":"$orderLines.cost"},"userId":{"$first":"$userId"}}},
        {"$lookup":{
            "from":"users",
            "localField":"userId",
            "foreignField":"userId",
            "as":"user"
        }}
    ]
)

2.6$bucket

分桶

{
  $bucket: {
      groupBy: <expression>,
      boundaries: [ <lowerbound1>, <lowerbound2>, ... ],
      default: <literal>,
      output: {
         <output1>: { <$accumulator expression> },
         ...
         <outputN>: { <$accumulator expression> }
      }
   }
}
//将3月,4月的订单分成2个通,并统计月份订单数
db.orders.aggregate(
    [
        {"$bucket":{
            "groupBy":"$orderDate",
            "boundaries":[ISODate("2019-03-01T00:00:00.000Z"),ISODate("2019-04-01T00:00:00.000Z"),
                          ISODate("2019-05-01T00:00:00.000Z")],
            "default":"other",
            "output":{
                "count":{"$sum":1}
            }
        }}
    ]
)

//返回
{ "_id" : "other", "count" : 79716 }
{ "_id" : ISODate("2019-03-01T00:00:00Z"), "count" : 10201 }
{ "_id" : ISODate("2019-04-01T00:00:00Z"), "count" : 10083 }

2.7 $facet

使用$facet,可以在一个聚合中使用多组聚合

{ $facet:
   {
      <outputField1>: [ <stage1>, <stage2>, ... ],
      <outputField2>: [ <stage1>, <stage2>, ... ],
      ...

   }
}
//userCount字段里面包含所有userId的订单数,orderCount计算订单总数
db.orders.aggregate(
    [{
        "$facet":{
            "userCount":[{"$group":{
                "_id":"$userId",
                "count":{"$sum":1}
            }}],
            "orderCount":[{"$group":{
                "_id":null,
                "count":{"$sum":1}
            }}]
        }
    }]
)

三,compass

compass是mongo官方出的mongodb管理工具,提供了可视化界面,可以直观的查看聚合每个stage的输出结果


image.png

还可以直接分析查询语句


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容