Docs Menu
Docs Home
/
MongoDBマニュアル
/ / /

$lookup(集計)

項目一覧

  • 定義
  • 互換性
  • 構文
  • 1 つの結合条件による等価一致
  • 結合されたコレクションの結合条件とサブクエリ
  • 簡潔な構文を使用した相関サブクエリ
  • 動作
  • ビューと照合
  • 制限事項
  • Atlas Search サポート
  • シャーディングされたコレクション
  • スロットベースのクエリ実行エンジン
  • パフォーマンスに関する考慮事項
  • 1 つの等価結合の実行 $lookup
  • 配列で$lookupを使用する
  • $lookup$mergeObjectsと一緒に使用する
  • 複数の結合条件と相関サブクエリの使用
  • 非相関サブクエリの実行 $lookup
  • 簡潔な相関サブクエリの実行 $lookup
  • サブパイプラインの名前空間
$lookup

バージョン8.0で変更

同じデータベース内のコレクションへの左外部結合を実行して 、"join" コレクションのドキュメントをフィルタリングして処理します。 $lookupステージでは、各入力ドキュメントに新しい配列フィールドが追加されます。 新しい配列フィールドには、結合済みコレクションと一致するドキュメントが含まれます。 $lookupステージは、これらを再作成したドキュメントを次のステージに渡します。

MongoDB 5.1以降では、シャーディングされたコレクションで$lookupを使用できます。

2 つの異なるコレクションの要素を結合するには、 $unionWithパイプライン ステージを使用します。

次の環境でホストされる配置には $lookup を使用できます。

  • MongoDB Atlas はクラウドでの MongoDB 配置のためのフルマネージド サービスです

  • MongoDB Enterprise: サブスクリプションベースの自己管理型 MongoDB バージョン

  • MongoDB Community: ソースが利用可能で、無料で使用できる自己管理型の MongoDB のバージョン

$lookup ステージには、次のセクションに示す構文のバリエーションがあります。

入力ドキュメントのフィールドと「結合された」コレクションのドキュメントのフィールドとの間で等価一致を実行するには、$lookup ステージで次の構文を使用します。

{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}

$lookup は次のフィールドを持つドキュメントを取得します。

フィールド
説明

結合を実行する同じデータベース内のコレクションを指定します。

from は任意であり、代わりに $lookup ステージで $documents ステージを使用できます。例については、「$lookup ステージでの $documents ステージの使用」を参照してください。

MongoDB 5.1 以降では、fromパラメータで指定されたコレクションをシャーディングできます。

$lookup ステージに入力されるドキュメントからフィールドを指定します。$lookupfrom コレクションのドキュメントから localFieldforeignField の等価一致を実行します。入力ドキュメントに localField が含まれていない場合、$lookup は一致させる目的でそのフィールドがnull の値を持つものとして扱います。

from コレクション内のドキュメントからフィールドを指定します。$lookup は入力ドキュメントの foreignFieldlocalField の等価一致を実行します。from コレクション内のドキュメントに foreignField が含まれていない場合、$lookup はマッチング目的でその値を null として扱います。

入力ドキュメントに追加する新しい配列フィールドの名前を指定します。新しい配列フィールドには、fromコレクションと一致するドキュメントが含まれます。指定した名前がすでに入力ドキュメントに存在する場合、既存のフィールドは上書きされます。

この操作は、次の疑似 SQL ステートメントに対応します。

SELECT *, (
SELECT ARRAY_AGG(*)
FROM <collection to join>
WHERE <foreignField> = <collection.localField>
) AS <output array field>
FROM collection;

注意

このページの SQL ステートメントは、MongoDB 集計パイプライン構文の比較に含まれています。SQL ステートメントは実行できません。

MongoDB の例については、次のページを参照してください。

MongoDB は以下をサポートします。

  • 結合したコレクションでパイプラインを実行する。

  • 複数の結合条件。

  • 相関サブクエリと非相関サブクエリ。

MongoDB では、相関サブクエリは、結合されたコレクションのドキュメント フィールドを参照する $lookupステージの パイプライン です。非相関サブクエリでは、結合されたフィールドは参照されません。

注意

MongoDB5.0 以降では、$lookup $sampleステージ、$sampleRate 演算子、または$rand 演算子を含む、 パイプライン ステージ内の相関のないサブクエリの場合、繰り返される場合はサブクエリが常に再度実行されます。以前は、サブクエリの出力サイズに応じて、サブクエリの出力がキャッシュされるか、サブクエリが再度実行されていました。

