如何用MongoDB的$aggregate按时间段分组发票数据并求和?
MongoDB按时间段分组汇总发票数据解决方案
需求说明
将发票数据按0-10天、11-20天、>20天的时间段分组,汇总每个时间段对应的value总和,使用MongoDB聚合管道实现。
示例数据
[ { '_id': '1', 'value': 10, 'due_date': '20221001' }, { '_id': '2', 'value': 10, 'due_date': '20221012' }, { '_id': '3', 'value': 10, 'due_date': '20221030' } ]
期望结果
[ { "_id": "0-10 days", "total": 10 }, { "_id": "11-20 days", "total": 10 }, { "_id": ">20 days", "total": 10 } ]
问题分析
你之前使用$facet的写法没有实现需求,核心问题是未正确处理日期格式转换、日期差计算,也没有设置符合要求的时间段分组逻辑。
正确聚合管道实现
以下是完整的聚合步骤,针对字符串格式的due_date做了适配:
- 转换日期格式:将字符串类型的
due_date(如20221001)转为MongoDB可识别的日期类型 - 计算天数差:算出当前日期与
due_date的间隔天数 - 标记时间段:根据天数差判断数据所属的分组区间
- 分组求和:按时间段分组,汇总
value的总和
完整聚合代码(MongoDB Shell)
db.invoices.aggregate([ // 转换due_date为日期类型 { $addFields: { dueDate: { $dateFromString: { dateString: "$due_date", format: "%Y%m%d" } } } }, // 计算当前日期与dueDate的天数差 { $addFields: { daysDiff: { $floor: { $divide: [ { $subtract: [new Date(), "$dueDate"] }, 1000 * 60 * 60 * 24 // 毫秒转天数 ] } } } }, // 标记所属时间段 { $addFields: { timeRange: { $switch: { branches: [ { case: { $and: [{ $gte: ["$daysDiff", 0] }, { $lte: ["$daysDiff", 10] }] }, then: "0-10 days" }, { case: { $and: [{ $gte: ["$daysDiff", 11] }, { $lte: ["$daysDiff", 20] }] }, then: "11-20 days" } ], default: ">20 days" } } } }, // 按时间段分组求和 { $group: { _id: "$timeRange", total: { $sum: "$value" } } }, // 可选:按时间段排序 { $sort: { _id: 1 } } ])
PHP环境适配(使用Carbon)
如果是在PHP中使用MongoDB扩展,代码调整如下:
$pipeline = [ // 转换日期格式 [ '$addFields' => [ 'dueDate' => [ '$dateFromString' => [ 'dateString' => '$due_date', 'format' => '%Y%m%d' ] ] ] ], // 计算天数差 [ '$addFields' => [ 'daysDiff' => [ '$floor' => [ '$divide' => [ ['$subtract' => [new MongoDB\BSON\UTCDateTime(), '$dueDate']], 1000 * 60 * 60 * 24 ] ] ] ] ], // 标记时间段 [ '$addFields' => [ 'timeRange' => [ '$switch' => [ 'branches' => [ [ 'case' => ['$and' => [['$gte' => ['$daysDiff', 0]], ['$lte' => ['$daysDiff', 10]]]], 'then' => '0-10 days' ], [ 'case' => ['$and' => [['$gte' => ['$daysDiff', 11]], ['$lte' => ['$daysDiff', 20]]]], 'then' => '11-20 days' ] ], 'default' => '>20 days' ] ] ] ], // 分组求和 [ '$group' => [ '_id' => '$timeRange', 'total' => ['$sum' => '$value'] ] ], // 排序 [ '$sort' => ['_id' => 1] ] ]; // 执行聚合查询 $result = $collection->aggregate($pipeline);
关键说明
$dateFromString:指定格式%Y%m%d,将字符串日期转为MongoDB日期类型- 日期差计算:通过
$subtract得到毫秒差后,除以一天的毫秒数得到天数,$floor取整避免小数干扰 $switch:覆盖所有天数区间的判断逻辑,确保每条数据都能分到对应组$group:按标记的时间段分组,用$sum完成数值汇总
内容的提问来源于stack exchange,提问作者Marlon code
相关产品推荐
相关产品推荐

