SQL LEFT JOINをNull保持付きのMongoDB $lookupに変換する

SQL LEFT JOINが結果にマッチしないドキュメントを保持するためにpreserveNullAndEmptyArraysを使用した$lookupと$unwindに変換される方法を学びます。

Migration

詳細な説明

LEFT JOINからpreserveNullAndEmptyArrays付き$lookupへ

SQLのLEFT JOINは左テーブルのすべての行と、右テーブルのマッチする行(マッチしない場合はNULL)を返します。MongoDBは$lookuppreserveNullAndEmptyArrays: trueを設定した$unwindの組み合わせでこれを実現します。

SQLの例

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

生成されるMongoDBクエリ

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

preserveNullAndEmptyArrays

これがINNER JOINとの重要な違いです。このオプションをtrueに設定すると、$unwindに以下を指示します:

  • as配列がのドキュメント(マッチが見つからない)を保持
  • asフィールドがnullまたは欠落しているドキュメントを保持

このオプションなしでは、マッチしないドキュメントは削除され、INNER JOINのように動作します。

INNER JOIN vs LEFT JOINの比較

結合タイプ $unwind設定 マッチしないドキュメント
INNER JOIN { $unwind: "$field" } 削除される
LEFT JOIN { $unwind: { path: "$field", preserveNullAndEmptyArrays: true } } 保持される

結合フィールドへのアクセス

LEFT JOIN変換後、マッチしないドキュメントでは結合フィールドがnullになります。アプリケーションコードでは、ネストされたプロパティにアクセスする前にnull値をチェックしてください:

// アプリケーションコードで
const total = doc.orders ? doc.orders.total : null;

代わりに非正規化を検討する場合

同じコレクションで頻繁にLEFT JOINを実行している場合、関連データを親ドキュメントに直接埋め込む方が効率的かもしれません。MongoDBのドキュメントモデルはこの種の非正規化のために設計されており、ストレージスペースとクエリパフォーマンスをトレードオフします。

ユースケース

ユーザーの最新の注文やプロフィール設定などのオプションの関連データを表示するユーザープロフィールページでは、関連データが存在しなくてもユーザーレコードが常に返されることを保証するためにLEFT JOINを一般的に使用します。この変換パターンはMongoDBでもその保証を維持します。

試してみる — SQL to MongoDB Query

フルツールを開く