Node.js 构建一对一文档间关系MongoDB
我刚开始使用MongoDB,我对如何构造文档以解决以下问题有点迷茫(请注意,这只是一个与我遇到的困难相匹配的基本示例) 问题是: 假设我有一个名为Node.js 构建一对一文档间关系MongoDB,node.js,mongodb,database-design,schema,Node.js,Mongodb,Database Design,Schema,我刚开始使用MongoDB,我对如何构造文档以解决以下问题有点迷茫(请注意,这只是一个与我遇到的困难相匹配的基本示例) 问题是: 假设我有一个名为Family的顶级文档,这些文档存储在名为families的集合中,并包含有关族的一些基本信息,即 { _id: ObjectId("foobar"), familyName: "Simpson", address: "743 Evergreen Terrace, SpringField", chil
Family
的顶级文档,这些文档存储在名为families
的集合中,并包含有关族的一些基本信息,即
{
_id: ObjectId("foobar"),
familyName: "Simpson",
address: "743 Evergreen Terrace, SpringField",
children: [
{
_id: ObjectId("barfoo"),
firstName: "Bartholomew",
preferredName: "Bart",
middleName: "JoJo"
}
// ... etc.
]
}
现在,假设我正在向我的应用程序添加另一个顶级文档:School
,存储在名为schools
的集合中。我需要把每个孩子和他们的学校联系起来,每个孩子在任何时候都只能上一所学校。在Mongo我将如何处理这个问题?我来自一个非常重的RDBMS背景,我很难弄明白这一点。我在解决方案中遇到的主要问题是,我需要有效地处理以下用例:
- 查看一所学校,可以看到所有在那里注册的孩子
- 查看一个家庭并查看其子女就读的所有学校
学校
文档中创建一个注册
数组,该数组引用了一个孩子的\u id
,以及他们的全名以方便使用,即
{
_id: ObjectId("asdadssa"),
name: "Springfield Elementary",
enrollments: [
{
child_id: ObjectId("barfoo"), // Bart Simpson
fullName: "Bart Simpson" // concatenation of preferredName and familyName
}
]
}
这对于第一个用例来说似乎太棒了,它只需要显示在特定学校注册的所有学生
然而,当我转向第二个用例时,我意识到我可能犯了一个错误。你到底怎么知道一个家庭的每个孩子都属于哪所学校?我所能看到的唯一方法是实际遍历学校集合中的每一所学校,深入调查他们的注册人数,看看孩子的id是否与家庭中的一个孩子匹配……这似乎不是很有效率,是吗?这导致了我的下一次尝试
在子对象中存储对学校的引用
因为每个孩子只能属于一所学校,我想我可以在每个孩子子文档中存储一个对学校
文档的引用,即巴特的文档现在变成:
{
_id: ObjectId("barfoo"),
firstName: "Bartholomew",
preferredName: "Bart",
middleName: "JoJo",
school_id: ObjectId("asdadssa")
}
现在,第二个用例令人满意,但第一个用例并不令人满意
结论
我能看到两个用例都得到满足的唯一方法是同时使用两个解决方案,即在子文档中存储学校id
,并在注册
数组中存储儿童id
这对我来说似乎很笨拙,这意味着每次注册变更至少需要写两封信(从学校移除并更改孩子)。据我所知,MongoDB只有原子写入,没有事务支持,所以这看起来像是一个数据完整性可能受到影响的地方
如果任何MongoDB大师能够提出一个替代方案,那将是非常好的。我知道这个特殊的问题真的叫“RDBMS!!!!”,但这只是应用程序的一小部分,而其他一些数据实际上可以用于文档存储
我现在只是在计划阶段,所以我不是100%致力于Mongo,但我想我应该尝试一下,因为我已经听到了一些关于它的好消息。对于您描述的小用例,我将把家庭集合切换到人或学生集合
{
"_id" : ObjectId("barfoo"),
"family_id" : ObjectId("spamneggs"),
"name" : {
"first" : "Bartholomew",
"last" : "Simpson"
},
"school_id" : ObjectId("asdadssa")
}
它将学生存储为单独的文档,但将他们与一个共同的家庭id
(我还以另一种方式偷偷地存储姓名)联系起来。学校可以只拥有学校信息而不需要注册。我将在mongo shell中为您的两个用例提供示例代码。要查找所有在巴特学校注册的儿童和巴特学校的文件:
> db.students.find({ "school_id" : ObjectId("asdadssa") })
> db.schools.find({ "_id" : ObjectId("asdadssa") })
要查找巴特家庭子女就读的所有学校:
> var schools = []
> db.students.find({ "family_id" : ObjectId("spamneggs") }, { "_id" : 0, "school_id" : 1 }).forEach(function(doc) {
schools.push(doc.school_id)
})
> db.schools.find({ "_id" : { "$in" : schools } })
这两个都是简单的应用程序端连接,在您的情况下应该可以很好地工作,因为一个家庭不会有无数的孩子。有关学校id
和家庭id
的索引将有所帮助
对于写入,只需使用正确的学校id
更新学生文档