MongoDB 相関サブクエリは、内部クエリが外部クエリ値を参照する SQL 相関サブクエリに相当します。SQL 非相関サブクエリは、外部クエリ値を参照しません。

MongoDB 5.0 は簡潔な相関サブクエリもサポートしています。

2 つのコレクションに対して相関サブクエリと非相関サブクエリを実行し、単一の等価一致以外の結合条件を実行するには、次の$lookup構文を使用します。

{
$lookup:
{
from: <joined collection>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to run on joined collection> ],
as: <output array field>
}
}

$lookupステージは、次のフィールドを持つドキュメントを受け入れます。

フィールド
説明

結合操作を実行する同じデータベース内のコレクションを指定します。

from は任意であり、代わりに $lookup ステージで $documents ステージを使用できます。例については、「$lookup ステージでの $documents ステージの使用」を参照してください。

MongoDB 5.1 以降では、 fromコレクションをシャーディングできます。

任意。 パイプラインステージで使用する変数を指定します。 変数式を使用して、 pipelineに入力される結合済みコレクションのドキュメントのフィールドにアクセスします。

パイプラインステージで変数を参照するには、 "$$<variable>"構文を使用します。

let 変数には、pipeline にネストされた追加の $lookup ステージを含むパイプラインのステージからアクセス可能です。

  • $matchステージでは、変数にアクセスするために$expr演算子を使用する必要があります。$expr演算子を使用すると、$match構文内で集計式を使用できます。

    $expr 演算子に配置されている $eq$lt$lte$gt$gte 比較演算子では、$lookup ステージで参照される from コレクションにインデックスを使用できます。制限:

    • インデックスはフィールドと定数の比較にのみ使用できるため、let オペランドは定数に変換する必要があります。

      たとえば、$a と定数値の比較にはインデックスを使用できますが、$a$b の比較には使用できません。

    • let オペランドが空の値または欠損値に変換される場合の比較には、インデックスは使用されません。

    • マルチキーインデックスは使用されません

  • パイプライン の他の($match $expr以外の)ステージでは、変数にアクセスするために 演算子は必要ありません。

結合済みコレクションで実行する pipelineを指定します。pipelineは、結合済みコレクションから生成されるドキュメントを決定します。すべてのドキュメントを返すには、空のpipeline []を指定します。

pipeline には $out ステージまたは $merge ステージを含めることはできません。バージョン 6 . 0以降では、pipeline にはパイプライン内の最初のステージとして Atlas Search $search ステージを含めることができます。詳しくは、「Atlas Search サポート」を参照してください。

pipelineは結合されたドキュメント フィールドに直接アクセスできません。 代わりに、 letオプションを使用して結合されたドキュメント フィールドの変数を定義し、 pipelineステージで変数を参照します。

パイプラインステージで変数を参照するには、 "$$<variable>"構文を使用します。

let 変数には、pipeline にネストされた追加の $lookup ステージを含むパイプラインのステージからアクセス可能です。

  • $matchステージでは、変数にアクセスするために$expr演算子を使用する必要があります。$expr演算子を使用すると、$match構文内で集計式を使用できます。

    $expr 演算子に配置されている $eq$lt$lte$gt$gte 比較演算子では、$lookup ステージで参照される from コレクションにインデックスを使用できます。制限:

    • インデックスはフィールドと定数の比較にのみ使用できるため、let オペランドは定数に変換する必要があります。

      たとえば、$a と定数値の比較にはインデックスを使用できますが、$a$b の比較には使用できません。

    • let オペランドが空の値または欠損値に変換される場合の比較には、インデックスは使用されません。

    • マルチキーインデックスは使用されません

  • パイプライン の他の($match $expr以外の)ステージでは、変数にアクセスするために 演算子は必要ありません。

結合済みドキュメントに追加する新しい配列フィールドの名前を指定します。新しい配列フィールドには、結合済みコレクションと一致するドキュメントが含まれます。指定した名前がすでに結合済みドキュメントに存在する場合、既存のフィールドは上書きされます。

この操作は、次の疑似 SQL ステートメントに対応します。

SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (
SELECT <documents as determined from the pipeline>
FROM <collection to join>
WHERE <pipeline>
);

次の例を参照してください。

バージョン 5.0 で追加

