【问题标题】:MongoDB aggregation pipeline for dating / chat app用于约会/聊天应用程序的 MongoDB 聚合管道
【发布时间】:2020-09-07 05:22:02
【问题描述】:

我正在开发一个带有 Node.JS / MongoDB 后端的 MERN 堆栈 Web 应用程序。

我正在构建一个聊天收件箱,用于显示用户最近的对话,并预览该对话中的最新消息。

我正在尝试创建一个 MongoDB 聚合,以查找 12 个最近的“对话”并返回该对话中的最新聊天并附加关联的用户。

管道涉及两个数据库集合:

  • 用户集合(每个用户的文档)
  • 聊天集合(发送的每条消息的文档)

聚合管道的目标是返回最近的聊天(发送或接收)并附加来自用户集合的关联用户

聊天模式的结构如下:

{ 
sender: (User Document ID of the sender),
receiver: (User Document ID of the receiver),
timestamp: (Date Message Was Sent)
}

这是我迄今为止构建的管道。注意:userid 变量是请求用户的 ObjectID。

 var findUserMessages = await Chat.aggregate([
{
  $match: {
    receiver: userid,
  },
},
{ $sort: { createdAt: -1 } },
{
  $group: {
    _id: "$sender",
    createdAt: { $first: "$createdAt" },
    isRead: { $first: "$isRead" },
    chat_id: { $first: "$_id" },
    message: { $first: "$message" },
  },
},
{
  $project: {
    _id: "$chat_id",
    createdAt: "$createdAt",
    isRead: "$isRead",
    senderId: "$_id",
    message: 1,
  },
},
{
  $lookup: {
    from: "users",
    localField: "senderId",
    foreignField: "_id",
    as: "user",
  },
},
{ $unwind: "$user" },
{ $sort: { createdAt: -1 } },
{ $skip: skip },
{ $limit: 12 },
]);

问题是这个聚合返回用户 ID 是接收者的最新消息。我需要它返回用户是发送者或接收者的最新消息。

我在比赛阶段试过这个:

  $match: {
    $or: [{ sender: userid }, { receiver: userid }],
  },

但是在小组赛阶段,我相信我现在需要根据用户是发送者还是接收者来确定 ID。如果用户是发送者,则组 ID 应该是接收者,反之亦然。

 {
  $group: {
    _id: "$sender",
    createdAt: { $first: "$createdAt" },
    isRead: { $first: "$isRead" },
    chat_id: { $first: "$_id" },
    message: { $first: "$message" },
  },
},

这就是我卡住的地方。我尝试聘请了几位代码导师,但没有人能够解决这个问题。

目标:应该从每个对话中返回最近的聊天(数量:1),按最近的排序,并附有相关的用户

  • 第一步:查找用户是发件人或收件人的所有聊天记录
  • 第二步:按最近排序
  • 第三步:按对话分组
  • 第四步:通过对话获取最近的聊天记录
  • 第五步:找到关联的其他用户并放松

【问题讨论】:

  • 你的问题太大了。如果你能把它分成几个问题,那就更好了。
  • 我建议您应该单独存储必要的数据以帮助查询。如果您的用例是聊天应用程序。您可能有太多的数据和流量来进行如此复杂的聚合
  • 我在您的聚合中看到您使用了架构中不存在的其他字段,请同时将它们包含在架构中以供参考。

标签: node.js reactjs mongodb aggregation-framework


【解决方案1】:

正如你在这里提到的

但是在小组赛阶段,我相信我现在需要根据用户是发送者还是接收者来确定 ID。如果用户是发送者,则组 ID 应该是接收者,反之亦然。

关键是使用$cond根据条件设置一个新字段

[{
  $match: {
    $or: [{ sender: userid }, { receiver: userid }],
  }
}, {
  $addFields: { // set friendId as receiver, if user is sender and vice versa
    friendId: { $cond: [{ $eq: ['$sender', userId] }, '$receiver', '$sender' ] }
  }
}, {
  $sort: { createdAt: -1 } // you have timestamp in the schema but using createdAt, I'll keep it like in your aggregation
}, {
  $group: {
    _id: '$friendId', // use the new field to group
    createdAt: { $first: "$createdAt" },
    isRead: { $first: "$isRead" },
    chatId: { $first: "$_id" },
    message: { $first: "$message" },
  }
}, {
  $addFields: {
    _id: '$chatId',
    friendId: '$_id',
  }
}, {
  $lookup: {
    from: "users",
    localField: "$friendId",
    foreignField: "_id",
    as: "user",
  },
}, {
  $unwind: '$user'
}, {
  $sort: { createdAt: -1 }
}, {
  $skip: skip
}, { 
  $limit: 12
}]

正如我在对您的问题的评论中提到的,最好单独保留有用的信息以帮助查询。例如,您可以拥有另一个跟踪“讨论”的集合,该集合在每对发送者和接收者中都是唯一的,您甚至可以在讨论集合中添加最近的消息和时间戳。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    相关资源
    最近更新 更多