SQL INNER JOINをMongoDBの$lookupと$unwindに変換する

テーブル間のSQL INNER JOINがコレクション間結合のためにMongoDB $lookup集計ステージと$unwindに変換される方法を学びます。

Migration

詳細な説明

INNER JOINから$lookup + $unwindへ

SQLのINNER JOINは、結合条件が満たされる2つのテーブルの行を結合します。MongoDBは$lookup集計ステージ(left outer joinを実行)と、結果をフラット化する$unwindを組み合わせてこれを処理します。

SQLの例

SELECT orders.id, orders.total, users.name
FROM orders
INNER JOIN users ON orders.user_id = users.id

生成されるMongoDBクエリ

db.orders.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "user_id",
      foreignField: "id",
      as: "users"
    }
  },
  { $unwind: "$users" },
  {
    $project: {
      id: 1,
      total: 1,
      "users.name": 1
    }
  }
])

$lookupの仕組み

$lookupは現在のコレクションと外部コレクション間のleft outer joinを実行します。主要なパラメータ:

  • from: 結合する外部コレクション
  • localField: 入力ドキュメントのフィールド
  • foreignField: 外部コレクションのフィールド
  • as: 出力配列フィールド名

なぜ$unwindが必要か?

$lookupasフィールドにマッチするドキュメントの配列を生成します。INNER JOIN相当のために、$unwindは以下の目的で使用されます:

  1. 配列をフラット化し、各マッチするドキュメントが個別の結果になる
  2. 空の配列(マッチなし)を持つドキュメントを削除し、INNER JOINの動作をシミュレート

$unwindなしでは、各結果ドキュメントはすべてのマッチする外部ドキュメントの配列を含みます。

複数のJOIN

3つ以上のテーブルを結合するクエリでは、複数の$lookup + $unwindステージを連鎖させます:

db.orders.aggregate([
  { $lookup: { from: "users", localField: "user_id", foreignField: "id", as: "user" } },
  { $unwind: "$user" },
  { $lookup: { from: "products", localField: "product_id", foreignField: "id", as: "product" } },
  { $unwind: "$product" }
])

パフォーマンスの考慮事項

MongoDBの$lookupは、SQLがインデックス付き結合を使用するのと同じ方法で外部コレクションのforeignFieldのインデックスを使用しません。高パフォーマンスの結合のために、外部フィールドにインデックスを付与し、頻繁な結合が必要な場合はデータモデルの非正規化を検討してください。

ユースケース

顧客名付きの注文詳細やメーカー情報付きの商品カタログを表示する必要がある注文管理システムではテーブル結合が必要です。$lookupの理解は、同じクエリパターンを維持しながらリレーショナルスキーマをMongoDBに移行する際に重要です。

試してみる — SQL to MongoDB Query

フルツールを開く