Laravel MongoDB中whereMonth/whereYear查询返回空集合问题
问题根因
whereMonth/whereYear这类日期查询方法在jenssegers MongoDB扩展包中的实现逻辑和SQL场景完全不同:
- 该类方法仅对存储类型为MongoDB原生
BSON UTCDateTime(即ISODate)的字段生效,不会自动解析字符串、整型时间戳格式的日期值做月份/年份提取 - 只要
created_at字段不是正确的BSON日期类型,查询条件就不会命中任何文档,直接返回空集合。参数格式问题只是次要影响因素:MongoDB日期提取返回的月份是1-12的整型值,带前导零的字符串(比如'06')会因为类型不匹配无法命中结果,但这不是你当前查询返回空的核心原因。
排查修复方案
- 第一步:核对字段存储类型
直接连接MongoDB查询单条文档,确认created_at字段的存储格式:正确格式为ISODate("2024-06-01T00:00:00Z"),如果是字符串、整型时间戳,说明字段类型不符合查询要求。 - 第二步:核对模型配置
检查MyModel的配置,确保没有手动将created_at排除出自动维护的日期字段列表;Laravel 8及以上版本需要确认$casts中对created_at的日期类型配置正确,不要自定义会输出纯字符串的$dateFormat导致存库时类型转换失效,参考配置:
配置修正后,新增的文档会自动写入正确的BSON日期类型;存量的非日期类型数据需要写一次性脚本批量转换为ISODate格式,转换完成后protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', ];whereMonth/whereYear就可以正常返回结果。 - 临时兼容方案(不修改存量数据时使用)
如果暂时无法调整存量数据格式,可以直接调用whereRaw写Mongo原生聚合查询实现相同的筛选效果,查询6月数据的示例:
按年份筛选只需要把表达式里的$collection = MyModel::whereRaw([ '$expr' => [ '$eq' => [ ['$month' => '$created_at'], 6 ] ] ])->get();$month替换为$year,对应传入整型年份值即可。如果created_at存的是整型时间戳,需要先在原生表达式中用$toDate把字段值转为日期类型再做月份/年份提取。
提示:传入月份参数时直接使用1-12的整型值即可,不要使用带前导零的字符串,否则会因为类型不匹配导致查询失效。
内容的提问来源于stack exchange,提问作者Gabriel Chenaouy
相关产品推荐
相关产品推荐

