我一直在深入研究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 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,它们对于使用普通关系数据库时发送的请求都是通用的 . 当需要将新注释写入数据库时,会出现此问题 . 而不是像在普通关系数据库中那样进行一次数据库写操作,我必须进行2次写入和1次读取 . 第一次写入以插入新的Comment并检索_id . 然后通过Post _id检索发送请求的读取,以便我可以将新的Comment _id推送到嵌套的引用数组 . 最后,最后一次写入将Post更新回数据库 .
这似乎效率极低 . 我的问题是双重的:
-
有没有更好/更有效的方法来处理这种关系模式(包含子引用ID数组的父级)?
-
如果没有,使用此模式的好处是什么,而不是A)将父_id存储在子类的属性中,类似于传统的外键,或者B)利用MongoDB文档并存储注释数组而不是评论的参考ID数组 .
提前感谢您的见解!
1 回答
关于你的第一个问题:
您特别要求更好的方法来处理存储在父级中的子ID . 如果它必须是这种模式,我很确定没有更好的方法来解决这个问题 .
但是这个问题也存在于关系数据库中 . 如果要将帖子保存在关系数据库中(使用该模式),还必须先创建注释,获取其ID,然后更新帖子 . 当然,您可以在单个请求中发送所有这些任务,这可能比使用mongoose更有效,但需要完成的工作类型是相同的 .
关于你的第二个问题:
变体A的好处是,你可以获得帖子,并立即知道它有多少评论,而不要求mongodb通过可能的文件 .
对于变体B的好处是,由于mongos 16MB文档大小限制,您可以在单个文档(单个帖子)中存储更多 references ,而不是 whole 注释 .
然而,下行是您提到的那个,维持该结构是低效的 . 我认为,这只是展示场景的一个例子,所以这就是我要做的:我会根据具体情况决定使用什么 .
如果文档将被大量阅读,并且没有多少写入, AND 它不可能大于16MB:嵌入子文档 . 这样,您就可以在单个查询中获取所有数据 .
如果您需要从 multiple 其他文档 AND 引用该文档,您的数据必须一致,那么您别无选择,只能引用它 .
如果需要从 multiple 引用该文档 BUT data-consitency不是那么超级重要 AND 来自第一个要点的限制适用,然后嵌入子文档,并编写代码以保持数据的一致性 .
如果您需要从 multiple 其他文档中引用该文档,并且它们被写入很多,但不经常读取,那么您需要编写代码来同步重复数据 .
在这个特定的情况下(帖子/评论)引用孩子的父母(让孩子知道父母
_id
)可能是一个好主意,因为它确实知道文件不会超过16MB,嵌入它们会更好,因为以这种方式查询数据的速度更快