MongoDB聚合:过滤嵌套数组内的目标记录
MongoDB 过滤嵌套数组并保留原文档结构的解决方案
问题场景
现有MongoDB集合中的文档结构如下:
{ "cId":23456, "description":"car", "brand": [{ "bId":23, "bName":"toyota", "model":[{ "mName":"camry", "vId":"12" }, { "mName":"corolla", "vId":"13" }] },{ "bId":24, "bName":"honda", "model":[{ "mName":"accord", "vId":"14" }, { "mName":"civic", "vId":"15" }] }] }
需要筛选出mName: civic的记录,最终输出需保留原文档层级,只保留包含目标model的brand,且该brand下仅保留符合条件的model,结果如下:
{ "cId":23456, "description":"car", "brand": [{ "bId":24, "bName":"honda", "model":[ { "mName":"civic", "vId":"15" }] }] }
解决方案(无需反复unwind)
可以通过聚合管道中的$filter与$map组合实现,避免多次unwind后分组的复杂操作:
db.collection.aggregate([ // 先筛选出包含目标model的文档,减少后续处理量 { $match: { "brand.model.mName": "civic" } }, // 过滤brand数组,只保留包含目标model的品牌 { $project: { cId: 1, description: 1, brand: { $filter: { input: "$brand", as: "brandItem", cond: { $in: ["civic", "$$brandItem.model.mName"] } } } } }, // 对保留的品牌,过滤其model数组只保留符合条件的项 { $addFields: { brand: { $map: { input: "$brand", as: "brandItem", in: { bId: "$$brandItem.bId", bName: "$$brandItem.bName", model: { $filter: { input: "$$brandItem.model", as: "modelItem", cond: { $eq: ["$$modelItem.mName", "civic"] } } } } } } } } ])
代码解释
- $match阶段:快速筛选出包含目标model的文档,避免对无关文档做后续处理,提升性能。
- $project阶段:用
$filter过滤外层brand数组,只保留包含civic型号的品牌项。 - $addFields阶段:用
$map遍历保留的品牌,对每个品牌的model数组再次做$filter,仅保留符合条件的型号记录。
更简洁的合并写法(MongoDB 3.6+)
可以将两个过滤步骤合并到同一个$project中:
db.collection.aggregate([ { $match: { "brand.model.mName": "civic" } }, { $project: { cId: 1, description: 1, brand: { $map: { input: { $filter: { input: "$brand", as: "brand", cond: { $in: ["civic", "$$brand.model.mName"] } } }, as: "brand", in: { bId: "$$brand.bId", bName: "$$brand.bName", model: { $filter: { input: "$$brand.model", as: "model", cond: { $eq: ["$$model.mName", "civic"] } } } } } } } } ])
为什么不推荐多次unwind?
反复unwind嵌套数组后再分组,容易丢失原文档的层级关系,且需要多次$group操作,代码复杂度高,性能也远不如直接用$filter和$map的组合方案。
内容的提问来源于stack exchange,提问作者rjc
相关产品推荐
相关产品推荐