MongoDB 5.0 以降、相関サブクエリに構文を使えるようになりました。相関サブクエリは、結合済みの「外部」コレクションと、aggregate()メソッドが実行された「ローカル」コレクションのドキュメント フィールドを参照します。

次の新しい簡潔な構文により、$expr演算子内の外部フィールドとローカルフィールドを等価一致させる必要がなくなります。

{
$lookup:
{
from: <foreign collection>,
localField: <field from local collection's documents>,
foreignField: <field from foreign collection's documents>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to run> ],
as: <output array field>
}
}

$lookupは、次のフィールドを含むドキュメントを受け入れます。

フィールド
説明

ローカルコレクションに結合する同じデータベース内の外部コレクションを指定します。

from は任意であり、代わりに $lookup ステージで $documents ステージを使用できます。例については、「$lookup ステージでの $documents ステージの使用」を参照してください。

MongoDB 5.1 以降では、 fromコレクションをシャーディングできます。

ローカルドキュメントの localField を指定して、外部ドキュメントの foreignField と等価一致を行います。

ローカル ドキュメントに localField 値が含まれていない場合、$lookup では一致するように null 値が使用されます。

外部ドキュメントの foreignField を指定して、ローカルドキュメントの localField と等価一致を実行します。

外部ドキュメントにforeignField値が含まれていない場合、 $lookupは一致にnull値を使用します。

任意。パイプラインステージで使用する変数を指定します。変数式を使用して、pipeline に入力されるドキュメント フィールドにアクセスします。

パイプラインステージで変数を参照するには、 "$$<variable>"構文を使用します。

let 変数には、pipeline にネストされた追加の $lookup ステージを含むパイプラインのステージからアクセス可能です。

  • $matchステージでは、変数にアクセスするために$expr演算子を使用する必要があります。$expr演算子を使用すると、$match構文内で集計式を使用できます。

    $expr 演算子に配置されている $eq$lt$lte$gt$gte 比較演算子では、$lookup ステージで参照される from コレクションにインデックスを使用できます。制限:

    • インデックスはフィールドと定数の比較にのみ使用できるため、let オペランドは定数に変換する必要があります。

      たとえば、$a と定数値の比較にはインデックスを使用できますが、$a$b の比較には使用できません。

    • let オペランドが空の値または欠損値に変換される場合の比較には、インデックスは使用されません。

    • マルチキーインデックスは使用されません

  • パイプライン の他の($match $expr以外の)ステージでは、変数にアクセスするために 演算子は必要ありません。

外部コレクションで実行する pipeline を指定します。pipelineは外部コレクションからドキュメントを返します。すべてのドキュメントを返すには、空のpipeline []を指定します。

pipelineには$out } ステージまたは$mergeステージを含めることはできません。 v 6.0以降では、 pipelineにはパイプライン内の最初のステージとしてAtlas Search $searchステージを含めることができます。 詳しくは、「 Atlas Search サポート 」を参照してください。

pipelineはドキュメント フィールドに直接アクセスできません。 代わりに、 letオプションを使用してドキュメント フィールドの変数を定義し、 pipelineステージで変数を参照します。

パイプラインステージで変数を参照するには、 "$$<variable>"構文を使用します。

let 変数には、pipeline にネストされた追加の $lookup ステージを含むパイプラインのステージからアクセス可能です。

  • $matchステージでは、変数にアクセスするために$expr演算子を使用する必要があります。$expr演算子を使用すると、$match構文内で集計式を使用できます。

    $expr 演算子に配置されている $eq$lt$lte$gt$gte 比較演算子では、$lookup ステージで参照される from コレクションにインデックスを使用できます。制限:

    • インデックスはフィールドと定数の比較にのみ使用できるため、let オペランドは定数に変換する必要があります。

      たとえば、$a と定数値の比較にはインデックスを使用できますが、$a$b の比較には使用できません。

    • let オペランドが空の値または欠損値に変換される場合の比較には、インデックスは使用されません。

    • マルチキーインデックスは使用されません

  • パイプライン の他の($match $expr以外の)ステージでは、変数にアクセスするために 演算子は必要ありません。

外部ドキュメントに追加する新しい配列フィールドの名前を指定します。新しい配列フィールドには、外部コレクションからの一致するドキュメントが含まれます。指定した名前がすでに外部ドキュメントに存在する場合、既存のフィールドは上書きされます。

この操作は、次の疑似 SQL ステートメントに対応します。

