You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:51:12