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

如何在Mongoose中修正聚合后的对象数组投影格式?

问题:移除聚合查询结果中history的嵌套对象,保留数组格式

我有一个记录卡车与司机的数据库,需要追踪司机驾驶卡车的时间以及驾驶过的所有卡车记录,为此定义了Mongoose Schema:

const driverHistorySchema = mongoose.Schema({
    driver: {
        type: Schema.Types.ObjectId, ref: "Employee",
        required: true,
    },
    history: [{
        transport: {
            type: Schema.Types.ObjectId, ref: "Transport",
            required: true,
        },
        time: {
            type: Date,
            required: true
        }
    }]
})

针对Transport表编写了聚合查询,想要关联司机的驾驶记录,但返回结果里的history字段嵌套了一层对象,不是预期的数组格式:

原查询代码:

const transports = await Transport.aggregate([
    {
      $lookup: {
        from: "driverhistories",
        let: {
          "driverId": "$driver"
        },
        pipeline: [
          { $match: { $expr: { $eq: ["$driver", "$$driverId"] } } },
          { $replaceRoot: { newRoot: { history: "$history" } } },
        ],
        as: "history"
      }

    },
    {
      "$unwind": {
        "path": "$history",
        "preserveNullAndEmptyArrays": true
      }
    },
  ]);

当前返回结果:

{
 "_id": "xxxx",
 ....
 "history": {
                "history": [
                    {
                        "transport": "xxxx",
                        "time": "2023-11-02T09:19:14.654Z",
                        "_id": "xxxx"
                    },
                    {
                        "transport": "xxxx",
                        "time": "2023-11-03T11:23:50.956Z",
                        "_id": "xxxx"
                    }
                ]
            }
}

期望结果:

{
    "_id": "xxxx",
    ....
    "history": [
                    {
                        "transport": "xxxx",
                        "time": "2023-11-02T09:19:14.654Z",
                        "_id": "xxxx"
                    },
                    {
                        "transport": "xxxx",
                        "time": "2023-11-03T11:23:50.956Z",
                        "_id": "xxxx"
                    }
                ]
}

尝试过几种投影方式都没能解决嵌套问题,比如:

  • { $project: { _id: 0, history: "$history" } }
  • { $project: { _id: 0, transport: "$history.transport", time: "$history.time" } }

解决方案

问题出在原查询的$replaceRoot步骤,它把driverHistory文档包装成了{ history: [...] }的对象,导致$lookup后得到的是包含该对象的数组,再经过$unwind就形成了嵌套结构。下面提供两种可行的修改方案:

方案一:针对单条driverHistory记录的场景(每个司机对应一条驾驶历史)

const transports = await Transport.aggregate([
  {
    $lookup: {
      from: "driverhistories",
      let: { driverId: "$driver" },
      pipeline: [
        { $match: { $expr: { $eq: ["$driver", "$$driverId"] } } },
        { $project: { _id: 0, history: 1 } } // 仅保留history数组字段
      ],
      as: "tempHistory" // 用临时字段存储lookup结果
    }
  },
  // 提取临时字段中第一个元素的history数组,赋值给目标history字段
  {
    $addFields: {
      history: { $arrayElemAt: ["$tempHistory.history", 0] }
    }
  },
  // 移除临时字段
  {
    $project: { tempHistory: 0 }
  },
  // 处理无匹配记录的情况,将history设为空数组
  {
    $addFields: {
      history: { $ifNull: ["$history", []] }
    }
  }
]);

方案二:兼容多条driverHistory记录的场景(合并所有匹配的驾驶记录)

如果存在一个司机对应多条driverHistory文档的情况,可以用$reduce合并所有history数组:

const transports = await Transport.aggregate([
  {
    $lookup: {
      from: "driverhistories",
      let: { driverId: "$driver" },
      pipeline: [
        { $match: { $expr: { $eq: ["$driver", "$$driverId"] } } },
        { $project: { _id: 0, history: 1 } }
      ],
      as: "tempHistory"
    }
  },
  // 合并所有匹配到的history数组
  {
    $addFields: {
      history: {
        $reduce: {
          input: "$tempHistory",
          initialValue: [],
          in: { $concatArrays: ["$$value", "$$this.history"] }
        }
      }
    }
  },
  { $project: { tempHistory: 0 } }
]);

内容的提问来源于stack exchange,提问作者Vladimir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:57:52