【Mongodb-04】Mongodb聚合管道操作基本功能

Mongodb系列整体栏目


内容 链接地址
【一】Mongodb亿级数据性能测试和压测 https://zhenghuisheng.blog.csdn.net/article/details/139505973
【二】springboot整合Mongodb(详解) https://zhenghuisheng.blog.csdn.net/article/details/139704356
【三】亿级数据从mysql迁移到mongodb辛酸历程 https://zhenghuisheng.blog.csdn.net/article/details/140302930
【四】Mongodb聚合管道操作基本功能 https://zhenghuisheng.blog.csdn.net/article/details/140402214

mongodb聚合操作基本功能

一,mongodb聚合操作基本功能

在mysql中,允许一些匹配,分组,排序,分页,求和以及多表之间的连表操作,在mongodb中,这些操作也是可以实现的,内部就是通过聚合操作实现。在mongodb中,聚合操作主要有三大类操作,分别是: 单一作用聚合、聚合管道、MapReduce ,在mongodb5.0开始,mapReduce被废弃,因为能通过MapReduce实现的功能,都可以通过这个聚合管道实现,并且聚合管道的效率还远高于MapReduce,因此本文主要是了解单一管道和聚合管道。

在了解聚合管道之前,可以先通过官方文档的教程以及如何使用:mongodb聚合管道的使用

管道的模型如下,其设计理念就是通过流水线的模式,每一个步骤处理一个操作,,每一个步骤处理完的结果传给下一个步骤,最后将结果给返回

在官网中,给了一个 sql-聚合 的映射图标,通过sql中的语法和者术语来更加清楚的了解 聚合操作的概念和使用

1,单一聚合

假设此时有一张orders的订单集合,类似于mysql中的orders订单表,在mongodb中对这些单一聚合提供了简单访问,如以下获取文档的总个数,去重等,纯原生的方式实现数据的获取,在性能上没有聚合管道的效率高

sql 复制代码
db.orders.estimatedDocumentCount(),  
db.orders.countDocument(),
db.orders.distinct()

2,聚合管道 + 聚合操作符

在官方文档中给了很多的聚合阶段的方式,接下来主要是结合官方提供的映射图标的文档,来理解mongodb中常用的一些聚合方式。在讲解聚合管道之前,先提供一些mongodb内部的一些操作符,类似于mysql的一些聚合函数,如比较操作符,日期操作符,算术表达式操作符等等。通过管道再结合对应的操作符,来实现对文档的查询,从而使得mongodb可以和关系型数据库一样的操作流程,并通过json的数据格式,让整个查询更加的灵活

2.1,Count

在mysql中,计算一张表的数据有多少条,可以直接使用 count(*) 命令

sql 复制代码
select count(*) as count from orders

在mongodb中聚合管道的操作命令如下,_id为空就是表示不根据某个字段进行分组,即所有文档在同一个组里面,里面的 {$sum:1} 表示的是一个累计计数器的效果,表示查询出文档的条数,这里的每有一条文档那么这个计数器就会加1。比如查询出来是100条,那么count的值就是100,和上面mysql的as count的操作是一样的

sql 复制代码
db.orders.aggregate([
    {
        $group:{
    		_id:null,
            count:{$sum:1}
        }
    }
])
2.2,SUM

对于mysql的求和统计操作,一般都是通过sum聚合函数实现,如查看订单表中的总价格的和

sql 复制代码
SELECT SUM(price) AS total FROM orders

在moogodb中的实现也可以如下,管道结合 $sum 操作符实现,这里和上面的一样,只是在sum求和时携带的参数不一样,上面的值设置的是1,下面的值设置的是每条文档中具体的某个字段的值,因此可以统计出所有订单的汇总价格

sql 复制代码
db.orders.aggregate([
 	{
    	$group:{
    		_id:null,
    		total:{$sum:"$price"}
    	}
    }   
])
2.3,Group By

在上面虽然也用了group by,但是在 _id 那个字段并没有设置对应文档中的字段,在实际情况看到会有根据某个字段先分组早再求和的情况,如根据用户id进行分组再进行求和

sql 复制代码
select user_id,sum(price) as total from orders group by user_id

结合上面的1,2两点,可以得知上面的mysql的效果实现如下,让_id 字段设置成要分组的字段即可

sql 复制代码
db.orders.aggregate([
    {
        $group:{
            _id:"$user_id",
            total:{$sum:"price"}
        }
    }
])
2.4,分组+排序

在订单表中,要查出每一个用户下单花费的总金额,并通过从大到小的方式进行排序

sql 复制代码
select user_id,sum(price) as total from orders group by user_id order by total

mongodb的实现如下,这里需要两个聚合操作,将上一个聚合操作的结果给下一个聚合操作,在sort聚合函数中,1表示升序,2表示降序

sql 复制代码
db.orders.aggregate([
    {
    	$group:{
    		_id:"$user_id",
    		total:{$sum,"$price"}
    	}
    },
    {
    	$sort:{total:1}
    }
])
2.5,多字段分组+时间格式化

就是需要通过user_id+每天的进行分组,统计每个用户每天消费了多少

sql 复制代码
select 
    user_id,date_format(created_at,'%y-%m-%d') as order_date,sum(price)
from 
    orders
group by 
    user_id,date_formate(created_at,'%-y-%m-%d')    

mongodb的聚合操作如下,如果有多个分组字段,那么就在 _id 对象中设置多个参数,并且按照从上往下的顺序设置,使用日期表达式操作符将时间进行格式化

