Mongodb 嵌套数组字段上的聚合查找覆盖其他字段

Mongodb Aggregation Lookup on Nested Array Fields overwriting other fields

这是我的 collections 的详细信息,我有 4 collections。

Chat,
ChatUser,
Messages,
User

这些 collections 的字段是,

Chat:
    _id,
    type ('dual', 'group')

ChatUser:
    _id,
    userId (Ref: Users)
    chatId (Ref: Chat)

Messages:
    _id,
    chatId (Ref: Chat)
    fromUserId (Ref: Chat)
    type: ('text', 'media')
    message

Users:
    _id,
    name,
    avatar

我写了一个聚合函数来收集我下面的所有聊天记录。

let chats = await Chat.aggregate([
    {$match: {_id: {$in: chatsOfUser}}},
    {
        $lookup: {
            from: "chatusers",
            let: {cId: "$_id"},
            pipeline: [
                // <-- Added Pipeline
                {
                    $match: {
                        $expr: {
                            $and: [
                                {$eq: ["$chatId", "$$cId"]},
                                {$ne: ["$userId", mongoose.Types.ObjectId(req.user._id)]},
                            ],
                        },
                    },
                },
            ],
            as: "ChatUser",
        },
    },
    {$unwind: "$ChatUser"},
    {$unwind: "$ChatUser.userId"},
    {
        $lookup: {
            from: "users",
            localField: "ChatUser.userId",
            foreignField: "_id",
            as: "Users",
        },
    },
    {$unwind: "$Users"},
    {$project: {"Users.password": 0}},
    {
        $lookup: {
            from: "messages",
            localField: "_id",
            foreignField: "chatId",
            as: "Messages",
        },
    },
    {$sort: {"Messages.createdAt": -1}},
]);

我的实际结果是,

{
    "chats": [
        {
            "_id": "60db388deb35c276cd023f32",
            "type": "dual",
            "ChatUser": {
                "_id": "60db388deb35c276cd023f36",
                "chatId": "60db388deb35c276cd023f32",
                "userId": "60db387feb35c276cd023f1f",
            },
            "Users": {
                "_id": "60db387feb35c276cd023f1f",
                "firstName": "Frodo",
                "lastName": "Baggins",
                "email": "Frodo.Baggins@gmail.com",
                "gender": "male",
            },
            "Messages": [
                {
                    "_id": "60db38f729d01c669696cb9e",
                    "message": "Hello friend",
                    "type": "text",
                    "chatId": "60db388deb35c276cd023f32",
                    "fromUserId": "60daeb5617b93e6cb968582e",// Here I want to populate the User name and avatar into a User Field
                },
                {
                    "_id": "60db38f729d01c669696cb9f",
                    "message": "Hi buddy",
                    "type": "text",
                    "chatId": "60db388deb35c276cd023f32",
                    "fromUserId": "60db387feb35c276cd023f1f", // Here I want to populate the User name and avatar into a User Field
                }
            ]
        },
    ]
}

这里我想在“chats.Messages.User”中填充“fromUserId”的用户详细信息。但是,如果我使用以下查找,它会将消息的所有字段替换为新用户 Object。在排序管道之前使用它。

{
      $lookup: {
          from: "users",
          let: { user_id: "$Messages.fromUserId" },    
          pipeline : [
              { $match: { $expr: { $eq: [ "$_id", "$$user_id" ] } }, },
              { $project : { _id:1, firstName:1 } }
          ],
          as: "Messages.User"
      }
  },

在上述查找之前使用展开管道,

{ "$unwind": "$Messages" },
{ "$unwind": "$Messages.fromUserId" },

但它通过消息展开了整个聊天数组。我真的只想在 'chats.Messages.User' 中提取 fromUserId。有解决办法吗?

解决这个问题的方法有很多种,我选择我认为最简单的方法,不会对现有管道做太多改动。

我会更改消息中的第二个 $lookup 以在其中包含一个嵌套的 $lookup 以获取用户。这样我们就不需要展开整个文档并在之后重组数据。

看起来像这样:

 {
    $lookup: {
      from: "messages",
      let: {
        id: "$_id"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$$id",
                "$chatId"
              ]
            }
          }
        },
        {
          $lookup: {
            from: "users",
            localField: "fromUserId",
            foreignField: "_id",
            as: "User"
          }
        },
        {
          $unwind: "$User"
        },
        {
          $project: {
            "User.password": 0
          }
        },
        
      ],
      as: "Messages",
      
    },
    
  },

完整示例位于:

Mongo playground