【问题标题】:Mongoose Private Chat Message Model猫鼬私聊消息模型
【发布时间】:2014-11-14 18:35:43
【问题描述】:

我正在尝试将用户之间的私人消息添加到我的数据模型中。我一直在两种可能的方式之间来回走动。

1) 每个用户都有一组 user_id、chat_id 对,它们对应于他们正在参与的聊天。聊天模型只存储 chat_id 和消息数组。

2) 根本不存储与用户的聊天,只需让聊天模型存储一对 user_ids 和消息数组。

选项 (1) 的问题是,每当用户加入或开始聊天时,我需要先查看用户数组以查看 user_id、chat_id 对是否已经存在。然后在 Chat 中再次查找 chat_id。如果它不存在,我需要在两个不同的地方为参与的两个用户创建 user_id、chat_id 对。

使用选项 (2),我将在 Chat 模型中搜索 user_id1、user_id2 对,如果找到,我就完成了,如果没有,我将为该对创建一个新的 Chat 记录并完成。

基于此选项 (2) 似乎是更好的处理方式。但是,我在弄清楚如何以在聊天模型中易于搜索的方式对“对”用户 ID 进行建模时遇到了问题。即,即使 user_ids 以错误的顺序传递,即 user_id2,user_id1,我如何确保我可以找到聊天记录。在 Mongoose 中进行建模的最佳方法是什么?

var chatSchema = mongoose.Schema({

  messages: [{
        text: { 
          type: String,
          max: 2000
        },
        sender: { 
          type: mongoose.Schema.Types.ObjectId, 
          ref: 'User'
        }
      }],
  participant1: [{                
          type: mongoose.Schema.Types.ObjectId, 
          ref: 'User'
        }]
  participant2: [{                
          type: mongoose.Schema.Types.ObjectId, 
          ref: 'User'
        }]
});

如果是上述情况,我将如何搜索参与者对?我能否以某种方式对参与者 ID 进行排序,以便它们始终是参与者 1

【问题讨论】:

  • 我想你会想要发件人你有它,然后而不是做participant1participant2,等等。只做participants: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],这会给你一个用户数组,然后要进行搜索,你会做这样的事情Chat.find({ participants: { $in: { "array of names" }}, function (err, participants) { }
  • @gmaniac 谢谢!我想我最终会做一些与你建议的非常相似的事情。 $in 不起作用,因为 $or 但我认为 $all 确实适用于此。
  • 很高兴能帮上忙!如果您想更新您的问题,我们可以进一步为您提供帮助,或者如果您找到了您正在寻找的内容,请发布答案并接受它。

标签: node.js mongodb mongoose


【解决方案1】:

嗯,这个问题没有正确答案,但可以肯定的是,你提到的方法根本不是最好的!

首先,当你考虑设计一个“聊天”模型时,你需要考虑到用户之间会有数百万条消息,所以当你想要获取聊天。

将消息存储到数组中根本不是一个好主意,您的模型的大小到那时会很大,您必须考虑到 MongoDB 的文档大小限制目前是每个文档 16 MB。

https://docs.mongodb.com/manual/reference/limits/

其次,您必须考虑分页方面,因为它会在聊天量很大时影响性能,当您检索两个用户之间的聊天时,您不会从一开始就请求所有聊天,您只会请求最近的,然后如果用户滚动聊天,你可以请求较旧的,这方面非常重要,不能忽视,因为它对性能的影响。

我的方法是将每条消息存储在一个单独的文档中

首先,将每条消息存储在单个文档中会提高您在获取聊天时的性能,并且文档大小会非常小。

这是一个很简单的例子,你需要根据自己的需要改变模型,它只是为了表达想法:

const MessageSchema = mongoose.Schema({
    message:{
        text: { type:String, required:true }
        // you can add any other properties to the message here.
        // for example, the message can be an image ! so you need to tweak this a little
    }
    // if you want to make a group chat, you can have more than 2 users in this array
    users:[{
        user: { type:mongoose.Schema.Types.ObjectId, ref:'User', required:true }
    }]
    sender: { type:mongoose.Schema.Types.ObjectId, ref:'User', required:true },
    read: { type:Date }
},
{
    timestamps: true
});

您可以通过此查询获取聊天记录:

 Message.find(({ users: { "$in" : [#user1#,#user2#]} })
    .sort({ updatedAt: -1 })
    .limit(20)

简单干净! 如您所见,使用这种方法进行分页变得非常容易。

【讨论】:

  • 您应该为对话创建一个新集合。在您的架构中,有数据重复。假设它是一个由 100 个成员组成的组。所以每个文档都包含 ObjectId(User) 的 100 个成员的数组。这些愚蠢的数据会更快地填满你的磁盘空间!
【解决方案2】:

一些建议。

首先 - 为什么将 Participant1 和 2 存储为数组?有一个特定的发件人和一个(或多个)收件人(取决于您是否需要群组消息)。

考虑以下架构:

var ChatSchema = new Schema({
    sender : {
        type : mongoose.Schema.Types.ObjectId,
        ref : 'User'
    },
    messages : [
        {
            message : String,
            meta : [
                {
                    user : {
                        type : mongoose.Schema.Types.ObjectId,
                        ref : 'User'
                    },
                    delivered : Boolean,
                    read : Boolean
                }
            ]
        }
    ],
    is_group_message : { type : Boolean, default : false },
    participants : [
        {
            user :  {
                type : mongoose.Schema.Types.ObjectId,
                ref : 'User'
            },
            delivered : Boolean,
            read : Boolean,
            last_seen : Date
        }
    ]
});

此架构允许一个聊天文档存储所有消息、所有参与者以及与每条消息和每个参与者相关的所有状态。

布尔 is_group_message 只是一种更短的过滤直接/组消息的方法,可能用于客户端查看或服务器端处理。直接消息显然更容易处理查询,但两者都非常简单。

meta 数组列出了单个消息的每个参与者的传递/读取状态等。如果我们不处理组消息,这不需要是一个数组,但我们是,所以没关系。

主文档(不是元子文档)上的 deliveredread 属性也只是判断最后一条消息是否已传递/未读的简写方式。它们会在每次写入文档时更新。

此架构允许我们将有关聊天的所有内容存储在一个文档中。甚至是群聊。

【讨论】:

  • 使用这种方式会使聊天的分页效率低下
  • @Maysara 我已经基于这种方法为生产聊天服务实现了一个更复杂的数据模式,如果需要,我可以花一些时间分享这个过程。任何选择都需要权衡取舍。将每条消息单独存储然后聚合它们似乎是另一种选择,这对我来说没有多大意义。反向分页在任何聊天系统中都比较少见,当它发生时,分页到这种实际上变得低效的程度真的非常罕见。
  • @AugieGardner,我正在建立一个聊天,但是 atm,我有一个对话模式和消息模式,它们具有对话的 ID。您的架构看起来非常好,并且可以防止来自 MongoDB 的多个查询。你能告诉我你所说的“更复杂”的模式吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-05
  • 2018-03-04
  • 2018-07-15
  • 2014-12-09
  • 2018-12-23
  • 2018-06-23
  • 2020-09-05
相关资源
最近更新 更多