如何基于Brands集合阈值,用Mongoose聚合筛选Sales数据
问题
需要从Sales集合中获取按brand_id分组,且品牌总销售额超过对应阈值的所有文档。目前已实现固定阈值(1000美元)的Mongoose聚合方案,但阈值实际存储在Brands集合中(每个品牌对应不同阈值)。不想循环遍历Brands文档多次调用聚合,寻求更优实现方案。
Sales集合样本数据
[ { "brand_id": "A", "price": 500 }, { "brand_id": "A", "price": 700 }, { "brand_id": "B", "price": 1500 }, { "brand_id": "C", "price": 100 }, { "brand_id": "D", "price": 400 }, { "brand_id": "D", "price": 600 }, { "brand_id": "D", "price": 200 } ]
现有固定阈值实现代码
const data = await Sales.aggregate([ { $group: { _id: "$brand_id", total_sales: { $sum: "$price" }, records: { $push: "$$ROOT" } } }, { $match: { total_sales: { $gt: 1000 } } }, { $unwind: "$records" }, { $replaceWith: "$records" } ])
Brands集合样本数据
[ { "brand_id":"A", "brand_name": "abc", "threshold": 1000 }, { "brand_id": "B", "brand_name": "hef", "threshold": 600 }, { "brand_id": "C", "brand_name": "xyz", "threshold": 310 } ]
期望输出格式
{ "A": [ { "_id": ObjectId("5a934e000102030405000000"), "brand_id": "A", "price": 500 }, { "_id": ObjectId("5a934e000102030405000001"), "brand_id": "A", "price": 700 } ], "B": [ { "_id": ObjectId("5a934e000102030405000002"), "brand_id": "B", "price": 1500 } ], "D": [ { "_id": ObjectId("5a934e000102030405000004"), "brand_id": "D", "price": 400 }, { "_id": ObjectId("5a934e000102030405000005"), "brand_id": "D", "price": 600 }, { "_id": ObjectId("5a934e000102030405000006"), "brand_id": "D", "price": 200 } ] }
解决方案
可以通过聚合管道关联两个集合,一次性完成计算、阈值匹配和结果格式化,无需循环调用。核心逻辑是先计算品牌总销售额,再关联Brands集合获取阈值,最后筛选并整理成目标格式。
完整Mongoose聚合代码如下:
const result = await Sales.aggregate([ // 1. 按brand_id分组,计算总销售额并保留原始记录 { $group: { _id: "$brand_id", total_sales: { $sum: "$price" }, records: { $push: "$$ROOT" } } }, // 2. 左连接Brands集合,获取对应品牌的阈值信息 { $lookup: { from: "brands", // 注意是数据库中的集合名称,非Mongoose模型名 localField: "_id", foreignField: "brand_id", as: "brand_info" } }, // 3. 提取阈值,处理Brands中不存在的品牌(如D),默认阈值设为0 { $addFields: { threshold: { $cond: { if: { $gt: [{ $size: "$brand_info" }, 0] }, then: { $arrayElemAt: ["$brand_info.threshold", 0] }, else: 0 } } } }, // 4. 筛选总销售额超过对应阈值的品牌 { $match: { $expr: { $gt: ["$total_sales", "$threshold"] } } }, // 5. 整理为key-value结构,方便后续转为对象 { $project: { _id: 0, key: "$_id", value: "$records" } }, // 6. 将所有key-value对合并成目标格式的对象 { $group: { _id: null, data: { $push: { k: "$key", v: "$value" } } } }, { $replaceWith: { $arrayToObject: "$data" } } ]) // 最终结果取数组中的第一个对象,为空则返回空对象 const finalData = result[0] || {}
步骤说明
- $group:按
brand_id聚合,计算每个品牌的总销售额,同时收集该品牌的所有销售记录。 - $lookup:通过左连接关联
Brands集合,获取对应品牌的阈值数据。 - $addFields:从连接结果中提取阈值,对
Brands中未收录的品牌设置默认阈值0,确保这类品牌的销售额判断逻辑正常。 - $match:使用
$expr实现动态字段比较,筛选出总销售额超过阈值的品牌。 - $project:将数据转换为
key-value结构,为后续生成目标对象做准备。 - $group + $replaceWith:将所有
key-value对合并成一个对象,得到期望的输出格式。
内容的提问来源于stack exchange,提问作者Akash
相关产品推荐
相关产品推荐

