MongoDB如何使用$pull删除多元素数组中属性为null的嵌套元素
问题描述
我需要使用$pull操作删除嵌套数组中同时满足两个条件的元素:
- 元素的某一属性值为null
- 所属数组的元素个数大于1
以下方的测试集合为例,我需要删除Orders数组中满足Amount = null、且Orders数组长度大于1的元素,即仅删除OrderId = 12的元素,其余所有元素都保留。
测试数据初始化代码:
db.TestProducts.insertMany([ { ProductDetails: { "ProductId": 1, Language: "fr" }, Orders: [ { "OrderId": 11, "Amount": 200 }, { "OrderId": 12, "Amount": null } ] }, { ProductDetails: { "ProductId": 2, Language: "es" }, Orders: [ { "OrderId": 13, "Amount": 300 }, { "OrderId": 14, "Amount": 400 } ] }, { ProductDetails: { "ProductId": 3, "Language": "en" }, Orders: [ { "OrderId": 15, "Amount": null } ] } ]);
我之前参考相关实现写了一版方案,通过聚合导出临时表再遍历更新:
db.TestProducts.aggregate( [ { $match: { "Orders.Amount": { "$eq": null } } }, { $unwind: "$Orders" }, { "$group": { "_id": { ProductId: "$ProductDetails.ProductId", Language: "$ProductDetails.Language" },"count": { "$sum": 1 } } }, { "$match": { "count": { "$gt": 1 } } }, { "$out": "temp_results" } ], { allowDiskUse: true} ); db.temp_results.find().forEach((result) => { db.TestProducts.updateMany({"ProductDetails.ProductId": result._id.ProductId, "ProductDetails.Language": result._id.Language }, { $pull: { "Orders": {"Amount": null } }}) });
这版方案可以正常运行实现需求,但我想找更简洁的实现,最好能直接在聚合管道内完成数组元素删除,省去额外的forEach遍历步骤。
解决方案
MongoDB 4.2及以上版本支持在更新操作中直接使用聚合管道,不需要中间临时表和遍历步骤,单条语句就能完成需求:
db.TestProducts.updateMany( { // 前置过滤:只处理存在null金额订单、且订单数组长度大于1的文档,减少无效执行 "Orders.Amount": null, $expr: { $gt: [{ $size: "$Orders" }, 1] } }, [ { $set: { Orders: { $filter: { input: "$Orders", cond: { $ne: ["$$this.Amount", null] } } } } } ] )
实现说明
- 第一层匹配条件直接筛掉不需要修改的文档:没有null金额订单的、订单数组长度只有1的文档都不会进入更新逻辑,比先聚合统计长度再遍历修改的方案性能更好。
- 聚合更新阶段用
$filter直接过滤掉Orders数组里Amount为null的元素,效果和$pull完全一致,全程在单条更新语句内完成,不需要生成临时集合、不需要手动遍历修改。 - 执行后结果符合预期:ProductId=1的文档仅保留OrderId=11的订单,ProductId=2的文档无变化,ProductId=3的文档因为Orders长度为1,即使存在null金额的订单也不会被修改。
如果是MongoDB 4.2以下版本不支持聚合更新,也可以直接在更新条件里加数组长度判断,不需要临时表:
db.TestProducts.updateMany( { "Orders.Amount": null, $where: "this.Orders.length > 1" }, { $pull: { Orders: { Amount: null } } } )
这版写法更简短,但是$where的执行性能比聚合表达式差,适合数据量较小的场景使用。
内容的提问来源于stack exchange,提问作者Developer
相关产品推荐
相关产品推荐

