MongoDB聚合$unwind后$group保留父结构并重组嵌套数组方案
MongoDB聚合嵌套数组筛选后结构重组方案
最优实现方案(避免冗余unwind)
直接通过数组操作符完成嵌套筛选,无需展开数组,天然保留原父级结构,性能更优:
const targetSigningPerson = ObjectId("你的指定签署人ID"); db.songs.aggregate([ // 1. 关联agreements集合 { $lookup: { from: "agreements", localField: "关联字段", foreignField: "关联字段", as: "agreements" } }, // 2. 嵌套筛选符合条件的协议版本 { $addFields: { agreements: { $filter: { input: { $map: { input: "$agreements", as: "agreement", in: { // 保留协议所有父级字段 $mergeObjects: [ "$$agreement", { agreementVersion: { $filter: { input: "$$agreement.agreementVersion", as: "version", cond: { $eq: ["$$version.isLatestVersion", true] } } } } ] } } }, as: "filteredAgreement", cond: { $gt: [{ $size: "$$filteredAgreement.agreementVersion" }, 0] } } } } }, // 3. 筛选版本下符合条件的approvalStatus { $addFields: { agreements: { $map: { input: "$agreements", as: "agreement", in: { $mergeObjects: [ "$$agreement", { agreementVersion: { $map: { input: "$$agreement.agreementVersion", as: "version", in: { // 保留版本所有父级字段 $mergeObjects: [ "$$version", { approvalStatus: { $filter: { input: "$$version.approvalStatus", as: "status", cond: { $and: [ { $eq: ["$$status.signingPerson", targetSigningPerson] }, { $ne: ["$$status.isApprovedByUser", true] } ] } } } } ] } } } } ] } } } } }, // 可选:过滤掉没有匹配审批状态的空版本/空协议 { $addFields: { agreements: { $filter: { input: "$agreements", as: "agreement", cond: { $gt: [ { $size: { $filter: { input: "$$agreement.agreementVersion", as: "v", cond: { $gt: [{ $size: "$$v.approvalStatus" }, 0] } } } }, 0 ] } } } } } ])
兼容unwind+group写法
如果必须保留先展开再匹配的原有逻辑,调整$group阶段的分组规则即可:
// $unwind、$match阶段和原有逻辑一致,仅修改$group阶段 { $group: { // 按歌曲ID+协议ID+版本ID作为唯一分组键,保证同协议同版本只留一份 _id: { songId: "$_id", agreementId: "$agreements._id", versionId: "$agreements.agreementVersion._id" }, // 保留歌曲根字段 songField: { $first: "$需要保留的歌曲字段" }, // 保留协议父级字段 agreementField: { $first: "$agreements.需要保留的协议字段" }, // 保留版本父级字段 versionField: { $first: "$agreements.agreementVersion.需要保留的版本字段" }, // 合并匹配到的所有审批状态 matchedApprovalStatus: { $push: "$agreements.agreementVersion.approvalStatus" } } }
核心注意点
- 嵌套数组筛选优先用
$map+$filter+$mergeObjects组合,避免多层unwind带来的结构碎片化问题 - 分组时唯一键必须同时包含协议ID和版本ID,才能保证同协议的不同版本不会被错误合并
$ne: ["$$status.isApprovedByUser", true]可以同时覆盖字段不存在和值为false两种场景,无需额外写$exists判断
内容的提问来源于stack exchange,提问作者GameAtrix1
相关产品推荐
相关产品推荐