SELECT *, <output array field>
FROM localCollection
WHERE <output array field> IN (
SELECT <documents as determined from the pipeline>
FROM <foreignCollection>
WHERE <foreignCollection.foreignField> = <localCollection.localField>
AND <pipeline match condition>
);

以下の例を参照してください。

$lookup$graphLookup のように複数のビューを含む集計を実行する場合、ビューの照合順序は同じである必要があります。

$lookup ステージには、$out ステージや $merge ステージを含めることはできません。具体的には、結合コレクションのパイプラインを指定するときに、pipeline フィールドにどちらのステージも含めることはできません。

{
$lookup:
{
from: <collection to join>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to execute on the joined collection> ], // Cannot include $out or $merge
as: <output array field>
}
}

MongoDB 6.0 以降では、 $lookup パイプラインで Atlas Search $search または $searchMeta ステージを指定して、Atlas クラスター上のコレクションを検索できます。$search または $searchMeta ステージは、 $lookup パイプライン内の最初のステージである必要があります。

たとえば、結合コレクションで条件とサブクエリを結合するか、簡潔な構文を使用して相関サブクエリを実行する 場合は、以下に示すようにパイプライン内で$searchまたは$searchMetaを指定できます。

[{
"$lookup": {
"from": <joined collection>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
"as": <output array field>,
"pipeline": [{
"$search": {
"<operator>": {
<operator-specification>
}
},
...
}]
}
}]
[{
"$lookup": {
"from": <joined collection>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
"as": <output array field>,
"pipeline": [{
"$searchMeta": {
"<collector>": {
<collector-specification>
}
},
...
}]
}
}]

$lookup とともに $search を使った例については、Atlas Search チュートリアルの「$lookup を使用した Atlas Search $search クエリの実行」を参照してください。

MongoDB5.1 以降では、from ステージの パラメーターで$lookup シャーディングされたコレクション を指定できます。

MongoDB 8.0 以降では、シャーディングされたコレクションをターゲットにしながら、トランザクション内で $lookup ステージを使用できます。

バージョン 6.0 以降、MongoDB は、パイプライン内の先行するすべてのステージもスロット ベースの実行エンジンで実行でき、次の条件のいずれも当てはまらない場合、スロット ベースの実行クエリ エンジンを使用して $lookup ステージを実行できます。

  • $lookup操作は、結合されたコレクション上でパイプラインを実行します。 この種の操作の例については、「結合されたコレクションの結合条件とサブクエリ 」を参照してください。

  • $lookuplocalField または foreignField は数値コンポーネントを指定します。例: { localField: "restaurant.0.review" }

  • パイプラインに含まれる任意の $lookupfrom フィールドには、ビューまたはシャーディングされたコレクションが明示されます。

詳細については、「$lookup 最適化」を参照してください。

$lookup パフォーマンスは、実行される操作の種類によって異なります。さまざまな$lookup操作のパフォーマンスに関する考慮事項については、次の表を参照してください。

$lookup 操作
パフォーマンスに関する考慮事項
  • $lookup では、外部コレクションに foreignField のインデックスが含まれている場合、単一結合で等価一致を実行する操作のパフォーマンスが向上します。

    重要: foreignField に対応するインデックスが存在しない場合、単一結合で等価一致を実行する $lookup 操作はパフォーマンスが低下する可能性があります。

  • $lookup では、内部パイプラインが外部コレクションのインデックスを参照できる場合、非相関サブクエリを含む操作のパフォーマンスが向上します。

  • ソース コレクションと外部コレクションの間には関係がないため、MongoDB はクエリをキャッシュする前に $lookup サブクエリを一度だけ実行する必要があります。サブクエリはソース コレクション内のどの値にも基づいていません。この動作により、 $lookup 操作の後続の実行時のパフォーマンスが向上します。

  • $lookup では、次の条件が当てはまる場合、相関サブクエリを含む操作は、より優れたパフォーマンスを実現します。

    • 外部コレクションはforeignFieldのインデックスを含みます。

    • 外部コレクションには、内部パイプラインを参照するインデックスが含まれています。

  • パイプラインが多数のドキュメントを $lookup クエリに渡す場合、次の戦略によってパフォーマンスが向上する可能性があります。

    • MongoDB が$lookupクエリに渡すドキュメントの数を減らします。たとえば、$match ステージでより厳しいフィルターを設定します。

    • $lookup サブクエリの内部パイプラインを別のクエリとして実行し、$out を使用して一時コレクションを作成します。次に、1 つの結合による等価一致を実行します。

    • データのスキーマを再検討して、ユースケースに最適であることを確認します。

