Docs 菜单
Docs 主页
/
MongoDB Manual
/ / /

使用嵌入式文档建立一对多关系模型

在此页面上

  • Overview
  • 嵌入式文档模式
  • 子集模式

本页描述的数据模型使用嵌入式文档描述连接数据之间的一对多关系。在单个文档中嵌入连接的数据可以减少获取数据所需的读取操作次数。一般来说,您应该构建模式,以便应用程序在单个读取操作中接收所需的所有信息。

请考虑以下映射客户和多个地址关系的示例。 该示例说明了如果您需要在另一个数据实体的上下文中查看多个数据实体,则嵌入相对于引用的优势。 在 patronaddress数据之间的这种一对多关系中, patron具有多个address实体。

在规范化Realm 数据模型中, address文档包含对patron文档的引用。

// patron document
{
_id: "joe",
name: "Joe Bookreader"
}
// address documents
{
patron_id: "joe", // reference to patron document
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
{
patron_id: "joe",
street: "1 Some Other Street",
city: "Boston",
state: "MA",
zip: "12345"
}

如果您的应用程序经常检索带有name信息的address数据,则您的应用程序需要发出多个查询来解析引用。 更优化的模式是将address数据实体嵌入到patron数据中,如以下文档所示:

{
"_id": "joe",
"name": "Joe Bookreader",
"addresses": [
{
"street": "123 Fake Street",
"city": "Faketon",
"state": "MA",
"zip": "12345"
},
{
"street": "1 Some Other Street",
"city": "Boston",
"state": "MA",
"zip": "12345"
}
]
}

借助嵌入式数据模型,应用程序可以通过一次查询检索完整的客户信息。

嵌入式文档模式的一个潜在问题是,它可能会导致文档过大,在嵌入式字段没有限制的情况下尤其如此。在这种情况下,您可以使用子集模式仅访问应用程序所需的数据,而不是访问整个嵌入数据集。

考虑一个包含产品评论列表的电商站点:

{
"_id": 1,
"name": "Super Widget",
"description": "This is the most useful item in your toolbox.",
"price": { "value": NumberDecimal("119.99"), "currency": "USD" },
"reviews": [
{
"review_id": 786,
"review_author": "Kristina",
"review_text": "This is indeed an amazing widget.",
"published_date": ISODate("2019-02-18")
},
{
"review_id": 785,
"review_author": "Trina",
"review_text": "Nice product. Slow shipping.",
"published_date": ISODate("2019-02-17")
},
...
{
"review_id": 1,
"review_author": "Hans",
"review_text": "Meh, it's okay.",
"published_date": ISODate("2017-12-06")
}
]
}

评论按时间倒序排列。用户访问产品页面时,应用程序会加载最近十条评论。

您可以将该集合拆分为两个集合,而不存储该产品的所有评论:

  • product collection 存储每个产品的信息,包括该产品的 10 条最新评论:

    {
    "_id": 1,
    "name": "Super Widget",
    "description": "This is the most useful item in your toolbox.",
    "price": { "value": NumberDecimal("119.99"), "currency": "USD" },
    "reviews": [
    {
    "review_id": 786,
    "review_author": "Kristina",
    "review_text": "This is indeed an amazing widget.",
    "published_date": ISODate("2019-02-18")
    }
    ...
    {
    "review_id": 777,
    "review_author": "Pablo",
    "review_text": "Amazing!",
    "published_date": ISODate("2019-02-16")
    }
    ]
    }
  • review collection 存储所有评论。每条评论都包含对相应产品的引用。

    {
    "review_id": 786,
    "product_id": 1,
    "review_author": "Kristina",
    "review_text": "This is indeed an amazing widget.",
    "published_date": ISODate("2019-02-18")
    }
    {
    "review_id": 785,
    "product_id": 1,
    "review_author": "Trina",
    "review_text": "Nice product. Slow shipping.",
    "published_date": ISODate("2019-02-17")
    }
    ...
    {
    "review_id": 1,
    "product_id": 1,
    "review_author": "Hans",
    "review_text": "Meh, it's okay.",
    "published_date": ISODate("2017-12-06")
    }

通过将 10 条最新评论存储在product集合中,在调用product集合时仅返回所需的总体数据子集。 如果用户想查看其他评论,应用程序会调用review collection。

提示

考虑在何处拆分数据时,应该将最常访问的数据部分放入应用程序首先加载的集合。在该示例中,以 10 条评论为间距对模式进行拆分,因为这是默认在应用程序中可见的评论数。

提示

另请参阅:

要了解如何使用子集模式对collection之间的关系进行建模,请参阅使用嵌入式文档对关系进行建模。

使用包含更频繁访问数据的较小文档可减小工作集的整体大小。这些较小的文档可以提高应用程序最常访问的数据的读取性能。

但是,子集模式会导致数据重复。 在此示例中,评价同时保留在product集合和reviews集合中。 必须采取额外步骤来确保每个collection之间的评价保持一致。例如,当客户编辑其评价时,应用程序可能需要执行两项写入操作:一项是更新productcollection,另一项是更新reviewscollection。

您还必须在应用程序中实现逻辑,确保product集合中的评论始终是该产品的最新 10 条评论。

除产品评论外,子集模式也非常适合存储:

  • 对博客文章的评论,即您只想默认显示最新或评分最高的评论。

  • 电影中的演员阵容成员,当您默认只想显示具有最大角色的演员阵容成员时。

后退

一对一嵌入式文档