【MongoDB】多种聚合操作详解,案例分析
1. 算术表达式操作符 ($abs)
// 插入数据
db.temperatureChange.insertMany([{ _id: 1, startTemp: 50, endTemp: 80 },{ _id: 2, startTemp: 40, endTemp: 40 },{ _id: 3, startTemp: 90, endTemp: 70 },{ _id: 4, startTemp: 60, endTemp: 70 }
]);// 计算温度差的绝对值
db.temperatureChange.aggregate([{$project: {delta: { $abs: { $subtract: ["$startTemp", "$endTemp"] } }}}
]);
2. 数组表达式操作符 ($arrayElemAt)
// 插入数据
db.users.insertMany([{ _id: 1, name: "dave123", favorites: ["chocolate", "cake", "butter", "apples"] },{ _id: 2, name: "li", favorites: ["apples", "pudding", "pie"] },{ _id: 3, name: "ahn", favorites: ["pears", "pecans", "chocolate", "cherries"] },{ _id: 4, name: "ty", favorites: ["ice cream"] }
]);// 获取数组首尾元素
db.users.aggregate([{$project: {name: 1,first: { $arrayElemAt: ["$favorites", 0] },last: { $arrayElemAt: ["$favorites", -1] }}}
]);
3. 按位操作符 ($bitAnd)
// 插入数据
db.switches.insertMany([{ _id: 0, a: NumberInt(0), b: NumberInt(127) },{ _id: 1, a: NumberInt(2), b: NumberInt(3) },{ _id: 2, a: NumberInt(3), b: NumberInt(5) }
]);// 按位与运算
db.switches.aggregate([{$project: {result: { $bitAnd: ["$a", "$b"] }}}
]);
4. 布尔表达式操作符 ($and)
// 插入数据
db.inventory.insertMany([{ _id: 1, item: "abc1", qty: 300 },{ _id: 2, item: "abc2", qty: 200 },{ _id: 3, item: "xyz1", qty: 250 },{ _id: 4, item: "wxz1", qty: 300 },{ _id: 5, item: "wxz2", qty: 180 }
]);// 检查数量范围
db.inventory.aggregate([{$project: {item: 1,qty: 1,result: { $and: [{ $gt: ["$qty", 100] },{ $lt: ["$qty", 250] }]}}}
]);
5. 条件表达式操作符 ($cond)
// 插入数据
db.inventory.insertMany([{ _id: 1, item: "abc1", qty: 300 },{ _id: 2, item: "abc2", qty: 200 },{ _id: 3, item: "xyz1", qty: 260 }
]);// 条件折扣计算
db.inventory.aggregate([{$project: {item: 1,discount: {$cond: {if: { $gte: ["$qty", 260] },then: 30,else: 20}}}}
]);
6. 日期操作符 ($dateAdd)
// 插入数据
db.shipping.insertMany([{ custId: 456, purchaseDate: ISODate("2020-12-31") },{ custId: 457, purchaseDate: ISODate("2021-02-28") },{ custId: 458, purchaseDate: ISODate("2021-02-26") }
]);// 计算预计交付日期(+3天)
db.shipping.aggregate([{$project: {expectedDeliveryDate: {$dateAdd: {startDate: "$purchaseDate",unit: "day",amount: 3}}}}
]);
7. 字符串操作符 ($concat)
// 插入数据
db.inventory.insertMany([{ _id: 1, item: "ABC1", description: "product 1" },{ _id: 2, item: "ABC2", description: "product 2" },{ _id: 3, item: "XYZ1", description: null }
]);// 拼接字符串
db.inventory.aggregate([{$project: {itemDescription: { $concat: ["$item", " - ", "$description"] }}}
]);
8. 累加器操作符 ($addToSet)
// 插入数据
db.sales.insertMany([{ _id: 1, item: "abc", date: ISODate("2014-01-01T08:00:00Z") },{ _id: 2, item: "jkl", date: ISODate("2014-02-03T09:00:00Z") },{ _id: 3, item: "xyz", date: ISODate("2014-02-03T09:05:00Z") },{ _id: 4, item: "abc", date: ISODate("2014-02-15T08:00:00Z") }
]);// 按日期分组统计唯一商品
db.sales.aggregate([{$group: {_id: {day: { $dayOfYear: "$date" },year: { $year: "$date" }},itemsSold: { $addToSet: "$item" }}}
]);
9. 变量操作符 ($let)
// 插入数据
db.sales.insertMany([{ _id: 1, price: 10, tax: 0.50, applyDiscount: true },{ _id: 2, price: 10, tax: 0.25, applyDiscount: false }
]);// 使用变量计算最终价格
db.sales.aggregate([{$project: {finalTotal: {$let: {vars: {total: { $add: ["$price", "$tax"] },discounted: { $cond: { if: "$applyDiscount", then: 0.9, else: 1 } }},in: { $multiply: ["$$total", "$$discounted"] }}}}}
]);
关键说明:
- 字段修正:所有
-id
已修正为_id
- 数据类型:
- 日期使用
ISODate()
- 整数使用
NumberInt()
- 日期使用
- 操作符注意:
$arrayElemAt
:索引从0开始,-1表示最后一个元素$cond
:三元运算符(条件 ? 值1 : 值2)$let
:先定义变量再使用($$变量名
)
- 执行顺序:
1. 创建集合 → 2. 插入数据 → 3. 执行聚合管道
- 版本要求:
- 按位操作符需要 MongoDB 6.3+
- 窗口函数需要 MongoDB 5.0+
建议逐段复制执行,每次执行前确保集合存在且数据格式正确。遇到语法错误时,检查括号闭合和逗号使用。
$mergeObjects 与集合表达式
场景说明
我们有两个集合:orders
(订单)和items
(商品)。我们需要将这两个集合的数据按照商品编号(item)进行关联,得到包含订单信息和商品库存信息的完整文档。
准备数据
// 创建orders集合并插入数据
db.orders.insertMany([{ "_id": 1, "item": "abc", "price": 12, "ordered": 2 },{ "_id": 2, "item": "jkl", "price": 20, "ordered": 1 }
])
// 创建items集合并插入数据
db.items.insertMany([{ "_id": 1, "item": "abc", description: "product 1", "instock": 120 },{ "_id": 2, "item": "def", description: "product 2", "instock": 80 },{ "_id": 3, "item": "jkl", description: "product 3", "instock": 60 }
])
执行$lookup
聚合操作
我们分三次执行,以便更好理解命令参数
- 左连接操作(orders)
db.orders.aggregate([{$lookup: {from: "items",localField: "item", // ordersforeignField: "item", // itemsas: "fromItems"}}
])
- 可以看到orders的左连接合并结果是以我们提供的
as: "fromItems"
为字段的数组
- 我们需要把数组提出来,合并到根文档上,让两表各自所需的字段合在一起
db.orders.aggregate([{$lookup: {from: "items",localField: "item",foreignField: "item",as: "fromItems"}},{$replaceRoot: {newRoot: {$mergeObjects: [{ $arrayElemAt: ["$fromItems", 0]},"$$ROOT"]}}}
])
- 接下来就是隐藏fromItems数组
db.orders.aggregate([{$lookup: {from: "items",localField: "item",foreignField: "item",as: "fromItems"}},{$replaceRoot: {newRoot: {$mergeObjects: [{ $arrayElemAt: ["$fromItems", 0]},"$$ROOT"]}}},{$project: { fromItems: 0}}
])