埋め込みドキュメントによる 1 対多の関係のモデル化
Overview
このページでは、 埋め込みドキュメントを使用して接続されたデータ間の 1 対多の関係を記述するデータモデルについて説明します。 接続されたデータを単一のドキュメントに埋め込むと、データを取得するために必要な読み取り操作の数を減らすことができます。 一般に、アプリケーションが 1 回の読み取り操作で必要な情報をすべて受け取れるようにスキーマを構成する必要があります。
埋め込みドキュメント パターン
パターンと複数のアドレス関係をマッピングする次の例を考えてみましょう。 この例では、多くのデータ エンティティを別のデータ エンティティに関連して表示する必要がある場合に、参照よりも埋め込みの利点が示されています。 patron
とaddress
データ間のこの 1 対多の関係では、 patron
には複数のaddress
エンティティがあります。
正規化されたデータモデルでは、 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
データを頻繁に取得する場合は、参照を解決するためにアプリケーションは複数のクエリを発行する必要があります。 より最適なスキーマは、次のドキュメントのように、 patron
データにaddress
データ エンティティを埋め込むことです。
{ "_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" } ] }
埋め込みデータ モデルを使用すると、アプリケーションは 1 回のクエリで完全なパッチ情報を検索できます。
サブセット パターン
埋め込みドキュメントパターンに関する潜在的な問題は、埋め込みフィールドが無制限の場合、大きなドキュメントにつながる可能性があることです。 この場合、サブセット パターンを使用すると、埋め込みデータ全体ではなく、アプリケーションに必要なデータのみにアクセスできます。
製品のレビューリストを持つeコマース サイトを考えてみましょう。
{ "_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") } ] }
レビューは、逆の時系列でソートされます。 ユーザーが製品ページにアクセスすると、アプリケーションは最新の 10 件のレビューを読み込みます。
製品のレビューをすべて保存する代わりに、 コレクションを 2 つのコレクションに分割できます。
product
コレクションには、製品の最新 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
コレクションにはすべてのレビューが保存されています。 各レビューには、それが書き込まれた製品への参照が含まれています。{ "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") }
product
コレクションに最新の 10 件のレビューを保存すると、 product
コレクションの呼び出しでは全体的なデータの必要なサブセットのみが返されます。 ユーザーが追加のレビューを表示したい場合、アプリケーションはreview
コレクションを呼び出します。
Tip
データを分割する場所を検討する場合、データの最も頻繁にアクセスする部分は、アプリケーションが最初に読み込むコレクションに含める必要がありGo 。 この例では、スキーマは 10 個のレビューに分割されます。これは、デフォルトでアプリケーションに表示されるレビュー数であるためです。
Tip
以下も参照してください。
サブセット パターンを使用してコレクション間の 1 対 1 の関係をモデル化する方法については、「埋め込みドキュメントによる 1 対 1 の関係のモデル化 」を参照してください。
サブセット パターンのトレードオフ
アクセス頻度の高いデータを含む小さいドキュメントを使用すると、 ワーキングセット の全体サイズは縮小されます。 これらのドキュメントが小さいと、アプリケーションが最も頻繁にアクセスするデータの読み取りパフォーマンスが向上します。
ただし、サブセット パターンではデータの重複が発生します。 この例では、レビューはproduct
コレクションとreviews
コレクションの両方で保持されています。 各コレクション間でレビューが一貫していることを確認するために、追加の手順を実行する必要があります。 たとえば、カスタマーがレビューを編集する場合、アプリケーションは 2 つの書込み操作を行う必要があります。1 つはproduct
コレクションを更新し、1 つはreviews
コレクションを更新します。
また、アプリケーションにロジックを実装して、 product
コレクション内のレビューが常にその製品の最新の 10 件のレビューになるようにする必要があります。
その他のサンプルユースケース
製品レビューに加えて、サブセット パターンは以下の保存にも適しています。
最新のコメントまたは最高評価のコメントのみをデフォルトで表示するブログ記事のコメント。
映画内で、最大のロールを持つキャスト メンバーのみをデフォルトで表示する場合。