Node.js 构建一对一文档间关系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

我刚开始使用MongoDB,我对如何构造文档以解决以下问题有点迷茫(请注意,这只是一个与我遇到的困难相匹配的基本示例)

问题是: 假设我有一个名为
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
更新学生文档