# 一对一嵌套关系模型

在本页面

* [概述](https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-one-relationships-between-documents/#overview)
* [嵌套文档模式](https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-one-relationships-between-documents/#embedded-document-pattern)
* [子集模式](https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-one-relationships-between-documents/#subset-pattern)

## [概述¶](https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-one-relationships-between-documents/#overview)

这章节使用嵌套文档的模型描述了具有一对一关系的数据实体。把有关联的数据嵌套在单个文档中，可以减少读操作的次数。通常来说，如果你按嵌套文档模式来设计你的数据结构，那么在一次读取操作里，你的应用程序会接收文档所有的信息。

## [嵌套文档模式](https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-one-relationships-between-documents/#embedded-document-pattern)

考虑下面 patron 和 address 映射关系的示例。这个示例说明嵌套文档优于引用：你需要在一个数据实体的内部查看另一个数据实体的信息。数据实体 `patron` 和 数据实体`address` 的关系是一对一的，一个 `address` 数据实体属于一个 `patron` 数据实体。

在标准化数据模型里，`address` 文档包含了 `patron`文档的引用。

```
// patron document
// patron 文档
{
   _id: "joe",
   name: "Joe Bookreader"
}

// address document
// address 文档
{
   patron_id: "joe", // reference to patron document // patron文档的引用
   street: "123 Fake Street",
   city: "Faketon",
   state: "MA",
   zip: "12345"
}
```

如果以引用的方式频繁读取`name` 和 `address` 的数据，那么你的应用程序需要查询多次才能获取信息。更好的方式应该把 `address` 实体嵌套到 `patron` 实体内，像下面这个示例。

```
{
   _id: "joe",
   name: "Joe Bookreader",
   address: {
              street: "123 Fake Street",
              city: "Faketon",
              state: "MA",
              zip: "12345"
            }
}
```

在嵌套文档模型里，你的应用程序查询一次就能获取 `patron` 的完整信息。

## [子集模式](https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-one-relationships-between-documents/#subset-pattern)

嵌套文档模型有一个潜在问题是：当文档包含了应用程序不需要的字段时，它会导致文档过大。这些冗余的数据会造成服务器的额外开销从而降低读的性能。相反，你可以把频繁被访问的数据子集放在单独的数据库中，以子集模式去方式去获取。

考虑一个应用会呈现电影的信息。数据库包含了一个 `movie` 集合，`movie` 的模式如下。

```
{
  "_id": 1,
  "title": "The Arrival of a Train",
  "year": 1896,
  "runtime": 1,
  "released": ISODate("01-25-1896"),
  "poster": "http://ia.media-imdb.com/images/M/MV5BMjEyNDk5MDYzOV5BMl5BanBnXkFtZTgwNjIxMTEwMzE@._V1_SX300.jpg",
  "plot": "A group of people are standing in a straight line along the platform of a railway station, waiting for a train, which is seen coming at some distance. When the train stops at the platform, ...",
  "fullplot": "A group of people are standing in a straight line along the platform of a railway station, waiting for a train, which is seen coming at some distance. When the train stops at the platform, the line dissolves. The doors of the railway-cars open, and people on the platform help passengers to get off.",
  "lastupdated": ISODate("2015-08-15T10:06:53"),
  "type": "movie",
  "directors": [ "Auguste Lumière", "Louis Lumière" ],
  "imdb": {
    "rating": 7.3,
    "votes": 5043,
    "id": 12
  },
  "countries": [ "France" ],
  "genres": [ "Documentary", "Short" ],
  "tomatoes": {
    "viewer": {
      "rating": 3.7,
      "numReviews": 59
    },
    "lastUpdated": ISODate("2020-01-09T00:02:53")
  }
}
```

目前，在展示一部电影的简介时，`movie` 集合包含了应用程序不需要的几个的字段，比如像 `fullplot` 和 `rating` 的值。并不是要把电影的所有的数据都存储在单个集合里，你可以把单个集合分离成两个集合：

* `movie` 集合包含了一部电影的基本信息。应用程序会默认加载这个文档数据。

  ```
  // movie collection
  // movie 集合

  {
    "_id": 1,
    "title": "The Arrival of a Train",
    "year": 1896,
    "runtime": 1,
    "released": ISODate("1896-01-25"),
    "type": "movie",
    "directors": [ "Auguste Lumière", "Louis Lumière" ],
    "countries": [ "France" ],
    "genres": [ "Documentary", "Short" ],
  }
  ```
* `movie_details` 集合包含了每部电影额外的，较少访问的数据。

  ```
  // movie_details collection
  // movie_details 集合

  {
    "_id": 156,
    "movie_id": 1, // reference to the movie collection
    "poster": "http://ia.media-imdb.com/images/M/MV5BMjEyNDk5MDYzOV5BMl5BanBnXkFtZTgwNjIxMTEwMzE@._V1_SX300.jpg",
    "plot": "A group of people are standing in a straight line along the platform of a railway station, waiting for a train, which is seen coming at some distance. When the train stops at the platform, ...",
    "fullplot": "A group of people are standing in a straight line along the platform of a railway station, waiting for a train, which is seen coming at some distance. When the train stops at the platform, the line dissolves. The doors of the railway-cars open, and people on the platform help passengers to get off.",
    "lastupdated": ISODate("2015-08-15T10:06:53"),
    "imdb": {
      "rating": 7.3,
      "votes": 5043,
      "id": 12
    },
    "tomatoes": {
      "viewer": {
        "rating": 3.7,
        "numReviews": 59
      },
      "lastUpdated": ISODate("2020-01-29T00:02:53")
    }
  }
  ```

这种方法提高了读取性能，因为它要求应用程序读取更少的数据来满足最常见的需求。如果需要那些较少被访问的数据，应用程序会调用其他的数据库。

> 提示：
>
> 当考虑在哪里分离你的数据时，在文档中频繁被访问的部分应该被分离出来，因为它会被应用程序首先加载。

另请参阅

了解如何使用子集模式到一对多关系集合模型中，参阅 [Model One-to-Many Relationships with Embedded Documents](https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-many-relationships-between-documents/#data-modeling-example-one-to-many)

### 子集模式的权衡

使用包含频繁被访问数据的小文档会减少工作集的大小。这些较小的文档集会提升了读取性能，并为应用程序提供更多内存可用。

然而，理解你的应用程序及其加载数据的方式是重要的。如果分离数据不当，你的应用程序会经常需要多次访问数据库和依赖 `JOIN` 操作才能获取应用程序需要的全部数据。

另外，你的数据被分离成很多个集合，会增加数据库的维护成本。因为它会使数据存储和数据查询变得困难。

原文链接：<https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-one-relationships-between-documents/>

译者：朱俊豪


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mongoing.com/shu-ju-mo-xing/data-model-design/model-one-to-one-relationships-with-embedded-documents.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
