HOWTO · MongoDB

MongoDB で複数の条件を使用してクエリを実行する

この説明記事では、MongoDB で複数の条件に基づいて複数のドキュメントを検索およびクエリするために使用できる演算子について説明します。 $and および $or 演算子について簡単に説明します。

このページの内容

find() とフィルタードキュメントを使うと、複数の条件で MongoDB を検索できます。同じフィルター内のフィールド条件は既定で AND として組み合わされます。列挙した条件のいずれかに一致させるには $or、すべてに一致させるには $and、1つのフィールドで等価条件の候補を指定するには $in を使います。

MongoDB でクエリ条件が組み合わされる仕組み

クエリフィルターは、返すドキュメントを指定します。たとえば、db.inventory.find({ quantity: { $gt: 10 }, price: { $lt: 20 } }) は、quantity が 10 より大きく、かつ price が 20 より小さい在庫ドキュメントに一致します。MongoDB は別々のフィールド条件を暗黙に AND で組み合わせるため、この場合に $and を明示する必要はありません。

以下のシェル例では、db.inventory に次の3つのドキュメントが含まれているものとします。{ item: "notebook", quantity: 30, price: 12 }、{ item: "pen", quantity: 80, price: 20 }、{ item: "folder", quantity: 80, price: 8 }。最初のクエリには notebook と folder が一致します。どちらも数量が10を超え、価格が20未満だからです。pen の価格は20なので、価格条件には一致しません。フィルターにフィールドを追加した場合も同じで、各条件に一致する必要があります。そのため、すべての条件を満たすドキュメントを検索するときは、通常のフィルターから始めるのが最も明快です。フィールド名と値はコレクション内のドキュメントに合わせてください。MongoDB のフィールド名は大文字と小文字を区別します。

$or でいずれかの条件に一致させる

$or は、配列内の条件のうち少なくとも1つに一致するドキュメントを選びます。たとえば、db.inventory.find({ $or: [{ quantity: { $lt: 51 } }, { price: 20 }] }) は、数量が51未満であるか、価格が20と等しいドキュメントを返します。{ price: 20 } は等価条件であり、価格が20未満という意味ではありません。比較を表すには $lt などの演算子を使います。

いずれかの条件に一致する仕組みを、小さなローカルの例で見てみましょう。

このセクションのコードと表示されるラベルや出力は英語のままです。

inventory = [
    {"item": "notebook", "quantity": 30, "price": 12},
    {"item": "pen", "quantity": 80, "price": 20},
    {"item": "folder", "quantity": 80, "price": 8},
]
matched = [
    item["item"]
    for item in inventory
    if item["quantity"] < 51 or item["price"] == 20
]
print(matched)
['notebook', 'pen']

この Python の例は、ローカルの Python リストに対して選択ロジックを適用するもので、MongoDB には接続しません。出力には、notebook(数量が51未満)と pen(価格が20と等しい)が表示されます。変更されていない例では、サンプルデータの項目名「notebook」と「pen」は英語のままです。上の MongoDB シェルクエリは、別途説明した db.inventory のサンプルに対して find() を実行し、同じ「いずれかの条件に一致する」ルールを適用します。

$and ですべての条件を必須にする

$and は、配列内のすべての条件に一致する場合にのみドキュメントを選びます。サンプルの db.inventory コレクションで、db.inventory.find({ $and: [{ quantity: { $gte: 20 } }, { price: { $lt: 15 } }] }) は、数量が20以上で、かつ価格が15未満であることを求めます。notebook と folder は、どちらも数量が20以上で価格が15未満なので一致します。

異なるフィールドを使う場合、同じ条件を暗黙の AND で表すフィルターは db.inventory.find({ quantity: { $gte: 20 }, price: { $lt: 15 } }) です。各フィールドが1回ずつしか出てこないなら、この短い形式を使うのがおすすめです。同じフィールドに複数の式を組み合わせる必要がある場合は、明示的な $and が役立ちます。たとえば、db.inventory.find({ $and: [{ price: { $gte: 10 } }, { price: { $lte: 20 } }] }) のように記述できます。また、プログラムでフィルターを組み立てるときに、条件のまとまりを明示する用途にも使えます。

別の条件でエラーが起きないようにする目的で、$and 内の条件の評価順に頼らないでください。クエリ式は一致させる条件であり、左から右へ短絡評価される保証はありません。

インデックスと演算子の制限

インデックスはクエリのパフォーマンスに関係することがありますが、特定のクエリが恩恵を受けるかどうかは、コレクション、インデックス、クエリによって異なります。自分のワークロードのパフォーマンスを評価するときは、フィルター内のフィールドと条件を検討してください。この記事では、特定のインデックスやクエリプランは指定しません。

条件を組み合わせるときは、それぞれの演算子の役割を明確にしてください。$or は候補のいずれかへの一致を表し、$and はすべての条件への一致を求めます。また、1つのフィルターに通常のフィールド条件を並べると、すでに AND が暗黙に適用されます。この違いを理解すると、クエリの意味を変えない論理演算子を不必要に追加せずに済みます。

1つのフィールドで等価条件の候補を指定するには $in を使う

同じフィールドに対する等価条件の候補が複数ある場合、そのフィールドを $or で繰り返すより $in を使うほうが簡潔です。たとえば、db.inventory.find({ price: { $in: [12, 20] } }) は、価格が12または20である notebook と pen に一致します。

候補が異なるフィールドに関係する場合や、数量の比較、価格の範囲指定など等価条件以外を使う場合は $or を使います。すべての条件に一致する必要があるときは、通常の複数フィールドフィルターを使います。どの形式を選ぶかは、必要な論理によって決まります。いずれかの条件なら $or、すべての条件なら $and(多くの場合は暗黙に適用)、1つのフィールドに対する列挙済みの等価値のいずれかなら $in です。