MongoDB聚合:unwind保留空数组后分组避免生成空对象
解决MongoDB聚合中空数组$unwind后分组出现空对象的问题
问题分析
当原文档的produceItems为空数组时,开启preserveNullAndEmptyArrays: true的$unwind会生成一个produceItems为null的文档。后续$addFields阶段设置"produceItems.properties"时,MongoDB会自动将null的produceItems转为空对象{},最终$group的$push操作会把这个空对象存入数组,导致结果不符合预期。
解决方案
以下三种方案均可解决该问题,按需选择:
方案一:分组后过滤空对象
在$group阶段之后添加$filter,移除数组中的空对象:
db.produce.aggregate([ { $unwind: { path: "$produceItems", preserveNullAndEmptyArrays: true } }, { $lookup: { from: "fruits", localField: "produceItems.item", foreignField: "_id", as: "fruitProperties" } }, { $lookup: { from: "vegetables", localField: "produceItems.item", foreignField: "_id", as: "vegetableProperties" } }, { $addFields: { "produceItems.properties": { $ifNull: [ { $first: "$fruitProperties" }, { $first: "$vegetableProperties" } ] } } }, { $group: { _id: "$_id", produceItems: { $push: "$produceItems" } } }, // 过滤数组中的空对象 { $addFields: { produceItems: { $filter: { input: "$produceItems", cond: { $ne: ["$$this", {}] } } } } } ])
方案二:分组时仅推送非空项
在$group的$push中通过$cond判断,仅推送非空的produceItems:
db.produce.aggregate([ { $unwind: { path: "$produceItems", preserveNullAndEmptyArrays: true } }, { $lookup: { from: "fruits", localField: "produceItems.item", foreignField: "_id", as: "fruitProperties" } }, { $lookup: { from: "vegetables", localField: "produceItems.item", foreignField: "_id", as: "vegetableProperties" } }, { $addFields: { "produceItems.properties": { $ifNull: [ { $first: "$fruitProperties" }, { $first: "$vegetableProperties" } ] } } }, { $group: { _id: "$_id", produceItems: { $push: { $cond: [ { $ne: ["$produceItems", {}] }, "$produceItems", "$$REMOVE" ] } } } } ])
方案三:提前处理null的produceItems
在$unwind后直接移除null的produceItems字段,避免后续生成空对象:
db.produce.aggregate([ { $unwind: { path: "$produceItems", preserveNullAndEmptyArrays: true } }, // 移除null的produceItems字段 { $addFields: { produceItems: { $ifNull: ["$produceItems", "$$REMOVE"] } } }, { $lookup: { from: "fruits", localField: "produceItems.item", foreignField: "_id", as: "fruitProperties" } }, { $lookup: { from: "vegetables", localField: "produceItems.item", foreignField: "_id", as: "vegetableProperties" } }, { $addFields: { "produceItems.properties": { $ifNull: [ { $first: "$fruitProperties" }, { $first: "$vegetableProperties" } ] } } }, { $group: { _id: "$_id", produceItems: { $push: "$produceItems" } } }, // 将null转为空数组 { $addFields: { produceItems: { $ifNull: ["$produceItems", []] } } } ])
效果验证
三种方案均能实现:
- 当原文档
produceItems为空数组时,返回{"_id": 1, "produceItems": []} - 当原文档
produceItems非空时,正常返回包含properties字段的数组
内容的提问来源于stack exchange,提问作者Grant
相关产品推荐
相关产品推荐

