具体来说,我正在考虑一个聊天应用程序,其中 User
有很多Chat
s 和每个 Chat
有很多Message
s。我该如何表达Chat --has-Many--> Message
关系?
最初,我想存储 Message
的列表ID 引用Chat
,例如一个Chat
文档可能类似于
{
_id: ObjectId('507f191e810c19729de860d5'),
title: 'Jack V, Kyle R, Sam P',
messages: [ObjectId('507f191e810c19729de860ea'), ...],
createdAt: 1546284204867
}
这样,我需要 $push
每次发送消息时都写入数组,或 $pull
如果被删除了。使代码更加复杂和晦涩,但仍然可行。
然后我读到 MongoDB docs这种方法仅在数组较小且增长有限时才有效。 为了避免可变的、不断增长的数组,最好 Message
保留chatId
引用回其父级 Chat
{
_id: ObjectId('507f191e810c19729de860ea'),
body: 'Hey Kyle! Mind if I ask ya a favor?',
chatId: ObjectId('507f191e810c19729de860d5'),
createdAt: 1546284204869
}
最后,如果我想获取与 Mongoose 聊天的消息,
const chatId = '507f191e810c19729de860d5'
// 1. through IDs array
Chat.findById(chatId).populate('messages').execPopulate()
// or
// 2. through ID ref
Message.find({ chatId })
据我了解,
- 第一个效率更高,b/c 它需要一个 ID 数组并快速找到
Message
文档,只有这样才会读取它们 - 第二个效率较低,因为它必须读取全部
Message
文档(可能数百万)并比较chatId
每个属性
这与 MongoDB 中读取比写入昂贵得多这一事实相符。我的说法正确吗?如果是这样,为什么大多数资源都推荐方法#2?我在MDN docs中看到它(请参阅黄色框),MongoDB 开发人员的 50 个提示和技巧以及 MongoDB 文档。
对于一对多关系,是在父模型上维护一个 ID 数组更好,还是在每个子模型上都有一个 ID 引用更好?
最佳答案
更新写入实际上非常昂贵。插入新文档很快,但更新需要一些时间,因为您执行读取和写入。如果O(r)
是“读取”的时间复杂度和 O(w)
是“写入”的时间复杂度,则更新为O(r+w)
。无论如何,如果您在正在查询的字段上构建了索引,那么读取实际上也非常高效,因此通常不需要担心。遵循的一般建议是将更新保持在最低限度,而读取和插入则很好,尽管只要索引良好,这些操作都不是真正的问题。
除此之外,我不建议对您的 Message
进行非规范化进入Chat
文档。文档大小上限为 16MB,因此如果聊天内容变得特别大,那么 MongoDB 将无法处理。即使它永远不会超过该限制,您也无法优化消息检索 - 任何时候您想要加载聊天,您都需要一次获取所有消息,但在最现实的场景中,您只需要检索例如最后几十条消息并根据需要加载更多消息!此外,将消息保存为单独的文档将允许您执行其他有用的任务,例如搜索并仅显示特定人员发送的消息、跳到某些时间点、清除早于给定日期的所有文档、创建 TTL索引以自动删除旧消息等。
因此,就潜在功能、性能、文档大小限制,甚至只是易于管理而言,具有单独的 Message
父级引用其相应 Chat
的文档是首选方法。
关于MongoDB 一对多 - 子模型上的 ID 数组或 ID 引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53990956/