我一直在深入研究 MongoDB,发现了一种特别有趣的模式,用于存储文档之间的关系。此模式涉及包含引用子文档的 id 数组的父文档,如下所示:
//Parent Schema
export interface Post extends mongoose.Document {
content: string;
dateCreated: string;
comments: Comment[];
}
let postSchema = new mongoose.Schema({
content: {
type: String,
required: true
},
dateCreated: {
type: String,
required: true
},
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }] //nested array of child reference ids
});
被引用的 child :
//Child Schema
export interface Comment extends mongoose.Document {
content: string;
dateCreated: string;
}
let commentSchema = new mongoose.Schema({
content: {
type: String,
required: true
},
dateCreated: {
type: String,
required: true
}
});
在我从前端发送创建新评论的请求之前,这一切看起来都很好。请求必须包含 Post _id(更新帖子)和新 Comment,这对于使用普通关系数据库时发送的请求来说都是常见的。将新 Comment 写入数据库时会出现此问题。而不是一个数据库写入,就像你在普通关系数据库中所做的那样,我必须做 2 次写入和 1 次读取。第一个写入插入新 Comment 并检索 _id。然后通过随请求发送的 Post _id 读取以检索 Post,这样我就可以将新的 Comment _id 推送到嵌套引用数组。最后,最后一次写入将 Post 更新回数据库。
这看起来效率极低。我的问题有两个:
是否有更好/更有效的方法来处理这种关系模式(包含子引用 ID 数组的父项)?
如果不是,与 A) 将父 _id 存储在类似于传统外键的子属性中,或 B) 利用 MongoDB 文档并存储一组评论,而不是一组评论的引用 ID。
预先感谢您的见解!
最佳答案
关于您的第一个问题:
您特别要求一种更好的方法来处理存储在父级中的子 ID。如果必须采用这种模式,我很确定没有更好的方法来处理这个问题。
但是这个问题也存在于关系型数据库中。如果您想将帖子保存在关系数据库中(使用该模式),您还必须首先创建评论,获取其 ID,然后更新帖子。当然,您可以在单个请求中发送所有这些任务,这可能比使用 Mongoose 更有效,但需要完成的工作类型是相同的。
关于你的第二个问题:
变体 A 的好处是,例如,您可以获得该帖子,并立即知道它有多少评论,而无需要求 mongodb 浏览可能成百上千的文档。
与变体 B 相比的好处是,您可以在单个文档(单个帖子)中存储更多对评论的引用,而不是整个评论,因为 mongos 16MB文档大小限制。
然而,缺点是您提到的缺点,即维护该结构效率低下。我认为这只是展示场景的示例,所以我会这样做: 我会根据具体情况决定使用什么。
如果文档将被大量阅读,而不是被写入,并且它不太可能增长到超过 16MB:嵌入子文档。这样您就可以在单个查询中获取所有数据。
如果您需要从多个其他文档中引用该文档并且您的数据确实必须一致,那么您别无选择,只能引用它。
如果您需要从多个 其他文档中引用该文档但是数据一致性不是那么重要和限制从第一个要点应用,然后嵌入子文档,并编写代码以保持数据一致。
如果您需要从多个 其他文档中引用该文档,并且它们被写入了很多,但并不经常阅读,那么您最好引用它们,因为这个更易于编码,因为您无需编写代码来同步重复数据。
在这种特定情况下(帖子/评论)从子项引用父项(让子项知道父项 _id
)可能是个好主意,因为它比其他方式更容易维护, 如果直接嵌入,文档可能会超过 16MB。如果我确定文档不会超过 16MB,那么嵌入它们会更好,因为这样查询数据会更快
关于javascript - MongoDB 和 Mongoose : Nested Array of Document Reference IDs,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42719978/