MongoDB一对多-子模型上的ID数组或ID引用?

时间:2018-12-31 19:47:35

标签: mongodb mongoose database-design

具体地说,我正在考虑一个聊天应用程序,其中一个User有很多Chat,每个Chat有很多Message。我将如何表达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中获悉,这种方法仅在数组为 small 且增长限制为 时才成立。 为避免可变的,不断增长的数组,最好将Message保留返回其父chatId的{​​{1}}引用

Chat

最后,如果我想获取与猫鼬聊天的消息,

{
  _id: ObjectId('507f191e810c19729de860ea'),
  body: 'Hey Kyle! Mind if I ask ya a favor?',
  chatId: ObjectId('507f191e810c19729de860d5'),
  createdAt: 1546284204869
}

据我了解

  • 1st效率更高,b / c需要一个ID数组并快速找到const chatId = '507f191e810c19729de860d5' // 1. through IDs array Chat.findById(chatId).populate('messages').execPopulate() // or // 2. through ID ref Message.find({ chatId }) 文档,然后只有这样才能读取它们
  • 2nd的效率较低,因为bem只能读取所有 Message个文档(可能是数百万个),并比较Message属性每个

这与MongoDB中的 reads 相比 writes 昂贵得多的事实保持一致。我说对了吗?如果是这样,为什么大多数资源推荐方法2?我在MDN docs(见黄框), MongoDB开发人员的50个技巧和窍门和MongoDB文档中看到了它。

对于一对多关系,在父模型上维护ID数组还是在每个子模型上都具有ID引用更好?

1 个答案:

答案 0 :(得分:3)

更新写入实际上非常昂贵。插入新文档的速度很快,但是更新要花一些时间,因为您要执行读写入操作。如果O(r)是“读取”的时间复杂度,而O(w)是“写入”的时间复杂度,则更新为O(r+w)。实际上,如果您在要查询的字段上建立索引,则读取实际上也非常有效,因此通常不必担心。遵循的一般建议是将更新保持在最低限度,而读和插入都可以,尽管只要索引良好,这些操作实际上都不是问题。

此外,我不建议您将Message规范化为Chat文档。文档的大小上限为16MB,因此,如果聊天变得特别大,则MongoDB将无法处理。即使它从未超过该限制,也无法优化消息检索-每次要加载聊天时,您都需要一次抓取所有消息,但是在大多数实际情况下,您只需要检索例如最后几十条消息,并根据需要加载更多!此外,将您的邮件保留为单独的文档将使您可以执行其他有用的任务,例如搜索和仅显示特定人员发送的邮件,跳过某些时间点,清除早于给定日期的所有文档,创建TTL索引以自动删除旧邮件等。

因此,从潜在的功能,性能,文档大小的限制甚至是易于管理的角度来看,首选的方法是使用单独的Message文档,并在其父文档中引用其相应的Chat