一般的なパフォーマンス戦略については、「インデックス戦略」と「クエリの最適化」を参照してください。

重要

クエリ内で$lookupを過度に使用すると、パフォーマンスが低下する可能性があります。複数の$lookup ステージを回避するには、埋め込みデータモデルを使用してクエリ パフォーマンスを最適化することを検討してください。

これらのドキュメントを使用してコレクションordersを作成します。

db.orders.insertMany( [
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
{ "_id" : 3 }
] )

これらのドキュメントを使用して別のコレクションinventoryを作成します。

db.inventory.insertMany( [
{ "_id" : 1, "sku" : "almonds", "description": "product 1", "instock" : 120 },
{ "_id" : 2, "sku" : "bread", "description": "product 2", "instock" : 80 },
{ "_id" : 3, "sku" : "cashews", "description": "product 3", "instock" : 60 },
{ "_id" : 4, "sku" : "pecans", "description": "product 4", "instock" : 70 },
{ "_id" : 5, "sku": null, "description": "Incomplete" },
{ "_id" : 6 }
] )

ordersコレクションに対する次の集計操作は、ordersコレクションのフィールドitemと inventoryコレクションのskuフィールドを使用して、ordersのドキュメントと inventoryコレクションのドキュメントを結合します。

db.orders.aggregate( [
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
] )

この操作では、次のドキュメントが返されます。

{
"_id" : 1,
"item" : "almonds",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "pecans",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}

この操作は、次の疑似 SQL ステートメントに対応します。

SELECT *, inventory_docs
FROM orders
WHERE inventory_docs IN (
SELECT *
FROM inventory
WHERE sku = orders.item
);

詳細については、「等価一致のパフォーマンスに関する考慮事項」を参照してください。

localFieldが配列の場合、$unwindステージを使用せずに、配列要素をスカラーforeignFieldと照合できます。

たとえば、次のドキュメントを使用してコレクション例「classes」を作成します。

db.classes.insertMany( [
{ _id: 1, title: "Reading is ...", enrollmentlist: [ "giraffe2", "pandabear", "artie" ], days: ["M", "W", "F"] },
{ _id: 2, title: "But Writing ...", enrollmentlist: [ "giraffe1", "artie" ], days: ["T", "F"] }
] )

これらのドキュメントを使用して別のコレクションmembersを作成します。

db.members.insertMany( [
{ _id: 1, name: "artie", joined: new Date("2016-05-01"), status: "A" },
{ _id: 2, name: "giraffe", joined: new Date("2017-05-01"), status: "D" },
{ _id: 3, name: "giraffe1", joined: new Date("2017-10-01"), status: "A" },
{ _id: 4, name: "panda", joined: new Date("2018-10-11"), status: "A" },
{ _id: 5, name: "pandabear", joined: new Date("2018-12-01"), status: "A" },
{ _id: 6, name: "giraffe2", joined: new Date("2018-12-01"), status: "D" }
] )

次の集計操作では、enrollmentlist フィールドと name フィールドを一致させて、classesコレクション内のドキュメントをmembersコレクションと結合します。

db.classes.aggregate( [
{
$lookup:
{
from: "members",
localField: "enrollmentlist",
foreignField: "name",
as: "enrollee_info"
}
}
] )

この操作では、以下を返します。

{
"_id" : 1,
"title" : "Reading is ...",
"enrollmentlist" : [ "giraffe2", "pandabear", "artie" ],
"days" : [ "M", "W", "F" ],
"enrollee_info" : [
{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },
{ "_id" : 5, "name" : "pandabear", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "A" },
{ "_id" : 6, "name" : "giraffe2", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "D" }
]
}
{
"_id" : 2,
"title" : "But Writing ...",
"enrollmentlist" : [ "giraffe1", "artie" ],
"days" : [ "T", "F" ],
"enrollee_info" : [
{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },
{ "_id" : 3, "name" : "giraffe1", "joined" : ISODate("2017-10-01T00:00:00Z"), "status" : "A" }
]
}

$mergeObjects 演算子は、複数のドキュメントを 1 つのドキュメントに結合します。

これらのドキュメントを使用してコレクションordersを作成します。

