MongoDB如何筛选出属于指定日期数组的日期?
嗨,我明白你现在的困扰了——你有一个MongoDB集合,里面的文档存着available_from和available_to两个日期字段,想找出指定日期数组里那些落在这个日期区间内的日期,或者筛选出包含这些日期的文档对吧?先说说你当前查询没结果的问题,再给你一步步讲正确的实现方式。
首先,你现在用的$match条件是直接精确匹配available_from和available_to等于字符串日期,虽然MongoDB有时候会自动把字符串转成ISODate,但这个逻辑本身就不符合你的需求呀!你要的不是找刚好这两个字段等于指定日期的文档,而是要找日期范围和指定数组有交集的文档,或者提取数组里落在区间内的日期。当然,就算是精确匹配,显式用ISODate()构造日期类型也更稳妥,避免时区或者类型转换的坑。
咱们分两种常见情况来解决:
情况1:筛选出包含指定数组中至少一个日期的文档
假设你的目标日期数组是["2024-09-20", "2024-09-25", "2024-09-30"],我们可以用聚合表达式$expr结合$anyElementTrue和$map来判断数组里有没有日期落在文档的区间内。这里建议把目标日期转成ISODate类型,比字符串处理更高效:
// 先定义目标日期数组(用ISODate类型) const targetDates = [ ISODate("2024-09-20T00:00:00.000Z"), ISODate("2024-09-25T00:00:00.000Z"), ISODate("2024-09-30T00:00:00.000Z") ]; // 执行查询 db.collection.find({ $expr: { $anyElementTrue: { $map: { input: targetDates, as: "dt", in: { $and: [ { $gte: ["$$dt", "$available_from"] }, // 目标日期 >= available_from { $lte: ["$$dt", "$available_to"] } // 目标日期 <= available_to ] } } } } })
这个查询会返回所有日期区间包含目标数组中至少一个日期的文档。
情况2:提取指定数组中落在文档日期区间内的日期
如果你不光要筛选文档,还想把目标数组里符合条件的日期单独提取出来,那就用聚合管道来实现,先筛选再提取:
const targetDates = [ ISODate("2024-09-20T00:00:00.000Z"), ISODate("2024-09-22T00:00:00.000Z"), ISODate("2024-09-30T00:00:00.000Z"), ISODate("2024-10-01T00:00:00.000Z") // 这个日期不在文档区间里,会被过滤掉 ]; db.collection.aggregate([ // 第一步:先筛选出有匹配日期的文档(可选,能提升后续处理的效率) { $match: { $expr: { $anyElementTrue: { $map: { input: targetDates, as: "dt", in: { $and: [ { $gte: ["$$dt", "$available_from"] }, { $lte: ["$$dt", "$available_to"] } ] } } } } } }, // 第二步:提取目标数组中落在区间内的日期,新增一个matchingDates字段 { $addFields: { matchingDates: { $filter: { input: targetDates, cond: { $and: [ { $gte: ["$$this", "$available_from"] }, { $lte: ["$$this", "$available_to"] } ] } } } } } ])
执行完这个聚合后,每个文档会多一个matchingDates字段,里面就是目标数组中落在该文档日期区间内的所有日期。
为什么你当前的查询没结果?
如果你确实是想精确匹配available_from和available_to等于指定日期,那大概率是因为字符串日期的类型转换问题——比如文档里的日期是UTC时区的0点,而你传入的字符串可能被MongoDB解析成了本地时区的日期,导致不匹配。这时候把字符串换成ISODate()就能解决:
db.collection.find({ "available_from": ISODate("2024-09-20T00:00:00.000Z"), "available_to": ISODate("2024-09-30T00:00:00.000Z") })
这样就能精准匹配到你的测试文档啦。
备注:内容来源于stack exchange,提问作者Zesshi

