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

Cod*_*key 2 database-design mongoose mongodb

具体来说,我正在考虑一个聊天应用程序,其中 aUser有很多Chats,每个Chat都有很多Messages。我将如何表达这种Chat --has-Many--> Message关系?

最初,我以为我会将MessageID 引用列表存储在上Chat,例如Chat文档可能看起来像

{
  _id: ObjectId('507f191e810c19729de860d5'),
  title: 'Jack V, Kyle R, Sam P',
  messages: [ObjectId('507f191e810c19729de860ea'), ...],
  createdAt: 1546284204867
}
Run Code Online (Sandbox Code Playgroud)

这样,$push每次发送消息或$pull删除消息时,我都需要访问该数组。使代码更加复杂和晦涩,但仍然可行。

然后我从MongoDB 文档中读到,这种方法只有在数组很小并且增长有限时才有效。为了避免可变的、不断增长的数组,最好将refMessage保留chatId回其父级Chat

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

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

const chatId = '507f191e810c19729de860d5'

// 1. through IDs array
Chat.findById(chatId).populate('messages').execPopulate()

// or

// 2. through ID ref
Message.find({ chatId })
Run Code Online (Sandbox Code Playgroud)

根据我的理解,

  • 1st 效率更高,b/c 它需要一组 ID 并快速定位Message文档,然后读取它们
  • 第二个效率较低,b/c 它必须读取所有 Message文档(可能是数百万)并比较chatId每个文档的属性

这与在 MongoDB中读取写入更昂贵的事实一致。我的说法正确吗?如果是这样,为什么大多数资源都推荐方法#2?我在MDN 文档(见黄框)、MongoDB 开发人员的 50 条提示和技巧以及 MongoDB 文档中看到了它。

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

B. *_*ing 6

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

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

因此,就潜在的功能、性能、文档大小限制,甚至只是易于管理而言,将Message父级引用到其对应的单独文档Chat是首选方法。