MongoDB聚合查询:筛选特定日期处于活跃状态的用户
MongoDB查询特定日期处于活跃状态的用户(基于start/end日期数组)
问题背景
用户集合中每个文档包含startDates(激活开始日期数组)和endDates(激活结束日期数组),规则如下:
- 每个
startDates[i]对应endDates[i],构成一个激活周期 - 若
startDates长度大于endDates,说明用户当前处于激活状态(最后一个开始日期无对应结束日期) - 需要查询指定日期(如
12.05.2020、22.11.2022)处于活跃的用户,排除17.12.2021这类不符合的日期
示例文档:
{ "active" : true, "endDates" : [ "16.11.2021", "27.06.2020" ], "startDates" : [ "21.10.2022", "16.10.2021", "09.04.2020" ] }
原方案问题
直接对两个数组执行两次$unwind会生成所有日期的笛卡尔积组合,无法对应真实的激活周期,导致筛选逻辑失效。
正确解决方案
核心思路是:先将字符串日期转为MongoDB标准Date类型,再按索引配对生成真实激活周期,最后判断目标日期是否落在任意周期内。
完整聚合代码:
// 替换为你要查询的目标日期,注意格式为YYYY-MM-DD const targetDate = new Date("2020-05-12"); collection.aggregate([ // 1. 将dd.mm.yyyy格式的字符串日期转为MongoDB Date类型 { $addFields: { parsedStartDates: { $map: { input: "$startDates", in: { $dateFromString: { dateString: "$$this", format: "%d.%m.%Y" } } } }, parsedEndDates: { $map: { input: "$endDates", in: { $dateFromString: { dateString: "$$this", format: "%d.%m.%Y" } } } } } }, // 2. 按索引配对生成激活周期,处理"当前活跃"的情况(无结束日期) { $addFields: { activePeriods: { $map: { input: { $range: [0, { $size: "$parsedStartDates" }] }, as: "index", in: { start: { $arrayElemAt: ["$parsedStartDates", "$$index"] }, end: { $cond: { if: { $lt: ["$$index", { $size: "$parsedEndDates" }] }, then: { $arrayElemAt: ["$parsedEndDates", "$$index"] }, else: null // null代表当前持续激活,无结束日期 } } } } } } }, // 3. 判断目标日期是否落在任意激活周期内 { $addFields: { isActiveOnTarget: { $anyElementTrue: { $map: { input: "$activePeriods", in: { $cond: { if: { $eq: ["$$this.end", null] }, // 当前活跃:目标日期 >= 开始日期 then: { $gte: [targetDate, "$$this.start"] }, // 已结束周期:目标日期在[start, end]区间内 else: { $and: [{ $gte: [targetDate, "$$this.start"] }, { $lte: [targetDate, "$$this.end"] }] } } } } } } } }, // 4. 筛选出符合条件的用户 { $match: { isActiveOnTarget: true } }, // 可选:清理聚合过程中生成的临时字段,返回原文档结构 { $project: { parsedStartDates: 0, parsedEndDates: 0, activePeriods: 0, isActiveOnTarget: 0 } } ])
逻辑验证
- 针对
2020-05-12:匹配周期2020-04-09至2020-06-27,符合条件 - 针对
2022-11-22:startDates长度大于endDates,最后一个开始日期为2022-10-21,目标日期晚于该日期,符合条件 - 针对
2021-12-17:所有已结束周期均不包含该日期,且最后一个开始日期2022-10-21晚于目标日期,不符合条件
内容的提问来源于stack exchange,提问作者Nargis
相关产品推荐
相关产品推荐

