Doctrine MongoDB ODM 2.x 多条件查询返回空结果问题求助
问题场景
我在PHP环境中使用mongodb-odm 2.x查询MongoDB时遇到异常:单条件查询正常,加第二个条件后返回空结果,但原生CLI执行相同条件查询可以正常返回数据。以下是不同写法的测试结果:
测试写法1:Query Builder链式双条件查询
$results = $dm->createQueryBuilder(self::$repository) ->find() ->field('isAdmin')->equals(true) ->field('language')->equals($this->getCountry()->getRegion()) ->getQuery() ->execute();
返回结果:
["items":"Doctrine\ODM\MongoDB\Iterator\CachingIterator":private]=> array(0) { } ["iterator":"Doctrine\ODM\MongoDB\Iterator\CachingIterator":private]=> object(Generator)#566 (0) { } ["iteratorAdvanced":"Doctrine\ODM\MongoDB\Iterator\CachingIterator":private]=> bool(false) ["iteratorExhausted":"Doctrine\ODM\MongoDB\Iterator\CachingIterator":private]=> bool(true) }
测试写法2:Repository findBy双条件查询
$results = $dm->getRepository(self::$repository)->findBy([ 'isAdmin' => true, 'language' => $this->getCountry()->getRegion() ]);
返回结果:
array(0) { }
测试写法3:Repository findBy单isAdmin条件查询
$results = $dm->getRepository(self::$repository)->findBy([ 'isAdmin' => true ]);
返回结果:
array(182) { [0]=> object(Model\User)#572 (41) {...},
已知前提
- 写法3仅传
isAdmin条件可正常返回182条数据 - 写法1严格参照官方Query Builder API文档编写
- MongoDB CLI直接执行
db.User.find({'isAdmin':true, 'language': 'en_GB'})可正常查询到数据
原因分析
这类双条件查询空结果、原生CLI查询正常的问题,基本都是两个原因导致的:
- 字段映射不匹配:Doctrine MongoDB ODM不会直接把传入的查询字段名透传给MongoDB,会根据文档类的属性映射配置转换字段名。比如User类的
$language属性如果通过注解/Attribute指定了存储字段名,或者开启了全局驼峰转下划线配置,实际查询的字段就不是代码里写的language,自然匹配不到数据。 - 查询值不匹配:
$this->getCountry()->getRegion()返回的值和数据库中存储的language字段值类型/内容不一致,比如返回的是地区值对象、带不可见空白字符、大小写不符,都会导致条件匹配失败。
排查与修复方案
- 先打印ODM实际生成的查询语句,直接定位差异点:
$query = $dm->createQueryBuilder(self::$repository) ->find() ->field('isAdmin')->equals(true) ->field('language')->equals($this->getCountry()->getRegion()) ->getQuery(); // 输出实际发往MongoDB的查询条件 var_dump($query->getQuery());
把输出的查询条件和CLI执行的查询语句逐字段对比,是字段名不对还是查询值不对可以直接定位。
2. 检查User文档类的language属性映射配置:
- 如果属性上配置了自定义存储字段名(比如
#[Field(name: 'lang')]),查询时必须使用配置的存储字段名,不能直接用类属性名language - 如果项目开启了全局自动命名转换规则,按照转换后的实际字段名查询即可
- 校验传入的查询值:
// 打印传入的language值的类型和内容,确认和数据库存储值完全一致 var_dump($this->getCountry()->getRegion());
如果返回值不是字符串、带多余空白、大小写和库中值不符,先做格式清洗,比如强制转为字符串(string)$this->getCountry()->getRegion()再传入查询。
4. 快速验证方法:直接把查询条件里的language值写死为'en_GB'、字段名换成CLI查询用的字段名测试,如果能返回结果,就能直接定位是映射配置问题还是传值问题。
内容的提问来源于stack exchange,提问作者cookie
相关产品推荐
相关产品推荐

