MongoDB4.0三层嵌套数组高效提取匹配_pid与sId方案
MongoDB 4.0 嵌套数组高性能查询方案
问题背景
当前使用MongoDB 4.0版本,集合内存储的文档结构如下:
{ _id: 1, "_a": [ { "_aId": { "CC": "CA" }, "_p": [ { "_pId": { "CC": "CA", "SN": 1 }, "s": { "c": { "t": [ { sId: 1, language: "CA", format: "A4" }, { sId: 2, language: "JP", format: "A4" } ] }, "a": { "t": [ { sId: 4, language: "CA", "format": "A4" }, { sId: 5, language: "EN", "format": "A3" } ] }, "d": { "t": [ { sId: 10, language: "CA", "format": "A4" } ] } } } ] } ] }
需求说明
需要实现高性能查询,满足以下要求:
- 首先过滤出符合
"_a._p._pId.CC":"CA"条件的文档 - 从匹配文档中提取所有同时满足
language:"CA"且format:"A4"条件的子文档,返回子文档对应的sId以及其所属的_pId字段 - 返回结果格式要求如下:
{_pId:{CC:"CA",SN:1},sId:1} {_pId:{CC:"CA",SN:1},sId:4} {_pId:{CC:"CA",SN:1},sId:10}
注:此前已有基础方案可提取符合条件的
sId,但无法在不损失查询性能的前提下,将子文档关联的_pId字段一并返回。
实现方案
前置索引优化
索引是保障查询性能的核心,先创建匹配过滤条件的索引,避免全集合扫描:
db.yourCollection.createIndex({"_a._p._pId.CC": 1})
聚合查询语句(兼容MongoDB 4.0,无高耗操作)
聚合管道全程将过滤逻辑前置,通过逐级打平数组保留上下文关联,避免嵌套遍历带来的性能损耗:
db.yourCollection.aggregate([ // 第一阶段走索引过滤,直接排除所有不符合CC=CA的文档,最小化后续处理数据集 { $match: { "_a._p._pId.CC": "CA" } }, // 逐级打平嵌套数组,打平时自动保留父节点字段,不会丢失_pId关联关系 { $unwind: "$_a" }, { $unwind: "$_a._p" }, // 二次过滤_p节点,提前剔除不符合CC=CA的无效_p数据 { $match: { "_a._p._pId.CC": "CA" } }, // 合并s路径下c、a、d三个节点的t数组,减少后续重复处理逻辑 { $project: { _pId: "$_a._p._pId", tList: { $concatArrays: [ { $ifNull: ["$_a._p.s.c.t", []] }, { $ifNull: ["$_a._p.s.a.t", []] }, { $ifNull: ["$_a._p.s.d.t", []] } ] } } }, // 打平合并后的t数组 { $unwind: "$tList" }, // 过滤出符合language=CA、format=A4的目标子文档 { $match: { "tList.language": "CA", "tList.format": "A4" } }, // 整理输出为要求的格式 { $project: { _id: 0, _pId: 1, sId: "$tList.sId" } } ])
性能说明
- 所有
$match阶段尽可能前置,第一阶段直接命中索引,后续处理的数据量被压缩到最小 - 未使用
$expr、$filter等需要全量遍历深层嵌套数组的高耗操作,$unwind配合前置过滤的执行效率在MongoDB 4.0版本远高于嵌套表达式遍历方案 - 数组打平过程中全程保留
_pId的上下文关联,不需要额外关联计算,无额外性能损耗
内容的提问来源于stack exchange,提问作者R2D2
相关产品推荐
相关产品推荐