db.orders.insertMany( [
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
] )

これらのドキュメントを使用して別のコレクションitemsを作成します。

db.items.insertMany( [
{ "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },
{ "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },
{ "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }
] )

次の操作では、まず$lookupステージを使用してitemフィールドで 2 つのコレクションを結合し、次に$replaceRoot$mergeObjectsを使用してitemsordersの結合されたドキュメントをマージします。

db.orders.aggregate( [
{
$lookup: {
from: "items",
localField: "item", // field in the orders collection
foreignField: "item", // field in the items collection
as: "fromItems"
}
},
{
$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$fromItems", 0 ] }, "$$ROOT" ] } }
},
{ $project: { fromItems: 0 } }
] )

この操作では、次のドキュメントが返されます。

{
_id: 1,
item: 'almonds',
description: 'almond clusters',
instock: 120,
price: 12,
quantity: 2
},
{
_id: 2,
item: 'pecans',
description: 'candied pecans',
instock: 60,
price: 20,
quantity: 1
}

パイプラインは結合されたコレクションに対して実行でき、複数の結合条件を含めることができます。$expr 演算子を使用すると、接続詞や不等価一致など、より複雑な結合条件が可能になります。

結合条件は、aggregate()メソッドが実行されたローカル コレクション内のフィールドを参照し、結合されたコレクション内のフィールドを参照できます。これにより、2 つのコレクション間の相関サブクエリが可能になります。

MongoDB 5.0 は簡潔な相関サブクエリをサポートしています。

これらのドキュメントを使用してコレクションordersを作成します。

db.orders.insertMany( [
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
] )

これらのドキュメントを使用して別のコレクションwarehousesを作成します。

db.warehouses.insertMany( [
{ "_id" : 1, "stock_item" : "almonds", warehouse: "A", "instock" : 120 },
{ "_id" : 2, "stock_item" : "pecans", warehouse: "A", "instock" : 80 },
{ "_id" : 3, "stock_item" : "almonds", warehouse: "B", "instock" : 60 },
{ "_id" : 4, "stock_item" : "cookies", warehouse: "B", "instock" : 40 },
{ "_id" : 5, "stock_item" : "cookies", warehouse: "A", "instock" : 80 }
] )

次の例:

  • orders.itemフィールドとwarehouse.stock_itemフィールドを結合した相関サブクエリを使用します。

  • 在庫品の数量が注文数量を満たせることを確認します。

db.orders.aggregate( [
{
$lookup:
{
from: "warehouses",
let: { order_item: "$item", order_qty: "$ordered" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$stock_item", "$$order_item" ] },
{ $gte: [ "$instock", "$$order_qty" ] }
]
}
}
},
{ $project: { stock_item: 0, _id: 0 } }
],
as: "stockdata"
}
}
] )

この操作では、次のドキュメントが返されます。

{
_id: 1,
item: 'almonds',
price: 12,
ordered: 2,
stockdata: [
{ warehouse: 'A', instock: 120 },
{ warehouse: 'B', instock: 60 }
]
},
{
_id: 2,
item: 'pecans',
price: 20,
ordered: 1,
stockdata: [ { warehouse: 'A', instock: 80 } ]
},
{
_id: 3,
item: 'cookies',
price: 10,
ordered: 60,
stockdata: [ { warehouse: 'A', instock: 80 } ]
}

この操作は、次の疑似 SQL ステートメントに対応します。

SELECT *, stockdata
FROM orders
WHERE stockdata IN (
SELECT warehouse, instock
FROM warehouses
WHERE stock_item = orders.item
AND instock >= orders.ordered
);

$expr 演算子に配置されている $eq$lt$lte$gt$gte 比較演算子では、$lookup ステージで参照される from コレクションにインデックスを使用できます。制限:

  • インデックスはフィールドと定数の比較にのみ使用できるため、let オペランドは定数に変換する必要があります。

    たとえば、$a と定数値の比較にはインデックスを使用できますが、$a$b の比較には使用できません。

  • let オペランドが空の値または欠損値に変換される場合の比較には、インデックスは使用されません。

  • マルチキーインデックスは使用されません

たとえば、インデックス{ stock_item: 1, instock: 1 } warehousesコレクションに存在する場合:

  • warehouses.stock_itemフィールドの等価一致はインデックスを使用します。

  • warehouses.instock フィールドのクエリの範囲部分でも、複合インデックス内のインデックス付きフィールドが使用されます。

