MongoDB aggregate按日期范围match查询返回空列表如何解决?
问题根因
- 字段类型不匹配
你数据库中存储的createdAt是字符串类型,当你用ISODate或Date对象作为查询条件时,MongoDB不会做跨类型隐式匹配,日期类型和字符串类型比对直接判定为不匹配,所以返回空列表。 - 字符串比对规则不兼容
如果你直接用字符串作为查询条件,$lte: '2021-03-02'只能匹配前缀小于等于2021-03-02的字符串,但你存储的是2020-09-10T18:56:15.094Z格式的时间字符串,T字符的ASCII码高于空字符,所有2021-03-02当天的时间字符串都会被判定为大于2021-03-02,就算是更早日期的字符串如果格式有细微差异也可能匹配失败。 - 聚合管道顺序错误
如果你的$match阶段没有放在聚合管道最前面,前面的$project、$group等阶段可能已经把createdAt字段或者符合条件的文档过滤掉了,也会导致返回空。
解决方案
方案1:修改字段为日期类型(推荐)
这种方式查询性能更高,也不会出现格式兼容问题:
- 先批量转换现有数据的
createdAt类型:
db.something.find({ createdAt: { $type: "string" } }).forEach(doc => { doc.createdAt = ISODate(doc.createdAt); db.something.save(doc); })
- 后续查询统一用日期类型,注意结束时间补到当天最后一毫秒,避免漏数据:
something.aggregate().match({ createdAt: { $gte: ISODate('2020-03-02T00:00:00.000Z'), $lte: ISODate('2021-03-02T23:59:59.999Z') } })
如果转换后还是查询不到,检查时区是否统一,MongoDB的ISODate默认存储UTC时间,查询时建议传入带Z后缀的UTC时间,避免时区偏移导致范围错误。
方案2:不修改现有数据结构
如果不能调整字段类型,直接调整查询字符串适配比对规则:
// 方法1:给结束时间补全最大时间后缀 something.aggregate().match({ createdAt: { $gte: '2020-03-02', $lte: '2021-03-02T23:59:59.999Z' } }) // 方法2:结束边界设置为后一天的零点,逻辑更简洁 something.aggregate().match({ createdAt: { $gte: '2020-03-02', $lt: '2021-03-03' } })
同时确认$match阶段放在聚合管道的最前面,避免前面的阶段过滤掉有效数据。
内容的提问来源于stack exchange,提问作者Kareem Etefy
相关产品推荐
相关产品推荐

