我一直在深入研究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数组。
提前感谢您的见解!
答案 0 :(得分:3)
关于你的第一个问题:
您特别要求使用更好的方法来处理存储在父级中的子ID。我很确定没有更好的方法来解决这个问题,如果它必须是这种模式的话。
但是这个问题也存在于关系数据库中。如果要将帖子保存在关系数据库中(使用该模式),还必须先创建注释,获取其ID,然后更新帖子。当然,您可以在一个请求中发送所有这些任务,这可能比使用mongoose更有效,但需要完成的工作类型是相同的。
关于你的第二个问题:
对变体A的好处是,您可以获得帖子,并立即知道它有多少评论,而不要求mongodb通过可能的文档。
对于变体B的好处是,您可以在单个文档(单个帖子)中存储更多引用,而不是整个注释,因为mongos 16MB文件大小限制。
然而,您所提到的那个下行,维持该结构是低效的。我认为,这只是展示场景的一个例子,所以这就是我要做的: 我会根据具体情况决定使用什么。
如果文档会被大量阅读,而且写得不多, AND 则不大可能超过16MB:嵌入子文档。这样,您就可以在一个查询中获取所有数据。
如果您需要从多个其他文档 AND 引用该文档,您的数据必须一致,那么您别无选择,只能引用它。< / p>
如果您需要从多个其他文档中引用该文档但数据符号不是那么超重 AND 的限制从第一个要点应用,然后嵌入子文档,并编写代码以保持数据的一致性。
如果您需要从多个其他文档中引用该文档,并且它们被写入很多,但不经常阅读,那么您可能最好不要引用它们,因为这样更容易编码,因为您不需要编写代码来同步重复数据。
在这个特定情况下(帖子/评论)引用孩子的父母(让孩子知道父母_id
)可能是一个好主意,因为它比其他方式更容易维护,并且如果直接嵌入文档,则文档可能会大于16MB。如果我肯定知道,文档不会超过16MB,嵌入它们会更好,因为以这种方式查询数据的速度更快