Tip

以下も参照してください。

集計パイプラインの $lookup ステージでは、結合コレクションに対してパイプラインを実行できるため、非相関サブクエリが可能になります。非相関サブクエリでは、結合されたドキュメント フィールドは参照されません。

注意

MongoDB5.0 以降では、$lookup $sampleステージ、$sampleRate 演算子、または$rand 演算子を含む、 パイプライン ステージ内の相関のないサブクエリの場合、繰り返される場合はサブクエリが常に再度実行されます。以前は、サブクエリの出力サイズに応じて、サブクエリの出力がキャッシュされるか、サブクエリが再度実行されていました。

これらのドキュメントを使用してコレクションabsencesを作成します。

db.absences.insertMany( [
{ "_id" : 1, "student" : "Ann Aardvark", sickdays: [ new Date ("2018-05-01"),new Date ("2018-08-23") ] },
{ "_id" : 2, "student" : "Zoe Zebra", sickdays: [ new Date ("2018-02-01"),new Date ("2018-05-23") ] },
] )

これらのドキュメントを使用して別のコレクションholidaysを作成します。

db.holidays.insertMany( [
{ "_id" : 1, year: 2018, name: "New Years", date: new Date("2018-01-01") },
{ "_id" : 2, year: 2018, name: "Pi Day", date: new Date("2018-03-14") },
{ "_id" : 3, year: 2018, name: "Ice Cream Day", date: new Date("2018-07-15") },
{ "_id" : 4, year: 2017, name: "New Years", date: new Date("2017-01-01") },
{ "_id" : 5, year: 2017, name: "Ice Cream Day", date: new Date("2017-07-16") }
] )

次の操作では、absencesコレクションをholidaysコレクションの 2018 年の休日情報と結合します。

db.absences.aggregate( [
{
$lookup:
{
from: "holidays",
pipeline: [
{ $match: { year: 2018 } },
{ $project: { _id: 0, date: { name: "$name", date: "$date" } } },
{ $replaceRoot: { newRoot: "$date" } }
],
as: "holidays"
}
}
] )

この操作では、以下を返します。

{
_id: 1,
student: 'Ann Aardvark',
sickdays: [
ISODate("2018-05-01T00:00:00.000Z"),
ISODate("2018-08-23T00:00:00.000Z")
],
holidays: [
{ name: 'New Years', date: ISODate("2018-01-01T00:00:00.000Z") },
{ name: 'Pi Day', date: ISODate("2018-03-14T00:00:00.000Z") },
{ name: 'Ice Cream Day', date: ISODate("2018-07-15T00:00:00.000Z")
}
]
},
{
_id: 2,
student: 'Zoe Zebra',
sickdays: [
ISODate("2018-02-01T00:00:00.000Z"),
ISODate("2018-05-23T00:00:00.000Z")
],
holidays: [
{ name: 'New Years', date: ISODate("2018-01-01T00:00:00.000Z") },
{ name: 'Pi Day', date: ISODate("2018-03-14T00:00:00.000Z") },
{ name: 'Ice Cream Day', date: ISODate("2018-07-15T00:00:00.000Z")
}
]
}

この操作は、次の疑似 SQL ステートメントに対応します。

SELECT *, holidays
FROM absences
WHERE holidays IN (
SELECT name, date
FROM holidays
WHERE year = 2018
);

詳細については、「非相関サブクエリのパフォーマンスに関する検討事項 」を参照してください。

バージョン 5.0 で追加

MongoDB 5.0以降、集計パイプラインの$lookupステージでは、コレクション間の結合を改善する簡潔な相関サブクエリ構文がサポートされています。 新しい簡潔な構文により、 ステージの$expr $match演算子内の外部フィールドとローカルフィールドを等価一致させる必要がなくなります。

コレクション restaurants を以下ように作成します。

db.restaurants.insertMany( [
{
_id: 1,
name: "American Steak House",
food: [ "filet", "sirloin" ],
beverages: [ "beer", "wine" ]
},
{
_id: 2,
name: "Honest John Pizza",
food: [ "cheese pizza", "pepperoni pizza" ],
beverages: [ "soda" ]
}
] )

食べ物と任意の飲み物の注文を含む別のコレクションordersを作成します。

