定義
$lookupバージョン 5.1 での変更。
同じデータベースのコレクションへの左外部結合を実行して、外部コレクションのドキュメントをフィルタリングして処理します。
$lookupステージでは、各入力ドキュメントに新しい配列フィールドが追加されます。新しい配列フィールドには、外部コレクションからの一致するドキュメントが含まれます。$lookupステージは、これらの再形成されたドキュメントを次のステージに渡します。MongoDB 5.1以降では、シャーディングされたコレクションで
$lookupを使用できます。2 つの異なるコレクションの要素を結合するには、
$unionWithパイプライン ステージを使用します。重要
$lookupを過度に使用すると、クエリ パフォーマンスが低下する可能性があります。$lookupへの依存関係を軽減するには、埋め込みデータモデルを使用して関連データを単一のコレクションに保存することを検討してください。$lookupパフォーマンスの詳細については、パフォーマンスに関する考慮事項 を参照してください。
互換性
次の環境でホストされる配置には $lookup を使用できます。
MongoDB Atlas はクラウドでの MongoDB 配置のためのフルマネージド サービスです
MongoDB Enterprise: サブスクリプションベースの自己管理型 MongoDB バージョン
MongoDB Community: ソースが利用可能で、無料で使用できる自己管理型の MongoDB のバージョン
構文
$lookupステージ構文:
{ $lookup: { from: <collection to join>, localField: <field from the input documents>, foreignField: <field from the documents of the "from" collection>, let: { <var_1>: <expression>, …, <var_n>: <expression> }, pipeline: [ <pipeline to run> ], as: <output array field> } }
$lookupは、次のフィールドを含むドキュメントを受け入れます。
フィールド | 必要性 | 説明 |
|---|---|---|
必須 | ローカルコレクションに結合する同じデータベース内の外部コレクションを指定します。 場合によっては、 MongoDB 5.1 以降では、 | |
| ||
| 外部ドキュメントの 外部ドキュメントに | |
任意 | パイプラインステージで使用する変数を指定します。変数式を使用して、 パイプラインステージで変数を参照には、 let | |
| 外部コレクションで実行する には
パイプラインステージで変数を参照には、 let | |
必須 | 入力ドキュメントに追加する新しい配列フィールドの名前を指定します。新しい配列フィールドには、 |
1 つの結合条件による等価一致
入力ドキュメントのフィールドと外部コレクションのドキュメントのフィールドとの間で等価一致を実行するには、$lookupステージで次の構文を使用します。
{ $lookup: { from: <collection to join>, localField: <field from the input documents>, foreignField: <field from the documents of the "from" collection>, pipeline: [ <pipeline to run> ], as: <output array field> } }
注意
この例では 、pipeline は任意であり、ローカルと外部の等価ステージの後に実行されます。
この操作は、次の疑似 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では、非相関サブクエリとは、すべての入力ドキュメントが同じ結果を返すことを意味します。相関サブクエリは、ローカル コレクションまたは $lookupinputコレクションのフィールドを使用して、各受信ドキュメントに相関した結果を返す ステージの パイプライン です。
注意
MongoDB5.0 以降では、$lookup $sampleステージ、$sampleRate 演算子、または$rand 演算子を含む、 パイプライン ステージ内の相関のないサブクエリの場合、繰り返される場合はサブクエリが常に再度実行されます。以前は、サブクエリの出力サイズに応じて、サブクエリの出力がキャッシュされるか、サブクエリが再度実行されていました。
MongoDB 相関サブクエリは、内部クエリが外部クエリ値を参照する SQL 相関サブクエリに相当します。SQL 非相関サブクエリは、外部クエリ値を参照しません。
MongoDB 5.0 は簡潔な相関サブクエリもサポートしています。
2 つのコレクションに対して相関サブクエリと非相関サブクエリを実行し、単一の等価一致以外の結合条件を実行するには、次の$lookup構文を使用します。
{ $lookup: { from: <foreign collection>, let: { <var_1>: <expression>, …, <var_n>: <expression> }, pipeline: [ <pipeline to run on foreign collection> ], as: <output array field> } }
この操作は、次の疑似 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> } }
この操作は、次の疑似 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 のように複数のビューを含む集計を実行する場合、ビューの照合順序は同じである必要があります。
制限事項
$out または $merge ステージを $lookup ステージに含めることはできません。具体的には、外部コレクションのパイプラインを指定するときに、pipeline フィールドにどちらのステージも含めることはできません。
{ $lookup: { from: <collection to join>, let: { <var_1>: <expression>, …, <var_n>: <expression> }, pipeline: [ <pipeline to execute on the foreign collection> ], // Cannot include $out or $merge as: <output array field> } }
Atlas Search サポート
MongoDB 6.0 以降では、 $lookup パイプラインで Atlas Search $search または $searchMeta ステージを指定して、Atlas クラスター上のコレクションを検索できます。$search または $searchMeta ステージは、 $lookup パイプライン内の最初のステージである必要があります。
たとえば、外部コレクションで条件とサブクエリを結合する場合や、簡潔な構文を使用した相関サブクエリを実行する場合は、次に示すようにパイプライン内で$searchまたは$searchMetaを指定できます。
$lookup とともに $search を使った例については、Atlas Search チュートリアルの「$lookup を使用した Atlas Search $search クエリの実行」を参照してください。
シャーディングされたコレクション
MongoDB5.1 以降では、from ステージの パラメーターで$lookup シャーディングされたコレクション を指定できます。
シャーディングされたコレクションをターゲットにしている間は、トランザクション内で $lookup ステージを使用できません。
スロットベースのクエリ実行エンジン
注意
バージョン 7.0.17 以降、スロットベースのクエリ実行エンジンは、7.0 のパッチ バージョンではデフォルトで有効ではなくなりました。スロットベースのクエリ実行エンジンを使用するクエリを使用する場合は、バージョン 8.0 にアップグレードしてください。デフォルトで有効になっている 。
バージョン 6.0 以降、MongoDB は、パイプライン内の先行するすべてのステージもスロット ベースの実行エンジンで実行でき、次の条件のいずれも当てはまらない場合、スロット ベースの実行クエリ エンジンを使用して $lookup ステージを実行できます。
$lookup操作は、外部コレクションに対してパイプラインを実行します。この種の操作の例については、「外部コレクションの結合条件とサブクエリ」を参照してください。$lookupのlocalFieldまたはforeignFieldは数値コンポーネントを指定します。例:{ localField: "restaurant.0.review" }。パイプラインに含まれる任意の
$lookupのfromフィールドには、ビューまたはシャーディングされたコレクションが明示されます。
詳細については、「$lookup 最適化」を参照してください。
パフォーマンスに関する考慮事項
$lookup パフォーマンスは、実行される操作の種類によって異なります。さまざまな$lookup操作のパフォーマンスに関する考慮事項については、次の表を参照してください。
$lookup 操作 | パフォーマンスに関する考慮事項 |
|---|---|
| |
| |
|
例
このページの例では、sample_mflixサンプルデータセットのデータを使用します。このデータセットを自己管理型MongoDB配置にロードする方法の詳細については、サンプルデータセットをロードする を参照してください。サンプルデータベースに変更を加えた場合、このページの例を実行するには、データベースを削除して再作成する必要がある場合があります。
1 つの等価結合の実行 $lookup
次の集計操作では、まず moviesコレクションをruntime が 1000 より大きい映画をフィルタリングし、次に _id フィールドと movie_id フィールドの commentsコレクションと結合します。
db.movies.aggregate( [ { $match: { runtime: { $gt: 1000 } } }, { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", as: "movie_comments" } }, { $project: { _id: 0, title: 1, year: 1, "movie_comments.name": 1, "movie_comments.text": 1, "movie_comments.date": 1 } } ] )
[ { title: 'Centennial', year: 1978, movie_comments: [ { name: 'Ellaria Sand', text: 'Excepturi nam nam eum possimus aspernatur autem. Quis nulla optio praesentium ut distinctio explicabo.', date: ISODate('1995-08-18T03:01:50.000Z') } ] }, { title: 'Baseball', year: 1994, movie_comments: [] } ]
この操作は、次の疑似 SQL ステートメントに対応します。
SELECT *, movie_comments FROM movies WHERE movie_comments IN ( SELECT * FROM comments WHERE movie_id = movies._id );
詳細については、「等価一致のパフォーマンスに関する考慮事項」を参照してください。
配列で を使用する$lookup
localFieldが配列の場合、$unwindステージを使用せずに、配列要素をスカラーforeignFieldと照合できます。
次の集計操作では、moviesコレクションと usersコレクションを結合し、movies の cast 配列フィールドと users のスカラー nameフィールドを照合します。
db.movies.aggregate( [ { $match: { title: { $in: [ "Roger & Me", "The Sum of Us", "Centennial" ] } } }, { $lookup: { from: "users", localField: "cast", foreignField: "name", as: "cast_users" } }, { $project: { _id: 0, title: 1, year: 1, cast: 1, "cast_users.name": 1, "cast_users.email": 1 } }, { $sort: { year: 1 } } ] )
[ { cast: [ 'Raymond Burr', 'Barbara Carrera', 'Richard Chamberlain', 'Robert Conrad' ], title: 'Centennial', year: 1978, cast_users: [] }, { cast: [ 'Michael Moore', 'Roger B. Smith', 'Rhonda Britton', 'Fred Ross' ], title: 'Roger & Me', year: 1989, cast_users: [ { name: 'Michael Moore', email: 'michael_moore@fakegmail.com' } ] }, { cast: [ 'Jack Thompson', 'Russell Crowe', 'John Polson', 'Deborah Kennedy' ], title: 'The Sum of Us', year: 1994, cast_users: [ { name: 'Deborah Kennedy', email: 'deborah_kennedy@fakegmail.com' } ] } ]
と$lookup を使用$mergeObjects
$mergeObjects 演算子は、複数のドキュメントを 1 つのドキュメントに結合します。
The following 操作 uses$lookup を使用してmovies コレクションとcomments コレクションを結合し、$mergeObjects の$replaceRoot を使用して最初のコメントdocumentを映画documentとマージします。
db.movies.aggregate( [ { $match: { runtime: { $gt: 1000 } } }, { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", as: "movie_comments" } }, { $replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$movie_comments", 0 ] }, "$$ROOT" ] } } }, { $project: { _id: 0, title: 1, year: 1, genres: 1, name: 1, email: 1, text: 1, date: 1 } } ] )
[ { name: 'Ellaria Sand', email: 'indira_varma@gameofthron.es', text: 'Excepturi nam nam eum possimus aspernatur autem. Quis nulla optio praesentium ut distinctio explicabo.', date: ISODate('1995-08-18T03:01:50.000Z'), genres: [ 'Action', 'Adventure', 'Drama' ], title: 'Centennial', year: 1978 }, { genres: [ 'Documentary', 'History', 'Sport' ], title: 'Baseball', year: 1994 } ]
複数の結合条件と相関サブクエリの使用
パイプラインは外部コレクションに対して実行でき、複数の結合条件を含めることができます。$expr 演算子を使用すると、接続詞や不等価一致など、より複雑な結合条件が可能になります。
結合条件は、aggregate()メソッドが実行されたローカル コレクション内のフィールドを参照し、外部コレクション内のフィールドを参照できます。これにより、2つのコレクション間の相関サブクエリが可能になります。
MongoDB 5.0 は簡潔な相関サブクエリをサポートしています。
次の例:
_idフィールドとmovie_idフィールドを使用してmoviesコレクションとcommentsコレクションを結合します。映画の公開年後に投稿されたコメントのみを含めるようにコメントをフィルタリングします。
db.movies.aggregate( [ { $match: { title: { $in: [ "Class Action", "Kafka", "Corpse Bride" ] } } }, { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", let: { movie_year: "$year" }, pipeline: [ { $match: { $expr: { $gt: [ { $year: "$date" }, "$$movie_year" ] } } }, { $project: { _id: 0, name: 1, date: 1 } } ], as: "post_release_comments" } }, { $project: { _id: 0, title: 1, year: 1, post_release_comments: 1 } } ] )
[ { year: 1991, title: 'Class Action', post_release_comments: [ { name: 'Khal Drogo', date: ISODate('2016-12-06T07:17:03.000Z') } ] }, { year: 1991, title: 'Kafka', post_release_comments: [ { name: 'Khal Drogo', date: ISODate('1998-05-10T03:10:20.000Z') } ] }, { year: 2005, title: 'Corpse Bride', post_release_comments: [] } ]
この操作は、次の疑似 SQL ステートメントに対応します。
SELECT *, post_release_comments FROM movies WHERE post_release_comments IN ( SELECT name, date FROM comments WHERE movie_id = movies._id AND YEAR(date) > movies.year );
$expr 演算子に配置されている $eq、$lt、$lte、$gt、$gte 比較演算子では、$lookup ステージで参照される from コレクションにインデックスを使用できます。制限:
インデックスはフィールドと定数の比較にのみ使用できるため、
letオペランドは定数に変換する必要があります。たとえば、
$aと定数値の比較にはインデックスを使用できますが、$aと$bの比較には使用できません。letオペランドが空の値または欠損値に変換される場合の比較には、インデックスは使用されません。
たとえば、インデックス{ movie_id: 1 } commentsコレクションに存在する場合:
comments.movie_idフィールドの等価一致はインデックスを使用します。
非相関サブクエリの実行 $lookup
集計パイプラインの $lookup ステージでは、外部コレクションに対してパイプラインを実行できるため、非相関サブクエリが可能になります。非相関サブクエリは、ローカル ドキュメント フィールドを参照しません。
注意
MongoDB5.0 以降では、$lookup $sampleステージ、$sampleRate 演算子、または$rand 演算子を含む、 パイプライン ステージ内の相関のないサブクエリの場合、繰り返される場合はサブクエリが常に再度実行されます。以前は、サブクエリの出力サイズに応じて、サブクエリの出力がキャッシュされるか、サブクエリが再度実行されていました。
次の操作では、moviesコレクションからの さらに 上映時間が 1000 分を超える映画と usersコレクションを結合します。
db.users.aggregate( [ { $match: { email: { $in: [ "mark_addy@gameofthron.es", "lena_headey@gameofthron.es" ] } } }, { $lookup: { from: "movies", pipeline: [ { $match: { runtime: { $gt: 1000 } } }, { $project: { _id: 0, title: 1, year: 1 } } ], as: "long_movies" } }, { $project: { _id: 0, name: 1, email: 1, long_movies: 1 } } ] )
[ { name: 'Robert Baratheon', email: 'mark_addy@gameofthron.es', long_movies: [ { title: 'Centennial', year: 1978 }, { title: 'Baseball', year: 1994 } ] }, { name: 'Cersei Lannister', email: 'lena_headey@gameofthron.es', long_movies: [ { title: 'Centennial', year: 1978 }, { title: 'Baseball', year: 1994 } ] } ]
この操作は、次の疑似 SQL ステートメントに対応します。
SELECT *, long_movies FROM users WHERE long_movies IN ( SELECT title, year FROM movies WHERE runtime > 1000 );
詳細については、「非相関サブクエリのパフォーマンスに関する検討事項 」を参照してください。
簡潔な相関サブクエリの実行 $lookup
バージョン 5.0 で追加
MongoDB 5.0以降、集計パイプラインの$lookupステージでは、コレクション間の結合を改善する簡潔な相関サブクエリ構文がサポートされています。 新しい簡潔な構文により、 ステージの$expr $match演算子内の外部フィールドとローカルフィールドを等価一致させる必要がなくなります。
次の例:
localField
_idを foreignFieldmovie_idと一致させることで、moviesコレクションとcommentsコレクションを結合します。一致はpipelineが実行される前に実行されます。それぞれ
$$movie_yearと$dateを使用してアクセスされた、映画の公開年後に投稿されたもののみを含むようにコメントをフィルタリングします。
db.movies.aggregate( [ { $match: { title: { $in: [ "I Don't Kiss", "Lucky Luke", "Mississippi Masala" ] } } }, { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", let: { movie_year: "$year" }, pipeline: [ { $match: { $expr: { $gt: [ { $year: "$date" }, "$$movie_year" ] } } }, { $project: { _id: 0, name: 1, date: 1 } } ], as: "post_release_comments" } }, { $project: { _id: 0, title: 1, year: 1, post_release_comments: 1 } } ] )
[ { title: "I Don't Kiss", year: 1991, post_release_comments: [ { name: 'Brandon Hardy', date: ISODate('2016-09-18T11:11:34.000Z') } ] }, { title: 'Lucky Luke', year: 1991, post_release_comments: [ { name: 'Kelsey Smith', date: ISODate('2010-01-13T17:55:01.000Z') } ] }, { title: 'Mississippi Masala', year: 1991, post_release_comments: [ { name: 'Phillip Collins', date: ISODate('2010-05-13T08:04:22.000Z') } ] } ]
この例では、MongoDB バージョン 5.0 以前の古い冗語構文を使用しており、以前の簡潔な例と同様の結果を返します。
db.movies.aggregate( [ { $match: { title: { $in: [ "I Don't Kiss", "Lucky Luke", "Mississippi Masala" ] } } }, { $lookup: { from: "comments", let: { movie_id: "$_id", movie_year: "$year" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: [ "$movie_id", "$$movie_id" ] }, { $gt: [ { $year: "$date" }, "$$movie_year" ] } ] } } }, { $project: { _id: 0, name: 1, date: 1 } } ], as: "post_release_comments" } }, { $project: { _id: 0, title: 1, year: 1, post_release_comments: 1 } } ] )
[ { title: "I Don't Kiss", year: 1991, post_release_comments: [ { name: 'Brandon Hardy', date: ISODate('2016-09-18T11:11:34.000Z') } ] }, { title: 'Lucky Luke', year: 1991, post_release_comments: [ { name: 'Kelsey Smith', date: ISODate('2010-01-13T17:55:01.000Z') } ] }, { title: 'Mississippi Masala', year: 1991, post_release_comments: [ { name: 'Phillip Collins', date: ISODate('2010-05-13T08:04:22.000Z') } ] } ]
前の例は、次の疑似 SQL ステートメントに対応します。
SELECT *, post_release_comments FROM movies WHERE post_release_comments IN ( SELECT * FROM comments WHERE comments.movie_id = movies._id AND YEAR(comments.date) > movies.year );
詳細については、「相関サブクエリのパフォーマンスに関する検討事項」を参照してください。
このページのC#の例では、Atlasサンプルデータセット の sample_mflixデータベースを使用します。MongoDB Atlasクラスターを無料で作成して、サンプルデータセットをロードする方法については、 MongoDB .NET/ C#ドライバーのドキュメントの「 開始 」を参照してください。
次の Movie クラスは、sample_mflix.movies コレクション内のドキュメントをモデル化します。
public class Movie { public ObjectId Id { get; set; } public int Runtime { get; set; } public string Title { get; set; } public string Rated { get; set; } public List<string> Genres { get; set; } public string Plot { get; set; } public ImdbData Imdb { get; set; } public int Year { get; set; } public int Index { get; set; } public string[] Comments { get; set; } [] public DateTime LastUpdated { get; set; } }
注意
パスカルケースの ConventionPack
このページのC# クラスはプロパティ名にパスカルケースを使用していますが、MongoDB コレクションのフィールド名はキャメルケースを使用しています。この違いを考慮するために、アプリケーションが起動する際に次のコードを使用してConventionPackを登録してください。
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
次の Comment クラスは、sample_mflix.comments コレクション内のドキュメントをモデル化します。
public class Comment { public Guid Id { get; set; } [] public Guid MovieId { get; set; } public string Text { get; set; } }
MongoDB .NET/ C#ドライバーを使用して$lookup ステージを集計パイプラインに追加するには、 オブジェクトでLookup() PipelineDefinitionメソッドを呼び出します。
次の例では、movies コレクションと comments コレクション間で左外部結合を実行するパイプラインステージを作成します。このコードは、各 Movieドキュメントの IdフィールドをComment ドキュメントの MovieIdフィールドに結合します。各映画のコメントは、各 Movieドキュメントの Comments という名前のフィールドに保存されます。
var commentCollection = client .GetDatabase("aggregation_examples") .GetCollection<Comment>("comments"); var pipeline = new EmptyPipelineDefinition<Movie>() .Lookup<Movie, Movie, Comment, Movie>( foreignCollection: commentCollection, localField: m => m.Id, foreignField: c => c.MovieId, @as: m => m.Comments);
このページのNode.js の例では、Atlasサンプルデータセット の sample_mflixデータベースを使用します。無料のMongoDB Atlas cluster を作成し、サンプルデータセットをロードする方法については、 MongoDB Node.jsドライバーのドキュメントの開始を参照してください。
MongoDB Node.jsドライバーを使用して $lookup ステージを集計パイプラインに追加するには、パイプラインオブジェクトで $lookup 演算子を使用します。
次の例では、movies コレクションと comments コレクションの間で左外部結合を実行するパイプラインステージを作成します。このコードは、各 movie ドキュメントの _id フィールドを comment ドキュメントの movie_id フィールドに結合します。comments フィールドは、各 movie ドキュメントの各映画に対するコメントを保存します。次に、この例は集計パイプラインを実行します。
const pipeline = [ { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", as: "comments" } } ]; const cursor = collection.aggregate(pipeline); return cursor;