MongoDB查询:匹配嵌套对象任意数组含指定字符串/正则的文档
MongoDB 动态键嵌套数组匹配查询方案
问题场景
现有同结构的MongoDB集合,文档结构示例如下:
{ _id: '1', text: 'xxxxxxxc', choices: { a: ['xxx','yyy','zzz'], b: ['aaa','bbb','ccc'], c: ['lll','mmm','ddd'], } } // 大量其他同结构文档... { _id: '2', text: 'xxxxxxxc', choices: { a: ['ooo','sss','qqq'], b: ['iii','hhh','ggg'], c: ['ddd','eee','fff'], } }
核心查询需求:
- 遍历集合所有文档,判断
choices字段下任意动态键对应的数组中,是否存在元素匹配指定字符串/正则规则 - 只要任意一个数组存在匹配元素,就返回对应文档;无匹配则不返回
- 约束:
choices下的键名不固定(除a/b/c外可能后续新增其他键),无法提前枚举所有键写死查询条件 - 示例:查询值为
'yyy'时,需要返回_id:1的文档(其choices.a包含该值),不返回_id:2的文档
实现方案
因为choices的键是动态的,不能写死字段路径,分场景选择对应写法即可:
1. 精确匹配字符串场景
用$objectToArray把choices对象转成键值对数组,再判断任意值数组里是否包含目标值即可,查询语句示例:
db.collection.find({ $expr: { $gt: [ { $size: { $filter: { input: { $objectToArray: "$choices" }, cond: { $in: ["yyy", "$$this.v"] } } } }, 0 ] } })
逻辑说明:
$objectToArray: "$choices"会把动态键的对象转成[{k:'a', v:['xxx','yyy','zzz']}, {k:'b', v:[...]}...]结构,不需要提前知道键名$filter筛选出v数组(也就是原来每个键对应的数组)里包含目标值的项- 只要筛选结果长度大于0,就说明存在匹配项,返回文档
2. 正则匹配场景
如果需要用正则做模糊匹配,把上面的$in替换成$regexMatch即可,示例(匹配所有以yy开头的元素):
db.collection.find({ $expr: { $gt: [ { $size: { $filter: { input: { $objectToArray: "$choices" }, cond: { $gt: [ { $size: { $filter: { input: "$$this.v", cond: { $regexMatch: { input: "$$this", regex: /^yy/ } } } } }, 0 ] } } } }, 0 ] } })
如果使用MongoDB 5.0及以上版本,还可以用$anyElementTrue简化写法:
// 精确匹配简化版 db.collection.find({ $expr: { $anyElementTrue: { $map: { input: { $objectToArray: "$choices" }, in: { $in: ["yyy", "$$this.v"] } } } } }) // 正则匹配简化版 db.collection.find({ $expr: { $anyElementTrue: { $map: { input: { $objectToArray: "$choices" }, in: { $anyElementTrue: { $map: { input: "$$this.v", in: { $regexMatch: { input: "$$this", regex: /^yy/ } } } } } } } } })
优化提示:如果集合数据量很大,可以给
choices字段加通配符索引优化查询性能,建索引语句:db.collection.createIndex({"choices.$**": 1})
内容的提问来源于stack exchange,提问作者loop loopertzg
相关产品推荐
相关产品推荐