db.orders.insertMany( [
{
_id: 1,
item: "filet",
restaurant_name: "American Steak House"
},
{
_id: 2,
item: "cheese pizza",
restaurant_name: "Honest John Pizza",
drink: "lemonade"
},
{
_id: 3,
item: "cheese pizza",
restaurant_name: "Honest John Pizza",
drink: "soda"
}
] )

次の例:

  • orders.restaurant_namelocalFieldrestaurants.nameforeignField を一致させることにより、orders コレクションと restaurantsコレクションを結合します。一致は pipeline が実行される前に実行されます。

  • それぞれ$$orders_drink$beveragesを使用してアクセスされるorders.drinkフィールドとrestaurants.beveragesフィールド間で$in配列の一致を実行します。

db.orders.aggregate( [
{
$lookup: {
from: "restaurants",
localField: "restaurant_name",
foreignField: "name",
let: { orders_drink: "$drink" },
pipeline: [ {
$match: {
$expr: { $in: [ "$$orders_drink", "$beverages" ] }
}
} ],
as: "matches"
}
}
] )

orders.drinkフィールドとrestaurants.beveragesフィールドにsoda値の一致があります。この出力は matches 配列を表示し、一致する restaurants コレクションからのすべての結合フィールドを含みます。

{
"_id" : 1, "item" : "filet",
"restaurant_name" : "American Steak House",
"matches" : [ ]
}
{
"_id" : 2, "item" : "cheese pizza",
"restaurant_name" : "Honest John Pizza",
"drink" : "lemonade",
"matches" : [ ]
}
{
"_id" : 3, "item" : "cheese pizza",
"restaurant_name" : "Honest John Pizza",
"drink" : "soda",
"matches" : [ {
"_id" : 2, "name" : "Honest John Pizza",
"food" : [ "cheese pizza", "pepperoni pizza" ],
"beverages" : [ "soda" ]
} ]
}

簡潔な相関サブクエリが導入される前は、複数の結合条件および相関サブクエリの使用で示されているように、pipeline $lookup$expr 演算子にある、ローカルフィールドと結合フィールド間の $eq 等価一致を使う必要がありました。

この例では、MongoDB バージョン 5.0 以前の古い冗語構文を使用しており、以前の簡潔な例と同様の結果を返します。

db.orders.aggregate( [
{
$lookup: {
from: "restaurants",
let: { orders_restaurant_name: "$restaurant_name",
orders_drink: "$drink" },
pipeline: [ {
$match: {
$expr: {
$and: [
{ $eq: [ "$$orders_restaurant_name", "$name" ] },
{ $in: [ "$$orders_drink", "$beverages" ] }
]
}
}
} ],
as: "matches"
}
}
] )

前の例は、次の疑似 SQL ステートメントに対応します。

SELECT *, matches
FROM orders
WHERE matches IN (
SELECT *
FROM restaurants
WHERE restaurants.name = orders.restaurant_name
AND restaurants.beverages = orders.drink
);

詳細については、「相関サブクエリのパフォーマンスに関する検討事項」を参照してください。

MongoDB 8.0 以降では、from フィールドと coll フィールドが正しく使用されるように、$lookup$unionWith 内のサブパイプラインの名前空間が検証されます。

  • $lookup の場合、指定されたコレクションを必要としないステージを持つサブパイプラインを使用する場合は、from フィールドを省略する(例: $documents ステージ)。

  • 同様に、$unionWith の場合、coll フィールドを省略する。

変更されていない動作:

  • コレクションのステージで開始される $lookup の場合(例: $match サブパイプラインまたは $collStats サブパイプライン)、from フィールドを含め、コレクションを指定する必要があります。

  • 同様に、$unionWith の場合は、coll フィールドを含め、コレクションを指定します。

次のシナリオは例です。

コレクション cakeFlavors を以下ように作成します。

db.cakeFlavors.insertMany( [
{ _id: 1, flavor: "chocolate" },
{ _id: 2, flavor: "strawberry" },
{ _id: 3, flavor: "cherry" }
] )

MongoDB 8.0 以降、次の例では、無効な from フィールドが含まれているためエラーが返されます。

db.cakeFlavors.aggregate( [ {
$lookup: {
from: "cakeFlavors",
pipeline: [ { $documents: [ {} ] } ],
as: "test"
}
} ] )

8.0 より前の MongoDB バージョンでは、前の例が実行されます。

有効な from フィールドの例については、「$lookup を使用して単一の等価一致を実行する」を参照してください。

戻る

$listSessions