MongoDB聚合查询:筛选日期范围内子文档并保留原结构
保留原文档结构筛选MongoDB用户订阅记录的解决方案
我明白你的需求——你想要筛选出每个用户下符合日期条件的订阅记录,同时保持原有的文档结构(每个用户对应一个文档,包含筛选后的订阅数组),而不是把每条订阅拆成单独的文档。之前的$unwind会破坏原结构,$redact又因为类型问题报错,咱们来解决这个问题:
正确的聚合查询
直接用$filter来处理订阅数组,不需要拆分组装,就能完美保留原文档结构:
db.gyms.aggregate([ { $addFields: { subscriptions: { $filter: { input: "$subscriptions", as: "sub", cond: { $and: [ { $ne: ["$$sub.activation_date", null] }, // 避免空日期导致的类型错误 { $eq: [{ $month: "$$sub.activation_date" }, 1] } // 筛选1月份的订阅 ] } } } } }, // 可选:如果要移除没有符合条件订阅的用户,加上这个阶段 { $match: { "subscriptions.0": { $exists: true } } } ]).pretty()
为什么这个方法可行?
- 保留原结构:
$addFields会直接替换原文档中的subscriptions字段为筛选后的数组,每个用户依然是一个独立的文档,完全符合你想要的输出格式。 - 避免类型错误:先通过
$ne判断日期字段不为空,解决了你用$redact时遇到的“无法将missing类型转为Date”的报错——因为$redact会遍历文档的所有字段,包括非订阅相关的字段,而$filter只针对subscriptions数组内的元素处理。 - 更高效简洁:相比先用
$unwind拆分再$group重组的方式,$filter一步到位,性能更好。
如果需要指定具体日期范围
比如要筛选2018年1月1日到1月31日的订阅,只需要修改cond里的判断条件:
db.gyms.aggregate([ { $addFields: { subscriptions: { $filter: { input: "$subscriptions", as: "sub", cond: { $and: [ { $ne: ["$$sub.activation_date", null] }, { $gte: ["$$sub.activation_date", ISODate("2018-01-01T00:00:00Z")] }, { $lt: ["$$sub.activation_date", ISODate("2018-02-01T00:00:00Z")] } ] } } } } }, { $match: { "subscriptions.0": { $exists: true } } } ]).pretty()
运行这个查询后,你就能得到每个用户仅包含符合条件订阅记录的文档,完全匹配你期望的输出格式。
内容的提问来源于stack exchange,提问作者AlpeshVasani
相关产品推荐
相关产品推荐

