【ITニュース解説】Understanding MongoDB $lookup performance

2025年09月05日に「Dev.to」が公開したITニュース「Understanding MongoDB $lookup performance」について初心者にもわかりやすいように丁寧に解説しています。

作成日: 更新日:

ITニュース概要

MongoDBの `$lookup` のパフォーマンス問題について解説。遅い原因は `$expr` とpipeline内の処理。`$lookup` で外部コレクションと結合する際、`localField` と `foreignField` を使い、pipelineを避けると高速化する。複雑な条件が必要な場合のみ、pipelineと組み合わせるのがおすすめ。インデックスも有効活用しよう。

出典: Understanding MongoDB $lookup performance | Dev.to公開日:

ITニュース解説

この記事は、MongoDBの$lookupという機能のパフォーマンスについて解説している。特に、$lookupの中で$exprpipelineを使うと、クエリが非常に遅くなる場合があることを指摘し、その原因と改善策を説明している。

まず、$lookupは、SQLにおけるJOINのような役割を果たす機能だ。複数のコレクション(SQLでいうテーブルのようなもの)を関連付けて、一つの結果として取得できる。基本的な$lookupの構文は以下のようになる。

1{
2  $lookup:
3    {
4      from: "<結合先のコレクション>",
5      localField: "<入力ドキュメントのフィールド>",
6      foreignField: "<結合先コレクションのフィールド>",
7      as: "<出力配列のフィールド>"
8    }
9}

fromは結合先のコレクションの名前、localFieldは入力ドキュメント(結合元のドキュメント)のフィールド、foreignFieldは結合先コレクションのフィールド、asは結合結果を格納する配列の名前を指定する。localFieldforeignFieldを使うと、指定したフィールドの値が一致するドキュメント同士が結合される。

しかし、今回の記事で問題になっているのは、letpipelineを使った$lookupだ。letを使うと、変数を定義してpipelineの中で利用できる。pipelineは、複数のステージ(処理)を順番に実行する機能で、$matchなどを使って複雑な条件でフィルタリングできる。

記事の例では、removedAtというフィールドがnullであるかどうかを条件に加えるために、pipeline$exprを使っている。ソフトデリートを実現するために削除されたデータを物理的に削除せず、removedAtフラグで管理している場合に、有効なデータのみを抽出するためにこのような条件が必要になる。しかし、$exprはコレクション内のドキュメントを一つ一つ評価するため、インデックスが効率的に利用できず、コレクションが大きくなるにつれてパフォーマンスが著しく低下する。

改善策としては、可能な限りlocalFieldforeignFieldを使うことが推奨されている。localFieldforeignFieldを使うことで、MongoDBはインデックスを効率的に利用できるため、クエリのパフォーマンスが向上する。もしpipelineを使う必要がある場合でも、不要な処理を減らし、できる限りシンプルなpipelineを心がけることが重要だ。

記事では、以下の例を使って改善方法を説明している。元のクエリでは、$exprを使ってremovedAtnullであるかどうかを判定していた。

1{
2  "$lookup": {
3    "from": "Company",
4    "let": {
5      "company": "$company"
6    },
7    "pipeline": [
8      {
9        "$match": {
10          "removedAt": null,
11          "$expr": {
12            "$and": [
13              {
14                "$eq": [
15                  "$_id",
16                  "$$company"
17                ]
18              }
19            ]
20          }
21        }
22      }
23    ],
24    "as": "company"
25  }
26}

このクエリを、以下のようにpipelineの中で$matchを使う形に変更することで、パフォーマンスを改善している。

1{
2  "$lookup": {
3    "from": "Company",
4    "localField": "company",
5    "foreignField": "_id",
6    "pipeline": [
7      {
8        "$match": {
9          "removedAt": null
10        }
11      }
12    ],
13    "as": "company"
14  }
15}

さらに、pipeline自体が不要な場合は、以下のようにlocalFieldforeignFieldだけを使うことで、最も効率的なクエリを実現できる。

1{
2  "$lookup": {
3    "from": "Company",
4    "localField": "company",
5    "foreignField": "_id",
6    "as": "company"
7  }
8}

このように、MongoDBの$lookupを使う際には、$exprpipelineの使用を避け、可能な限りlocalFieldforeignFieldを使うことが、パフォーマンス向上のための重要なポイントとなる。特に大規模なデータを扱うシステムでは、わずかな変更でも大きな効果が得られるため、これらの点を意識してクエリを設計することが重要だ。

【ITニュース解説】Understanding MongoDB $lookup performance | いっしー@Webエンジニア