乐趣区

关于mongodb:mongo-聚合操作

清空集合中的文档

db.users.drop()

数据筹备

筹备 users 表数据

在 users 外面筹备一组数据,蕴含 item,qty,status,tags 和 size 字段,其中 size 是内嵌文档,size 外面又蕴含了 h,w,uom 字段

db.users.insertMany([{ item: "canvas", qty: 100, size: { h: 28, w: 35.5, uom: "cm"}, status: "A" },
   {item: "journal", qty: 25, tags: ["blank", "red", "small"], size: {h: 14, w: 21, uom: "cm"}, status: "A" },
   {item: "notebook", qty: 50, tags: ["gray", "yellow", "green"], size: {h: 8.5, w: 11, uom: "in"}, status: "A" },
   {item: "paper", qty: 100, tags: ["gray", "yellow", "green"], size: {h: 8.5, w: 11, uom: "in"}, status: "D" },
   {item: "planner", qty: 75, tags: ["gray", "yellow", "green"], size: {h: 22.85, w: 30, uom: "cm"}, status: "D" },
   {item: "postcard", qty: 45, tags: ["gray", "yellow", "green"], size: {h: 10, w: 15.25, uom: "cm"}, status: "A" },
   {item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm"}, status: "A" },
   {item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm"}, status: "A" },
   {item: "mat", qty: 85, tags: ["gray", "yellow", "green"], size: {h: 27.9, w: 35.5, uom: "cm"}, status: "A" },
   {item: "mousepad", qty: 25, tags: ["gel", "blue", "big"], size: {h: 19, w: 22.85, uom: "cm"}, status: "A" },
   {item: "mobile", qty: 250, tags: ["red", "big"], status: "A" },
   {item: "map", qty: 250, length: [129, 500, 1000], status: "A" },
   {item: "apple", qty: 250, length: [29, 50, 90], status: "A" },
   {item: "banana", qty: 150, status: "A"},
   {item: "orange", qty: 90, size: { h: 19, w: 22.85, uom: "cm"}, status: "A" },
   {"_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120},
   {"_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80},
   {"_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60},
   {"_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70},
   {"_id" : 5, "sku": null, description: "Incomplete"},
   {"_id" : 6}
]);

这里能够看到筹备的数据,最初 6 个文档,咱们本人指定了 _id 字段的值

后果如下:

咱们插入的文档中,没有本人指定 _id 字段,则 mongodb 会为咱们生成这个主键,若咱们本人指定了这个字段,那么就会依照咱们自定义的形式来

筹备 sales 字段

在文档中退出日期字段,整型字段,小数字段,别离应用 mongodb 的函数

  • ISODate
  • NumberInt
  • NumberDecimal
db.sales.insertMany([{ "_id" : 1, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("2"), "date" : ISODate("2014-03-01T08:00:00Z") },
  {"_id" : 2, "item" : "jkl", "price" : NumberDecimal("20"), "quantity" : NumberInt("1"), "date" : ISODate("2014-03-01T09:00:00Z") },
  {"_id" : 3, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt("10"), "date" : ISODate("2014-03-15T09:00:00Z") },
  {"_id" : 4, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" :  NumberInt("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },
  {"_id" : 5, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") },
  {"_id" : 6, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("5") , "date" : ISODate("2015-06-04T05:08:13Z") },
  {"_id" : 7, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("10") , "date" : ISODate("2015-09-10T08:43:00Z") },
  {"_id" : 8, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("5") , "date" : ISODate("2016-02-06T20:20:13Z") },
])

orders 表数据筹备

db.orders.insert(
[
  {
    "_id": 1,
    "item": "almonds",
    "price": 12,
    "quantity": 2
  },
  {
    "_id": 2,
    "item": "pecans",
    "price": 20,
    "quantity": 1
  },
  {"_id": 3}
]
)

数据聚合操作

计算汇合的文档数

通过 _id 字段分组,此处分组条件是 _id 字段为空,示意筛选所有的文档,$sum:1 示意有 1 个文档就加 1,最初以 salesCount 字段展现进去

> db.sales.aggregate([{$group: {_id: null, salesCount: { $sum: 1} }}] )
{"_id" : null, "salesCount" : 8}

其余两个表格做法统一

> db.users.aggregate( 
[
  {
    "$group": {
      "_id": null,
      "usersCount": {"$sum": 1}
    }
  }
]
)
{"_id" : null, "usersCount" : 21}
> db.orders.aggregate( 
[
  {
    "$group": {
      "_id": null,
      "ordersCount": {"$sum": 1}
    }
  }
]
)
{"_id" : null, "ordersCount" : 3}
>

通过以上形式,咱们就能很快的得出每一个汇合的文档个数,当然咱们还能够加上别的筛选条件来聚合数据

例如咱们能够这样,先筛选出 price 字段大于 5 的文档数,才统计文档的个数,解决思路如下:

分成 2 步进行

  • 先找到 price 大于 5 的文档列表,作为下一个步骤的管道输出
  • 拿到上述输出后,计算文档个数
> db.sales.aggregate([{$match:{price:{$gt:NumberDecimal("5")}}}])
{"_id" : 1, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : 2, "date" : ISODate("2014-03-01T08:00:00Z") }
{"_id" : 2, "item" : "jkl", "price" : NumberDecimal("20"), "quantity" : 1, "date" : ISODate("2014-03-01T09:00:00Z") }
{"_id" : 5, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : 10, "date" : ISODate("2014-04-04T21:23:13.331Z") }
{"_id" : 6, "item" : "def", "price" : NumberDecimal("7.5"), "quantity" : 5, "date" : ISODate("2015-06-04T05:08:13Z") }
{"_id" : 7, "item" : "def", "price" : NumberDecimal("7.5"), "quantity" : 10, "date" : ISODate("2015-09-10T08:43:00Z") }
{"_id" : 8, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : 5, "date" : ISODate("2016-02-06T20:20:13Z") }

聚合看看数量

> db.sales.aggregate( 
[
  {
    $match:{
      price:{$gt:NumberDecimal("5")
      }
    }
  },
  {
    $group:{
      _id:null,
      count:{$sum:1}
    }
  }
]
)
{"_id" : null, "count" : 6}

果然是 6 个文档,没错

计算 sales 表格 每一个条目标总价,筛选出 大于 100 的

思路如下:

分成 2 步进行

  • 先计算出每一个条目标数量与价格的乘积后果,放到一张长期表中
  • 从长期表中筛选出后果大于 100 的条目

上述说的长期表,其实咱们此处用到的是聚合管道,例如这样

db.sales.aggregate(
  [
    // First Stage
    {
      $group :
        {
          _id : "$item",
          totalSaleAmount: {$sum: { $multiply: [ "$price", "$quantity"] } }
        }
     },
     // Second Stage
     {$match: { "totalSaleAmount": { $gte: 100} }
     }
   ]
 )

看到这里,不要认为咱们只能分成 2 步骤来实现,咱们上一篇文章写到过,这些阶段的关键字都是能够重复使用的,只是某几个非凡的关键字不能重复使用

例如上面这个例子,咱们就能够 $match 屡次,最初计算出一个后果,理论利用中,咱们能够依据咱们的需要来进行分批次解决,怎么不便怎么来

> db.sales.aggregate( 
[
  {
    $match:{
      price:{$gt:NumberDecimal("5")
      }
    }
  },
  {
    $match:{
      price:{$gt:NumberDecimal("10")
      }
    }
  },
  {
    $group:{
      _id:null,
      count:{$sum:1}
    }
  }
]
)
{"_id" : null, "count" : 1}

略微简单点的例子

操作 sales 表

  • 筛选出日期在 2014-01-01 到 2015-01-01 之间的数据
  • 分组,

    • 将_id 赋值为 字符串的日期格局,
    • 将 totalSaleAmount 赋值为 原表 price 和 quantity 的乘积 再将同样日期的乘积后果相加
    • 将 averageQuantity 赋值为 quantity 的平均数
    • count 计算文档个数
  • 排序,-1 是倒序,1 是正序
  • project 管制显示的字段
db.sales.aggregate([
  {$match : { "date": { $gte: new ISODate("2014-01-01"), $lt: new ISODate("2015-01-01") } }
  },
  {
    $group : {_id : { $dateToString: { format: "%Y-%m-%d", date: "$date"} },
       totalSaleAmount: {$sum: { $multiply: [ "$price", "$quantity"] } },
       averageQuantity: {$avg: "$quantity"},
       count: {$sum: 1}
    }
  },
  {$sort : { totalSaleAmount: -1} },
  // 管制所须要显示的字段名
 {$project : { _id : 1 , totalSaleAmount : 1} }
 ])

关上 project 的正文,咱们就只管制显示 _id 和 totlSaleAmount 字段,后果如下

{"_id" : "2014-04-04", "totalSaleAmount" : NumberDecimal("200") }
{"_id" : "2014-03-15", "totalSaleAmount" : NumberDecimal("50") }
{"_id" : "2014-03-01", "totalSaleAmount" : NumberDecimal("40") }

多表操作

最初来演示一个多表操作的例子

咱们查问 users 和 orders 表,别离关联 orders 的 item 和 users 的 sku 字段,后果放到 users_docs 中

db.orders.aggregate([
   {
     $lookup:
       {
         from: "users",
         localField: "item",
         foreignField: "sku",
         as: "users_docs"
       }
  }
])

分页

咱们先来看看如何将 users 表中的 tags 数组元素都变成对象

查问 users 表中数据,能够看出 tags 还是一个数组

db.users.find().pretty()

应用 unwind 来将元素做成文档,能够看出 tags 不在是数组,而是字符串了

> db.users.aggregate([ { $unwind : "$tags"} ] )
{"_id" : ObjectId("615d049f3ea73badd681950e"), "item" : "journal", "qty" : 25, "tags" : "blank", "size" : {"h" : 14, "w" : 21, "uom" : "cm"}, "status" : "A" }
{"_id" : ObjectId("615d049f3ea73badd681950e"), "item" : "journal", "qty" : 25, "tags" : "red", "size" : {"h" : 14, "w" : 21, "uom" : "cm"}, "status" : "A" }
{"_id" : ObjectId("615d049f3ea73badd681950e"), "item" : "journal", "qty" : 25, "tags" : "small", "size" : {"h" : 14, "w" : 21, "uom" : "cm"}, "status" : "A" }
...

开始咱们的实际

  • 咱们将 users 表中的 tags 数组中的元素,都做成一个对象
  • 分组,依照 tags 来进行分组,_id 赋值为 tags 字段,averageQty 赋值为 qty 字段的依据 tags 的平均数
  • 在倒序排列
  • 显示的时候,跳过后面 2 个,显示前面 2 个
db.users.aggregate( [
   {$unwind: { path: "$tags", preserveNullAndEmptyArrays: true}
   },
   {
     $group:
       {
         _id: "$tags",
         averageQty: {$avg: "$qty"}
       }
   },
   {$sort: { "averageQty": -1} },
   //{$skip: 2},
   //{$limit: 2}
] )
{"_id" : "red", "averageQty" : 137.5}
{"_id" : "green", "averageQty" : 71}

不加分页的话,咱们能够看到后果是这样子的

欢送点赞,关注,珍藏

敌人们,你的反对和激励,是我保持分享,提高质量的能源

好了,本次就到这里

技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。

我是 阿兵云原生,欢送点赞关注珍藏,下次见~

退出移动版