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 条
]);