前言
在MongoDB中,aggregate
是用于进行数据聚合操作的强大工具。它允许你对文档集合进行多个数据处理步骤,从而进行复杂的数据分析和转换。aggregate
的基本思想是将一系列数据处理操作连接在一起,以生成所需的结果。
基本语法
aggregate
方法的基本语法如下:
js
db.collection.aggregate(pipeline, options)
collection
: 你要对其进行数据聚合的集合(数据表)。pipeline
: 一个由多个聚合阶段组成的数组,每个阶段代表一个特定的数据处理操作。options
(可选):一个对象,用于指定其他选项,例如超时、允许磁盘使用等。
聚合管道中的各个阶段是按顺序执行的,每个阶段可以执行不同的操作,如筛选、变换、分组、排序等。
常用的聚合阶段
以下是一些常用的聚合阶段以及它们的作用:
注:示例中的
db.*
是数据的集合(数据表)
1. $match:筛选文档
$match
阶段用于筛选文档,只保留满足指定条件的文档。
js
// 仅选择日期在2023年1月1日之后的销售记录
db.sales.aggregate([
{ $match: { date: { $gte: ISODate("2023-01-01") } } }
])
对比SQL:
js
SELECT * FROM sales WHERE date >= '2023-01-01';
2. $project:重塑文档的结构
$project
阶段用于重塑文档的结构,可以包括字段的重命名、字段的新增、字段的删除等。
js
db.products.aggregate([
{
$project: {
productName: "$name", // 重命名字段
price: 1,
discountPrice: { $multiply: ["$price", 0.9] } // 添加计算字段
}
}
])
对比SQL:
js
SELECT name AS productName, price, price * 0.9 AS discountPrice FROM products;
3. $group:将文档分组
$group
阶段用于将文档分组,并通常与累积操作(如求和、平均值)一起使用。
js
db.sales.aggregate([
{
$group: {
_id: "$product", // 根据产品分组
totalAmount: { $sum: "$amount" } // 计算总销售额
}
}
])
对比SQL:
js
SELECT product, SUM(amount) AS totalAmount FROM sales GROUP BY product;
4. $sort:对文档进行排序
$sort
阶段用于对文档进行排序。
js
db.contacts.aggregate([
{ $sort: { lastName: 1, firstName: 1 } } // 按姓氏和名字升序排序
])
对比SQL:
js
SELECT * FROM contacts ORDER BY lastName, firstName;
5. $limit:限制输出文档的数量
$limit
阶段用于限制输出文档的数量。
js
db.logdata.aggregate([
{ $limit: 10 } // 仅获取前10条日志记录
])
对比SQL:
js
SELECT * FROM logdata LIMIT 10;
6. $skip:跳过前几个文档
$skip
阶段用于跳过输出结果中的前几个文档。
js
db.orders.aggregate([
{ $skip: 5 } // 跳过前5个订单
])
对比SQL:
js
SELECT * FROM orders OFFSET 5;
7. $unwind:展开数组字段
$unwind
阶段用于展开数组字段,将包含数组的文档拆分成多个文档。
js
db.articles.aggregate([
{ $unwind: "$tags" } // 将文章按标签展开为多个文档
])
对比SQL(SQL中不直接等效,需要使用JOIN来处理类似情况):
js
// 假设articles和tags是不同的表
SELECT articles.*, tags.tag
FROM articles
JOIN tags ON articles.id = tags.article_id;
8. ✨ $lookup:执行左连接
$lookup
阶段用于执行左连接,将两个集合的数据合并在一起。
js
db.orders.aggregate([
{
$lookup: {
from: "customers", // 关联的集合
localField: "customerId", // 本集合字段
foreignField: "_id", // 关联集合字段
as: "customerInfo" // 存储结果的字段
}
}
])
对比SQL(SQL的JOIN操作与MongoDB的$lookup类似):
js
SELECT orders.*, customers.*
FROM orders
LEFT JOIN customers ON orders.customerId = customers.id;
这些MongoDB的聚合阶段可以组合使用,以满足特定的数据处理需求。
多个聚合阶段组合使用
假设我们有一个名为 customers
的集合( 客户数据表 )和一个名为 orders
的集合( 订单数据表 ),包含客户订单的信息。
用户信息文档(用户数据)如下:
json
// customers 集合
[
{
"_id": 101,
"name": "John Doe",
"email": "john.doe@example.com"
},
// 其他客户信息...
]
订单文档集合( 订单数据 )如下:
json
// orders 集合
[
{
"_id": 1,
"customerId": 101, // 客户标识字段
"orderDate": ISODate("2023-02-15T08:00:00Z"),
"totalAmount": 500
},
// 其他订单信息
]
现在我想实现的目标是:将订单与客户联系起来,获取每个客户的总订单金额,并按订单金额降序排列。
js
// 定义分页参数
const pageSize = 10; // 每页显示的数量
const currentPage = 1; // 当前页码(从 1 开始)
db.orders.aggregate([
// 阶段 1: 按 customerId 分组订单,计算每个客户的总订单金额
{
$group: {
_id: "$customerId", // 必须
// 自定义字段使用聚合操作符:$sum 计算(订单表的totalAmount字段的)总和
totalOrders: { $sum: "$totalAmount" }
}
},
// 阶段 2: 加入客户信息,将订单信息和客户信息合并
{
$lookup: {
from: "customers", // 客户信息存储在 "customers" 集合中
localField: "customerId", // (当前)通过订单的 "customerId" 字段关联
foreignField: "_id", // (目标)通过客户的 "_id" 字段关联
as: "customerInfo" // 输出到字段重命名
}
},
// 阶段 3: 将分组后端数据重新格式化输出文档,删除不必要的字段
{
$project: {
_id: 0, // 不包括 _id 字段
customerId: "$_id",
totalOrders: 1,
customerInfo: 1
}
},
// 阶段 4: 按总订单金额降序排列
{
$sort: { totalOrders: -1 }
},
// 阶段 5: 添加分页支持 - 跳过 (skip) 某数量的文档
{
$skip: (currentPage - 1) * pageSize
},
// 阶段 6: 添加分页支持 - 限制 (limit) 返回的文档数量
{
$limit: pageSize
},
// 阶段 7: 获取总条数
// 注意这里不是文档所有条数是查询出来多少条(当前页的文档数量)
// 总文档数(表所有数据数量)可以单独使用:db.orders.countDocuments() 来查询
{
$count: "totalItems"
}
])
查询结果如下:
json
{
"currentPage": 1, // 当前页码
"pageSize": 10, // 每页显示的数量
"totalItems": 50, // 总条数
"data": [ // 包含查询结果的数组
[
{
"customerId": 101,
"totalOrders": 2500,
"customerInfo": [
{
"_id": 101,
"name": "John Doe",
"email": "john.doe@example.com"
}
]
},
{
"customerId": 102,
"totalOrders": 1800,
"customerInfo": [
{
"_id": 102,
"name": "Jane Smith",
"email": "jane.smith@example.com"
}
]
},
// 其他客户的信息...
]
]
}
最后
内容有写得不对或者不明白之处,欢迎指正交流学习哦:v:
完 ~~