操作 を削減$lookup
Overview
$lookup
操作は、指定されたフィールドに基づいて同じデータベース内の 2 つのコレクションのデータを結合します。 $lookup
操作は、データがリレーショナルデータベースと同様に構造化されており、大規模な階層データセットをモデル化する必要がある場合に役立ちます。 ただし、これらの操作では 1 つのコレクションではなく 2 つのコレクションに対してロジックを読み取って実行する必要があるため、時間がかかり、リソースが集中する可能性があります。
$lookup
操作を頻繁に実行する場合は、アプリケーションが 1 つのコレクションをクエリして必要な情報をすべて取得できるようにスキーマを再構築することを検討してください。 埋め込みドキュメントと配列を含む MongoDB の柔軟なスキーマ モデルを利用して、単一のドキュメント構造でデータ間の関係をキャプチャできます。 この非正規化モデルを使用して、MongoDB の豊富なドキュメントを活用し、アプリケーションが 1 回のクエリで関連データを検索および操作できるようにします。
例
次の例は、 $lookup
操作を削減するために設計された 2 つのスキーマ構造を示しています。
埋め込みドキュメントの使用
たとえば、食料品店が 1 対 1 の在庫と食料品情報を 2 つの個別のコレクションで追跡する次の例を考えてみましょう。 各在庫アイテムは、一意の機能停止アイテムに対応しています。 nutrition_id
フィールドは、表形式データベースと同様に、 inventory
コレクションをnutrition_facts
コレクションにリンクします。
// inventory collection { "name": "Pear", "stock": 20, "nutrition_id": 123, // reference to a nutrition_fact document ... } { "name": "Candy Bar", "stock": 26, "nutrition_id": 456, ... }
// nutrition_facts collection { "_id": 123, "calories": 100, "grams_sugar": 17, "grams_protein": 1, ... } { "_id": 456, "calories": 250, "grams_sugar": 27, "grams_protein": 4, ... }
アプリケーションが在庫商品のレシピをname
でリクエストする場合、このスキーマ構造では、在庫商品のnutrition_id
と一致するエントリを見つけるために、 nutrition_facts
コレクションの$lookup
が必要です。
代わりに、 inventory
コレクションに機能情報を埋め込むことができます。
// inventory collection { "name": "Pear", "stock": 20, "nutrition_facts": { "calories": 100, "grams_sugar": 17, "grams_protein": 1, ... } ... } { "name": "Candy Bar", "stock": 26, "nutrition_facts": { "calories": 250, "grams_sugar": 27, "grams_protein": 4, ... } ... }
これにより、 inventory
の食材をクエリすると、別のクエリや$lookup
操作を必要とせずに、資格情報が結果に含まれます。 コレクション間のデータに 1 対 1 の関係がある場合は、ドキュメントの埋め込みを検討してください。
配列の使用
表形式データベースと同様に、データベースのplayers
コレクション内のドキュメントがteams
コレクション内のドキュメントを参照する次の例を考えてみます。
// players collection { "team_id": 1, // reference to a team document "name": "Nick", "position": "Pitcher" ... } { "team_id": 1, "name": "Anuj", "position": "Shortstop" ... }
// teams collection { "_id": 1, "name": "Danbury Dolphins" ... }
アプリケーションがチームのプレイヤーのリストをリクエストする場合、このスキーマ構造はteam_id
に一致する各プレイヤーを検索するために、 players
コレクションの$lookup
を必要とします。
代わりに、チーム ドキュメント自体に配列にplayers
を一覧表示できます。
// teams collection { "_id": 1, "name": "Danbury Dolphins", "players": [ { "name": "Nick", "position": "Pitcher" ... }, { "name": "Anuj", "position": "Shortstop" ... } ] }
配列 を使用して関連データを保持することで、アプリケーションは$lookup
操作や他のコレクションのインデックスを必要とせずに、そのチームのプレイヤーを含む完全なteam
情報を取得できます。 この場合、配列を使用する方が、情報を個別のコレクションに保存するよりもパフォーマンスが優れています。
注意
上記の例では、ベース チームには一定数のメンバーがあり、 の配列が大きくなりすぎるリスクはありません。
配列の考慮事項
大規模な配列への読み取りと書き込みのパフォーマンス コストは、 $lookup
操作を回避することで得られるメリットを超える可能性があります。 配列が無制限または大きすぎる場合、それらの配列は読み取りと書込みのパフォーマンスを低下させる可能性があります。
配列にインデックスを作成すると、配列内の各要素にインデックスが付けられます。 その配列に頻繁に書き込む場合、大きくなる可能性のある配列フィールドのインデックス作成や再インデックス作成のパフォーマンス コストが重大になる可能性があります。
Tip
以下も参照してください。
非正規化
スキーマの非正規化とは、フィールドを重複させたり、既存のフィールドから新しいフィールドを生成したりするプロセスです。 非正規化により、次のようなさまざまなケースで読み取りパフォーマンスが向上します。
定期クエリには、別のコレクション内の大きなドキュメントのいくつかのフィールドが必要です。 2 つの異なるコレクションをマージしたり、頻繁に
$lookup
操作を実行したりするように、定期クエリの対象となるコレクションの埋め込みドキュメントにこれらのフィールドのコピーを保持することを選択できます。コレクション内の一部のフィールドの平均値は、頻繁に要求されます。 書込みの一部として更新され、そのフィールドの実行平均を維持する別の コレクションに派生フィールドを作成することを選択できます。
関連データをグループ化するには、重複のないドキュメントまたは配列の埋め込みが推奨されますが、個別のコレクションを維持する必要がある場合は非正規化によって読み取りパフォーマンスが向上します。
注意
スキーマを非正規化する場合、一貫性のある重複データを維持する必要があるのはユーザーの責任です。
詳細
スキーマに最適な構造は、アプリケーションのコンテキストによって異なります。 次のリソースでは、データ モデリングに関する詳細情報と、埋め込みドキュメントと配列のその他のユースケース例を提供しています。
データモデル
MongoDB のデータ モデリングと柔軟なスキーマ モデルの詳細については、「データ モデリングの概要」を参照してください。
埋め込みデータモデルと正規化データモデルのトレードオフの詳細については、「データモデリング 」を参照してください。
MongoDB では、データ モデリングに関する無料の MongoDB University コース「 MongoDB のデータ モデリング 」も提供しています。
MongoDB.Live 2020 データモデリングのプレゼンテーション
柔軟なデータモデルをスキーマに組み込む方法については、MongoDB.live 2020 の以下のプレゼンテーションを参照してください。
MongoDB のエンティティ関係と、MongoDB を使用したデータ モデリングによるその実装例について学びます。
スキーマに組み込むことができる高度なデータモデリング設計パターンについて学ぶには、「高度なスキーマ デザイン パターン」をご覧ください。
配列
MongoDB で配列をクエリする方法の詳細については、「 配列のクエリ 」を参照してください。
設計パターン
配列が適切に機能する状況については、次の設計パターンを参照してください。
非正規化スキーマ
データを重複させることによってスキーマが改善される状況については、次の設計パターンを参照してください。
拡張参照パターンを使用して、大きなドキュメントから頻繁に読み取られるデータの部分を小さなドキュメントに複製します。