sql 复制代码
db.orders.aggregate([
    {
        $group:{
            _id:{
    			user_id:"$user_id",
               	order_date: { 
    				$dateToString: {
              			format: "%Y-%m-%d",
              			date: "$created_at"
           			}
    			}
    		},
    		total:{$sum:"price"}
        }
    }
])
2.6,Group By + Having

比如返回用户消费大于1000的用户信息,mysql的语法如下

sql 复制代码
select user_id,sum(price) as total from orders group by user_id having total > 1000

使用mongodb聚合操作的语法如下,通过match聚合命令匹配实现,并且结合gt的比较运算符进行数据的过滤,最后将对应的文档返回

sql 复制代码
db.orders.aggregate([
    {
        $group:{
            _id:"user_id",
          	total:{$sum:"$price"}
        }
    },
    {
        $match:{
            total:{count:{$gt:1000}}
        }
    }
])
2.7,Where

比如返回订单表中商品id为10001的商品,并且差每个人花费的总金额,按降序排序

sql 复制代码
select user_id,sum(price)as total from orders where product_id = 10001 group by user_id order by total

使用mongodb的聚合操作的语法如下,期语法罗伊和mysql的一样,先过滤,再分组,再排序

sql 复制代码
db.orders.aggregate([
    {
        $match:{
            product_id:10001
        },
        $group:{
    		_id:"$user_id",
    		total:{$sum:"$price"}
    	},
        {
    		$sort:{total:1}
    	}
    }
])
2.8,子查询

比如查询一个简单的,查询订单表中总共有多少用户下单(用户去重)

sql 复制代码
SELECT
    COUNT(*)
FROM 
    (
        SELECT user_id FROM orders GROUP BY user_id
    ) t

通过mongodb举个查询的语法如下,只需要通过两个group就能实现,在第二个group中将id值设置为null

sql 复制代码
db.orders.aggregate([
    {
        $group:{
    		_id:"$user_id",
    		total:{$sum:"$price"}
    	}
    },
    {
         $group:{
    		_id:null,
    		count: { $sum: 1 }
    	}
    }
])
2.9,分页

在mysql中,分页查询直接通过limit即可,如查询订单表中商品id为10001的花费最多的钱10名的用户

sql 复制代码
select
	user_id,sum(price) as total
from orders
where product_id = 10001
group by 
	user_id 
order by total desc limit 10

在mongodb的聚合管道中,其实现方式也比较简单,只需要多聚合几个条件即可,先执行where条件的管道,然后分组管道,排序管道,分页管道,最终将数据返回,通过数据库层面实现数据的过滤和查询,从而减少网络io传输

sql 复制代码
db.orders.aggregate([
    {
        $match:{product_id,10001},
    },
    {
        $group:{
            _id:"$user_id",
            total:{$sum:"$price"}
        }
    },
    {
        $sort:{total:-1}
    },
    {
        $limit:10
    }
])
2.10,连表查询

在mysql中,查询商品id为10001的花费最多的钱10名的用户的用户名

sql 复制代码
select
	u.name,order.user_id,sum(order.price) as total
from orders order
left join user u on u.id = order.user_id
where order.product_id = 10001
group by 
	order.user_id 
order by total desc limit 10

通过mongodb聚合渠道的方式如下,通过$lookup 关键字来实现联表,from表示需要连接的那张表,localField表示主表中需要关联的字段,foreignField表示需要关联的那张表中要关联的字段,最活通过一个 unwind 关键字将数组的值展开,让每一个订单表中查询出来的数据对应一个用户信息,最后再通过增加一个分组,排序和分页的聚合操作

sql 复制代码
db.orders.aggregate([
    { $match: { product_id: 10001 } },  // 筛选出 product_id 为 10001 的订单
    { $lookup: {                       // 左外连接用户表
        from: "users",                 // 'users' 集合(假设用户集合名称为 users)
        localField: "user_id",         // orders 集合中连接键
        foreignField: "id",            // users 集合中连接键
        as: "user_info"                // 查询结果的新字段名
    }},
    { $unwind: "$user_info" },         // 展开连接结果,使每个订单对应一个用户信息
    { $group: {                        // 分组操作
        _id: "$user_id",               // 根据 user_id 分组
        name: { $first: "$user_info.name" },  // 从用户信息中获取名字
        total: { $sum: "$price" }              // 计算总金额
    }},
    { $sort: { total: -1 } },          // 按 total 降序排序
    { $limit: 10 }                     // 限制结果为前 10 条
]);
相关推荐
skywalk816323 分钟前
学习关系型数据库:在MAC下编译安装firebird
数据库·学习·macos
我就说好玩33 分钟前
创建SQLiteOpenHelper 类来创建和管理SQLite数据库
数据库·sqlite·数据库开发
阿东日志1 小时前
Redis高级---面试总结之内存过期策略及其淘汰策略
数据库·redis·缓存·面试
想寻1 小时前
关于redis存储数据类型选择
数据库·redis·缓存
J老熊1 小时前
Redis持久化方式、常见问题及解决方案
java·数据库·redis·面试·系统架构
L黎蕊1 小时前
sqli-labs靶场通关攻略(41-50)
数据库·sql
无极低码1 小时前
java一键生成数据库说明文档html格式
java·数据库·html
刘大帅ps1 小时前
MySQL 集群技术全攻略:从搭建到优化(上)
linux·运维·服务器·网络·数据库·sql·mysql
m0_635502202 小时前
MySQL数据库事务的学习(有业务场景案例)
数据库·学习·mysql
不断前进的皮卡丘2 小时前
MySQL日志
数据库·mysql·adb·日志