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

标签 mongodb mongoose database-design

具体来说,我正在考虑一个聊天应用程序,其中 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/

相关文章:

javascript - 如何从 JSON 对象中删除 __proto__ 属性?

javascript - Meteor 响应式(Reactive)搜索栏在未聚焦时显示所有结果

在两个文档上写入时 Mongodb 在副本集上的最终一致性

node.js - 使用 mongoose 获取子文档的 Node api

mysql - SQL schema优化,如何避免union?

mysql - 专辑轨道的数据库格式

python - 插入 mongodb (pymongo) 时的效率

node.js - 修改 mongoose pre-validate hook 中的数据

javascript - Node.js 检测两个 Mongoose 查找何时完成

MySQL:如何转换为 EAV - 第 2 部分?