【问题标题】:Structuring one-to-one interdocument relationship MongoDB构建一对一的文档间关系MongoDB
【发布时间】:2014-12-24 15:53:24
【问题描述】:

我刚开始使用 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 背景,我很难弄清楚这一点。我在解决方案中遇到的主要问题围绕着我需要有效处理以下用例:

  • 查看一所学校并能够查看那里注册的所有儿童
  • 查看一个家庭并查看其子女就读的所有学校

我尝试过的:

将子引用存储在 `School` 中

我采用的第一个解决方案是在我的School 文档中创建一个enrollments 数组,该数组引用了一个孩子的_id 以及他们的全名以方便起见,即

{
    _id:         ObjectId("asdadssa"),
    name:        "Springfield Elementary",
    enrollments: [
        {
            child_id: ObjectId("barfoo"), // Bart Simpson
            fullName: "Bart Simpson" // concatenation of preferredName and familyName
        }
    ]
}

这对于第一个用例来说似乎很棒,它只需要显示在特定学校注册的所有学生。

但是,当我转向第二个用例时,我意识到我可能犯了一个错误。您到底如何确定Family 中的每个孩子属于哪所学校?我能看到的唯一方法是实际遍历schools 集合中的每一所学校,深入到他们的enrollments 并查看 child_id 是否与家庭中的孩子匹配......似乎不是很有效?这导致了我的下一次尝试。

在子对象中存储对学校的引用

因为每个孩子只能属于一所学校,我想我可以只在每个孩子的子文档中存储对 School 文档的引用,即 Bart 的文档现在将变为:

{
    _id:           ObjectId("barfoo"),
    firstName:     "Bartholomew",
    preferredName: "Bart",
    middleName:    "JoJo",
    school_id:     ObjectId("asdadssa")
}

现在第二个用例很满意,但第一个不满意。

结论

我可以看到两个用例都得到满足的唯一方法是,如果我同时使用这两种解决方案,即将school_id 存储在子子文档中,并将child_id 存储在enrollments 数组中。

这对我来说似乎很笨拙,这意味着每次注册更改您至少需要执行两次写入(从学校移除并更改孩子)。据我所知,MongoDB 只有原子写入,没有事务支持,所以这看起来像是数据完整性可能受到影响的地方。

如果任何 MongoDB 专家可以提出替代解决方案,那就太好了。我知道这个特殊的问题真的叫“RDBMS !!!”,但这只是应用程序的一小部分,其他一些数据确实适合文档存储。

我现在只处于计划阶段,所以我不是 100% 致力于 Mongo,但我想我会试一试,因为我听到了一些关于它的好消息。

【问题讨论】:

    标签: node.js mongodb database-design schema


    【解决方案1】:

    对于您描述的小型用例,我会将families 集合切换为peoplestudents 集合

    {
        "_id" : ObjectId("barfoo"),
        "family_id" : ObjectId("spamneggs"),
        "name" : {
            "first" : "Bartholomew",
            "last" : "Simpson"
        },
        "school_id" : ObjectId("asdadssa")
    }
    

    将学生存储为单独的文档,但将它们与一个共同的 family_id 联合起来(我还偷偷用另一种方式来存储姓名)。学校可以只拥有学校信息而无需注册。我将在 mongo shell 中为您的两个用例提供示例代码。要查找在 Bart 学校注册的所有孩子和 Bart 学校的文档:

    > db.students.find({ "school_id" : ObjectId("asdadssa") })
    > db.schools.find({ "_id" : ObjectId("asdadssa") })
    

    要查找 Bart 家有孩子就读的所有学校:

    > 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 } })
    

    这两个都是简单的应用程序端联接,在您的情况下应该可以正常工作,因为一个家庭不会有数以亿计的孩子。 school_idfamily_id 上的索引会有所帮助。

    对于写入,只有学生文档需要使用正确的school_id 进行更新。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-25
      相关资源
      最近更新 更多