带组的猫鼬聚合摆脱了列表顺序

Mongoose Aggregation with group gets rid of list order

我有2个collections.

Collection Name: chats
Chat {
     _id : Object(Id)
     mssg : string
     room : Object Ref (room _id)
     creationDate: DateTimeStamp
}

Collection Name: rooms
Room {
     _id : Object(Id)
     chatMssgs: [Object Ref (Chat_id_1), Object Ref (Chat_id_2), Object Ref (Chat_id_x)]
     roomName: string
     creationDate: DateTimeStamp
}

我正在尝试使用 mongoose 聚合管道按最新聊天消息的顺序对房间进行排序。为此,我正在执行以下操作:

  1. 按最新聊天消息排序(使用创建日期)
  2. 使用 Lookup 获取聊天 mssg 的参考房间
  3. 展开结果的房间数组
  4. 使用 replaceRoot 将房间数组结果作为数组中的主要 objects。
  5. Return 独特的房间列表,不会丢失最新聊天消息日期的排序。

到目前为止,以下代码可以运行到第 4 点。

await Chat.aggregate([
     {$sort: {creationDate: -1}},
     {$lookup: {
                from: "rooms",
                localField: "room",
                foreignField: "_id",
                as: "chat_room"
               }
      },
      {$unwind: "$chat_room"},
      {$replaceRoot: {newRoot: "$chat_room"}},
])

这是我迷路的地方。如果我保持当前的聚合代码不变,它就可以工作,但我的房间 ID 有重复项。这是因为一个房间可以有多个聊天消息。如果我在 replaceRoot 之后使用 $group,我的排序就会消失,最新聊天消息的顺序也会丢失。

理想情况下,我会尝试按最后一条聊天消息对房间进行排序。我使用聚合是因为我还需要使用 addField 对房间后记执行一些计数,并且我对此查询使用分页,因此需要偏移量和限制来保持查询速度相对较快。

return 聊天室最新聊天消息降序排列的最佳方式是什么? 所以房间 1 将有最新的消息,房间 2,...等直到房间 x 显示最早发布的消息。

更新:尝试第一个回复中建议的解决方案后,我得到:

[
  {
    "_id": 3,
    "creationDate": ISODate("2014-01-03T08:00:00Z"),
    "mssg": "ghi",
    "room": 2
  },
  {
    "_id": 2,
    "creationDate": ISODate("2014-01-02T08:00:00Z"),
    "mssg": "def",
    "room": 1
  }
]

这也是我 运行 在我的方法中遇到的问题。如何使输出看起来像这样?

[
      {
        "_id": 2,
         "roomName": "room 2"
        "creationDate": ISODate("2014-01-01T08:00:00Z"),
        "chatMssgs": [3 ],
      },
      {
        "_id": 1,
        "roomName": "room 1"
        "creationDate": ISODate("2014-01-01T08:00:00Z"),
        "chatMssgs": [1,2 ],
      }
    ]

找到 max 日期,然后 filter 它。

db.chats.aggregate([
  {
    "$match": {
      _id: {
        "$in": [
          1,
          2,
          3
        ]
      }
    }
  },
  {
    "$group": {
      "_id": "$room",
      "latestMsgDate": {
        "$max": "$creationDate"
      },
      "Msg": {
        $push: "$$ROOT"
      }
    }
  },
  {
    "$set": {
      "Msg": {
        "$filter": {
          "input": "$Msg",
          "as": "m",
          "cond": {
            "$eq": [
              "$$m.creationDate",
              "$latestMsgDate"
            ]
          }
        }
      }
    }
  },
  {
    "$replaceRoot": {
      "newRoot": {
        "$first": "$Msg"
      }
    }
  },
  {
    "$sort": {
      creationDate: -1
    }
  }
])

mongoplayground

好的。感谢 YuTing 的回答,我能够得到我想要的东西(请参阅有问题的更新部分)。对于任何感兴趣的人:

   db.chats.aggregate([
      {$lookup: {
          from: "rooms",
          localField: "room",
          foreignField: "_id",
          as: "chat_room",
        },
      },
    { $group: {
       _id: "$chat_room",
       latestMsgDate: { $max: "$creationDate" },
       },
     },
    {$sort: { latestMsgDate: -1 }},
{$unwind: "$_id"},
{$replaceRoot: { newRoot: "$_id" }}
    ])

您可以在 mongoplayground 中进行测试。输出为:

[
  {
    "_id": 2,
    "chatMssgs": [
      3
    ],
    "creationDate": ISODate("2014-01-01T08:00:00Z"),
    "roomName": "room 2"
  },
  {
    "_id": 1,
    "chatMssgs": [
      1,
      2
    ],
    "creationDate": ISODate("2014-01-01T08:00:00Z"),
    "roomName": "room 1"
  }
]