【mongo 系列】mongodb 学习五,聚合操作实操

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: 在 users 里面准备一组数据,包含 item ,qty,status,tags 和 size 字段,其中 size 是内嵌文档,size 里面又包含了 h,w,uom 字段

清空集合中的文档

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 字段的值

结果如下:

image.png

我们插入的文档中,没有自己指定 _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 字段展示出来

javascript

复制代码

> 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") }

uantity" : 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 的条目

上述说的临时表,其实我们此处用到的是聚合管道,例如这样


image.png

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

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

image.png

例如下面这个例子,我们就可以 $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 } }
 ])

image.png

打开 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"
       }
  }
])

image.png

分页

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

查询 users 表中数据,可以看出 tags 还是一个数组

shell

复制代码

db.users.find().pretty()

image.png

使用 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" }
...

image.png

开始我们的实践

  • 我们将 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 }

不加分页的话,我们可以看到结果是这样子的

image.png

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

image.png

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

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

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
7天前
|
存储 JSON NoSQL
学习 MongoDB:打开强大的数据库技术大门
MongoDB 是一个基于分布式文件存储的文档数据库,由 C++ 编写,旨在为 Web 应用提供可扩展的高性能数据存储解决方案。它与 MySQL 类似,但使用文档结构而非表结构。核心概念包括:数据库(Database)、集合(Collection)、文档(Document)和字段(Field)。MongoDB 使用 BSON 格式存储数据,支持多种数据类型,如字符串、整数、数组等,并通过二进制编码实现高效存储和传输。BSON 文档结构类似 JSON,但更紧凑,适合网络传输。
36 15
|
2月前
|
SQL NoSQL Unix
MongoDB 聚合
10月更文挑战第17天
28 4
|
3月前
|
SQL NoSQL Unix
MongoDB聚合操作总结
这篇文章总结了MongoDB中聚合操作的作用、方法、常见聚合表达式以及聚合管道的概念和常用操作符,以及SQL与MongoDB聚合操作的对应关系。
59 2
MongoDB聚合操作总结
|
2月前
|
SQL NoSQL 数据处理
深入探索MongoDB的聚合操作
【10月更文挑战第13天】
47 0
|
3月前
|
NoSQL MongoDB 数据库
python3操作MongoDB的crud以及聚合案例,代码可直接运行(python经典编程案例)
这篇文章提供了使用Python操作MongoDB数据库进行CRUD(创建、读取、更新、删除)操作的详细代码示例,以及如何执行聚合查询的案例。
41 6
|
4月前
|
持续交付 jenkins Devops
WPF与DevOps的完美邂逅:从Jenkins配置到自动化部署,全流程解析持续集成与持续交付的最佳实践
【8月更文挑战第31天】WPF与DevOps的结合开启了软件生命周期管理的新篇章。通过Jenkins等CI/CD工具,实现从代码提交到自动构建、测试及部署的全流程自动化。本文详细介绍了如何配置Jenkins来管理WPF项目的构建任务,确保每次代码提交都能触发自动化流程,提升开发效率和代码质量。这一方法不仅简化了开发流程,还加强了团队协作,是WPF开发者拥抱DevOps文化的理想指南。
101 1
|
3月前
|
SQL NoSQL JavaScript
04 MongoDB各种查询操作 以及聚合操作总结
文章全面总结了MongoDB中的查询操作及聚合操作,包括基本查询、条件筛选、排序以及聚合管道的使用方法和实例。
103 0
|
4月前
|
存储 NoSQL JavaScript
MongoDB存储过程实战:聚合框架、脚本、最佳实践,一文全掌握!
【8月更文挑战第24天】MongoDB是一款备受欢迎的文档型NoSQL数据库,以灵活的数据模型和强大功能著称。尽管其存储过程支持不如传统关系型数据库,本文深入探讨了MongoDB在此方面的最佳实践。包括利用聚合框架处理复杂业务逻辑、封装业务逻辑提高复用性、运用JavaScript脚本实现类似存储过程的功能以及考虑集成其他工具提升数据处理能力。通过示例代码展示如何创建订单处理集合并定义验证规则,虽未直接实现存储过程,但有效地演示了如何借助JavaScript脚本处理业务逻辑,为开发者提供更多实用指导。
92 2
|
4月前
|
持续交付 jenkins C#
“WPF与DevOps深度融合:从Jenkins配置到自动化部署全流程解析,助你实现持续集成与持续交付的无缝衔接”
【8月更文挑战第31天】本文详细介绍如何在Windows Presentation Foundation(WPF)项目中应用DevOps实践,实现自动化部署与持续集成。通过具体代码示例和步骤指导,介绍选择Jenkins作为CI/CD工具,结合Git进行源码管理,配置构建任务、触发器、环境、构建步骤、测试及部署等环节,显著提升开发效率和代码质量。
96 0
|
4月前
|
测试技术 C# 开发者
“代码守护者:详解WPF开发中的单元测试策略与实践——从选择测试框架到编写模拟对象,全方位保障你的应用程序质量”
【8月更文挑战第31天】单元测试是确保软件质量的关键实践,尤其在复杂的WPF应用中更为重要。通过为每个小模块编写独立测试用例,可以验证代码的功能正确性并在早期发现错误。本文将介绍如何在WPF项目中引入单元测试,并通过具体示例演示其实施过程。首先选择合适的测试框架如NUnit或xUnit.net,并利用Moq模拟框架隔离外部依赖。接着,通过一个简单的WPF应用程序示例,展示如何模拟`IUserRepository`接口并验证`MainViewModel`加载用户数据的正确性。这有助于确保代码质量和未来的重构与扩展。
117 0