MongoDB: 検索演算子:30以上の演算子完全ガイド
最終更新:2026-08-26
検索演算子はMongoDB検索の核です。30以上の演算子をマスターすれば、90%の検索シーンに対応できます。
本コースでは、全検索演算子の使い方、組み合わせ、パフォーマンス最適化テクニックを体系的に解説します。
1. 学習内容
- 比較演算子($eq/$ne/$gt/$lt/$gte/$lte/$in/$nin)
- 論理演算子($and/$or/$not/$nor)
- 要素演算子($exists/$type)
- 配列演算子($all/$size/$elemMatch)
- 評価演算子($regex/$expr/$mod)
- 演算子の組み合わせとパフォーマンスへの影響
2. データアナリストの実話
(1) 課題:複雑な検索にネストしたif-else文が必要
Dianaはeコマースのデータアナリストで、以下の条件を満たす商品を探す必要があります:「価格100-500、在庫> 0、タグに'bestseller'または'new'を含む、評価>= 4」:
// ❌ 反例:全件検索してからフィルター
const allProducts = await Product.find();
const filtered = allProducts.filter(p =>
p.price >= 100 && p.price <= 500 &&
p.stock > 0 &&
(p.tags.includes('bestseller') || p.tags.includes('new')) &&
p.rating >= 4
);
// 10万件のデータ、100件のみ返却、しかし全件チェック済み
(2) MongoDB演算子による解決策
// ✅ 正例:単一クエリ、演算子を組み合わせ
const products = await Product.find({
price: { $gte: 100, $lte: 500 },
stock: { $gt: 0 },
tags: { $in: ['bestseller', 'new'] },
rating: { $gte: 4 }
});
// データベースレベルのフィルタリング、100万件→100件、パフォーマンス↑1000倍
graph TB
A[検索演算子] --> B[比較演算子<br/>$eq/$gt/$lt]
A --> C[論理演算子<br/>$and/$or/$not]
A --> D[要素演算子<br/>$exists/$type]
A --> E[配列演算子<br/>$all/$size/$elemMatch]
A --> F[評価演算子<br/>$regex/$expr/$mod]
style B fill:#d4edda
style E fill:#d4edda
3. 比較演算子
概念概要: 比較演算子は最も基本的で最もよく使われる検索演算子のカテゴリです。等価、不等価、より大きい、より小さい、範囲、複数値マッチングなどの操作をサポートします。8つの比較演算子は日常的な検索シーンの90%をカバーし、$inと範囲検索($gt/$gte/$lt/$lteの組み合わせ)が最も頻繁に使用されます。
仕組み: 比較演算子は単一フィールドに作用し、MongoDBはそれらをインデックススキャンまたは全表スキャンに変換します。$eqと$inは最もインデックスフレンドリー(完全一致または複数値スキャンをサポート)であり、範囲検索($gt/$lt)もインデックスを使用できますが、効率は範囲サイズに依存します。$neと$ninはインデックスフレンドリーではありません。「ある値と等しくない」ドキュメントはインデックス全体に散在する可能性があるためです。
graph TB
A[比較演算子] --> B[等価類<br/>$eq / $ne]
A --> C[範囲類<br/>$gt / $gte / $lt / $lte]
A --> D[複数値類<br/>$in / $nin]
B --> B1[インデックス対応:$eq ✅ / $ne ⚠️]
C --> C1[インデックス対応:✅ 範囲スキャン]
D --> D1[インデックス対応:$in ✅ / $nin ⚠️]
| 演算子 | 意味 | SQL相当 | インデックス対応 | 使用頻度 |
|---|---|---|---|---|
$eq |
等しい | = value |
✅ | ⭐⭐⭐(デフォルト) |
$ne |
等しくない | != value |
⚠️ | ⭐ |
$gt |
より大きい | > value |
✅ | ⭐⭐ |
$gte |
以上 | >= value |
✅ | ⭐⭐⭐ |
$lt |
より小さい | < value |
✅ | ⭐⭐ |
$lte |
以下 | <= value |
✅ | ⭐⭐⭐ |
$in |
配列に含まれる | IN (...) |
✅ | ⭐⭐⭐ |
$nin |
含まれない | NOT IN (...) |
⚠️ | ⭐ |
(1) 全一覧
| 演算子 | 意味 | 例 |
|---|---|---|
$eq |
等しい | { price: { $eq: 599 } } |
$ne |
等しくない | { status: { $ne: 'deleted' } } |
$gt |
より大きい | { age: { $gt: 18 } } |
$gte |
以上 | { rating: { $gte: 4 } } |
$lt |
より小さい | { stock: { $lt: 10 } } |
$lte |
以下 | { discount: { $lte: 0.5 } } |
$in |
配列に含まれる | { category: { $in: ['A', 'B'] } } |
$nin |
配列に含まれない | { status: { $nin: ['deleted', 'banned'] } } |
▶ サンプル 1:比較演算子の実践
// === 価格範囲 ===
db.products.find({
price: { $gte: 100, $lte: 500 } // 100 <= price <= 500
});
// === 複数値マッチング ===
db.products.find({
category: { $in: ['Electronics', 'Books'] }
});
// === 特定値の除外 ===
db.users.find({
role: { $nin: ['banned', 'deleted'] }
});
4. 論理演算子
概念説明明: 論理演算子は複数の検索条件を組み合わせてAND/OR/NOTロジックを実装します。MongoDBで最も一般的なロジックは暗黙のANDです。{ a: 1, b: 2 }は{ $and: [{ a: 1 }, { b: 2 }] }と同等です。明示的な$andは「同一フィールドに複数条件がある」場合にのみ必要です(例:{ $and: [{ price: { $gte: 100 } }, { price: { $lte: 500 } }] })。
仕組み: $andは全てのサブ条件が同時に満たされる必要があります。MongoDBオプティマイザーは最も選択性の高い条件を優先実行します。$orはいずれか1つのサブ条件が満たされる必要があります。MongoDBは各サブ条件のクエリを独立して実行し、結果セットをマージします。つまり$orの各サブ条件にはインデックスがあることが望ましく、そうでなければ複数の全表スキャンが発生します。
graph TB
A[論理演算子] --> B[暗黙のAND<br/>{ a: 1, b: 2 }<br/>最も使用頻度が高い ⭐⭐⭐]
A --> C[$and<br/>同一フィールドに複数条件]
A --> D[$or<br/>いずれかを満たす ⭐⭐]
A --> E[$not<br/>否定 ⭐]
A --> F[$nor<br/>全条件を満たさない]
style B fill:#d4edda
| 演算子 | 意味 | SQL相当 | 注意点 |
|---|---|---|---|
| 暗黙のAND | カンマ区切り | WHERE a=1 AND b=2 |
最も一般的な記述 |
$and |
明示的AND | WHERE (a=1 AND b=2) |
同一フィールドに複数条件の場合に必須 |
$or |
いずれかを満たす | WHERE a=1 OR b=2 |
各サブ条件にインデックス推奨 |
$not |
満たさない | WHERE NOT (cond) |
$regexとの組み合わせでよく使用 |
$nor |
全て満たさない | WHERE NOT (a=1 OR b=2) |
低使用頻度 |
(1) 全一覧
| 演算子 | 意味 |
|---|---|
$and |
全て満たす |
$or |
いずれかを満たす |
$not |
満たさない |
$nor |
全て満たさない |
▶ サンプル 2:論理演算子の実践的応用
// === 暗黙の$and ===
db.products.find({
category: 'Electronics',
stock: { $gt: 0 }
// { $and: [{ category: 'Electronics' }, { stock: { $gt: 0 } }] } と同等
});
// === 明示的$and(同一フィールドに複数条件)===
db.products.find({
$and: [
{ price: { $gte: 100 } },
{ price: { $lte: 500 } }
// $andが必須、同一フィールドに複数条件を直接指定できないため
]
});
// === $or ===
db.products.find({
$or: [
{ category: 'Electronics' },
{ tags: 'bestseller' }
]
});
// === $nor(全て満たさない)===
db.products.find({
$nor: [
{ category: 'Books' },
{ stock: 0 }
]
});
// Booksカテゴリでなく、かつ在庫がある商品を返す
// === ネストされた論理 ===
db.products.find({
$and: [
{ category: 'Electronics' },
{
$or: [
{ tags: 'bestseller' },
{ rating: { $gte: 4.5 } }
]
}
]
});
5. 要素演算子
概念説明明: 要素演算子はドキュメント内のフィールドの存在と型をチェックします。$existsはフィールドの存在を判定し、$typeはフィールドのBSON型を判定します。これら2つの演算子はデータ品質チェックとダーティデータのクリーンアップに重要です。$existsは必須フィールドが欠落しているドキュメントを特定でき、$typeは型が間違っているフィールド(例:priceが誤ってstringとして保存されているなど)を特定できます。
仕組み: $exists: trueは指定フィールドを含む全ドキュメントにマッチします(フィールド値がnullのドキュメントを含む)。$exists: falseはそのフィールドを含まないドキュメントにマッチします。$typeはBSON型コードまたは型名でマッチし、配列形式もサポートします(例:{ price: { $type: ["number", "decimal"] } }はDoubleまたはDecimal128にマッチ)。
| 演算子 | 意味 | 典型的なシーン |
|---|---|---|
$exists: true |
フィールドが存在 | discountフィールドを持つ商品を検索 |
$exists: false |
フィールドが不在 | emailが欠落したユーザーを検索 |
$type: "string" |
フィールド型が文字列 | priceが誤って文字列として保存されたドキュメントを検索 |
$type: ["number", "decimal"] |
いずれかの型 | priceが任意の数値型のドキュメントを検索 |
(1) $exists フィールドの存在チェック
// === フィールドが存在 ===
db.products.find({ discount: { $exists: true } });
// discountフィールドを持つ商品
// === フィールドが不在 ===
db.users.find({ lastLoginAt: { $exists: false } });
// ログイン履歴のないユーザー
// === 他条件との組み合わせ ===
db.products.find({
discount: { $exists: true, $ne: null }
});
// discountフィールドがあり、かつnullではない
(2) $type:フィールド型のチェック
// === 型コード参考 ===
// 1: Double, 2: String, 3: Object, 4: Array
// 5: Binary, 7: ObjectId, 8: Boolean, 9: Date
// 10: Null, 16: Int32, 18: Long, 19: Decimal128
// === 文字列型 ===
db.products.find({ sku: { $type: "string" } });
// === 数値型(各種)===
db.products.find({ price: { $type: ["number", "decimal"] } });
// Double または Decimal128
// === 実践応用:型エラーのフィールドを特定 ===
db.products.find({ price: { $type: "string" } });
// priceフィールドが誤って文字列として保存されたドキュメントを検索(データ移行時のよくある問題)
▶ サンプル 3:要素演算子の実践
// === フィールド欠落の「ダーティ」データを検索 ===
db.users.find({
$or: [
{ email: { $exists: false } },
{ username: { $exists: false } }
]
});
// === 型修正 ===
db.products.find({ price: { $type: "string" } }).forEach(doc => {
db.products.updateOne(
{ _id: doc._id },
{ $set: { price: NumberDecimal(doc.price) } }
);
});
6. 配列演算子
概念説明明: 配列演算子はMongoDB検索演算子の中で最もユニークなカテゴリです。配列フィールド専用に設計されています。$allは指定要素を全て含む配列にマッチ、$sizeは特定長の配列にマッチ、$elemMatchは配列内の単一要素が複数条件を同時に満たす場合にマッチします。この中で$elemMatchが最もエラーが起きやすく、最も重要な配列演算子です。
仕組み: デフォルトでは、MongoDBの配列検索は「任意要素マッチ」を使用します。{ tags: "5g" }はtags配列に"5g"を含むドキュメントにマッチします。しかし複数条件検索では{ "reviews.rating": {$gte:4}, "reviews.content": /good/ }は異なる配列要素にマッチする可能性があります(あるコメントが高評価、別のコメントがキーワードを含む)。$elemMatchはこの問題を解決します。同一配列要素が全条件を同時に満たすことを保証します。
graph TB
A[配列演算子] --> B[$all<br/>指定要素を全て含む]
A --> C[$size<br/>配列長の完全一致]
A --> D[$elemMatch<br/>同一要素の複数条件マッチ<br/>⭐ 最重要]
D --> D1[ドット記法<br/>異なる要素がマッチする可能性]
D --> D2[$elemMatch<br/>同一要素がマッチ必須]
style D fill:#d4edda
style D2 fill:#d4edda
| 演算子 | 意味 | 構文 | 注意点 |
|---|---|---|---|
$all |
指定要素を全て含む | { tags: { $all: ["5g", "amoled"] } } |
順序不問 |
$size |
配列長の完全一致 | { tags: { $size: 3 } } |
範囲非対応、$exprを使用 |
$elemMatch |
同一要素の複数条件マッチ | { reviews: { $elemMatch: { ... } } } |
ドット記法との区別 |
(1) $all 全要素をマッチ
// === 指定要素を全て含む(順序不問)===
db.products.find({ tags: { $all: ['5g', 'amoled'] } });
// 5gとamoled両方を含む商品
// === $andと同等 ===
db.products.find({
$and: [
{ tags: '5g' },
{ tags: 'amoled' }
]
});
(2) $size 配列長のマッチ
// === 配列長の完全一致 ===
db.products.find({ tags: { $size: 3 } });
// tagsの数が正確に3つ
// === 範囲非対応、$exprが必要 ===
db.products.find({
$expr: { $gt: [{ $size: "$tags" }, 3] }
});
// tagsの数 > 3
(3) $elemMatch 要素マッチ(重要!)
// === シーン:レビューの中で評価>= 4かつキーワード"good"を含むコメントを検索 ===
db.products.find({
reviews: {
$elemMatch: {
rating: { $gte: 4 },
content: /good/i
}
}
});
// ✅ 正しい:単一コメントが両方の条件を同時に満たす
// === 反例:$elemMatchを使用しない ===
db.products.find({
"reviews.rating": { $gte: 4 },
"reviews.content": /good/i
});
// ⚠️ エラー:異なるコメントがマッチする可能性(あるコメントが高評価、別のコメントがキーワードを含む)
▶ サンプル 4:配列演算子の実践
// === シーン:学生の履修登録システム ===
db.students.insertMany([
{
name: "Alice",
courses: [
{ name: "Math", score: 85 },
{ name: "Physics", score: 92 },
{ name: "Chemistry", score: 78 }
]
},
// ...
]);
// 検索:Mathを選択し、かつ成績>= 90の学生
db.students.find({
courses: {
$elemMatch: { name: "Math", score: { $gte: 90 } }
}
});
// 検索:3科目以上履修している学生
db.students.find({
$expr: { $gte: [{ $size: "$courses" }, 3] }
});
// 検索:全科目の成績が>= 80の学生
db.students.find({
courses: {
$not: {
$elemMatch: { score: { $lt: 80 } }
}
}
});
7. 評価演算子
概念説明明: 評価演算子はより柔軟な検索機能を提供します。$regexは正規表現マッチング(あいまい検索)をサポート、$exprは集計式を使用したフィールド間比較をサポート、$modは剰余演算をサポートします。これらの演算子は強力ですがパフォーマンスが低く、「最後の手段」として使用すべきです。インデックスフレンドリーな検索方法を優先してください。
仕組み: $regexはデフォルトでインデックスを使用しません(プレフィックスアンカークエリ^patternを除く)。正規表現マッチングのために全ドキュメントをスキャンする必要があります。$exprは集計パイプライン式を使用する場合、各ドキュメントで式の結果を計算してから比較するため、パフォーマンスが低くなります。これらは高頻度クエリの主条件として使用すべきではありません。
| 演算子 | 意味 | インデックス対応 | パフォーマンス | 用途 |
|---|---|---|---|---|
$regex |
正規表現マッチング | ⚠️ プレフィックスのみ | 遅い | あいまい検索 |
$expr |
集計式 | ❌ | 非常に遅い | フィールド間比較 |
$mod |
剰余演算 | ❌ | 遅い | 剰余フィルタリング |
(1) $regex 正規表現
// === 単純な正規表現 ===
db.products.find({ title: /^iPhone/ });
// タイトルが"iPhone"で始まる
// === 大文字小文字を区別しない ===
db.products.find({ title: { $regex: 'phone', $options: 'i' } });
// === 複数パターンマッチング ===
db.products.find({
title: { $regex: 'phone|mobile', $options: 'i' }
});
// === パフォーマンス最適化:正規表現アンカー(^.*ワイルドカードを回避)===
// ✅ 高速: db.products.find({ title: /^iPhone/ })
// ⚠️ 低速: db.products.find({ title: /iPhone/ })
(2) $expr 集計式を使用
// === フィールド間比較 ===
db.products.find({
$expr: { $gt: ["$discount", "$price"] }
});
// discount > price(異常データ)
// === 配列長の比較 ===
db.products.find({
$expr: { $gte: [{ $size: "$tags" }, 5] }
});
// === 文字列操作 ===
db.products.find({
$expr: { $eq: [{ $substr: ["$title", 0, 5] }, "Hello"] }
});
(3) $mod:剰余
// === フィールド値をNで割った余り ===
db.products.find({ price: { $mod: [100, 0] } });
// priceが100で割り切れる
// === priceを100で割った余りが50 ===
db.products.find({ price: { $mod: [100, 50] } });
8. 総合実習
概念説明明: 実際のビジネスシーンでは、検索は単一演算子を使用することは稀で、通常は複数演算子の組み合わせです。組み合わせ検索の主要原則は:最も選択性の高い条件を先に配置(後続条件がスキャンするデータ量を削減)、全ての$or節のフィールドにインデックスを作成、$exprで複雑な集計式を避けることです。
パフォーマンス最適化の原則:
- 等価検索(
$eq/$in)が最も選択性が高く、フィルターの最初に配置 - 範囲検索(
$gt/$lt)が次に選択性が高く、等価条件の後に配置 $orの各節は独立したインデックスサポートを持つべき$regexはプレフィックス検索のみインデックスを使用、それ以外は全表スキャン$exprのパフォーマンスが最も低い、最後の手段として使用
graph TB
A[総合検索最適化] --> B[1. 等価フィルタリング<br/>選択性最高]
A --> C[2. 範囲フィルタリング<br/>選択性次点]
A --> D[3. 論理組み合わせ<br/>$and/$or]
A --> E[4. ソート + ページネーション<br/>sort + limit]
B --> B1[{ category: 'E' }<br/>インデックススキャン]
C --> C1[{ price: { $gte: 100 } }<br/>範囲スキャン]
D --> D1[複数条件の組み合わせ<br/>結果セットマージ]
E --> E1[インデックスソート<br/>メモリソート回避]
style B1 fill:#d4edda
| 最適化戦略 | 検索方法 | インデックス要件 | パフォーマンス |
|---|---|---|---|
| 最適 | 等価 + インデックスソート | 複合インデックス | O(log N) |
| 良好 | 等価 + 範囲 + インデックス | 複合インデックス | O(log N + K) |
| 普通 | $or + 各節のインデックス | 複数の独立インデックス | O(K * log N) |
| 低い | $regex プレフィックスアンカー | プレフィックスインデックス | O(log N + K) |
| 最悪 | $expr / インデックスなしのソート | なし | O(N log N) |
(1) 複雑検索の例
// === シーン:eコマースの高度な検索 ===
// 価格100-1000、ElectronicsまたはComputers、在庫> 0、
// タグに'bestseller'または'new'を含む、評価>= 4、割引あり
db.products.find({
$and: [
{ price: { $gte: 100, $lte: 1000 } },
{
$or: [
{ category: 'Electronics' },
{ category: 'Computers' }
]
},
{ stock: { $gt: 0 } },
{ tags: { $in: ['bestseller', 'new'] } },
{ rating: { $gte: 4 } },
{ discount: { $exists: true, $ne: null } }
]
});
▶ サンプル 5:演算子のパフォーマンス比較
// === パフォーマンステスト:100万ドキュメント ===
// ❌ 低速:インデックスなしのフィールド + $exists
db.users.find({ lastLoginAt: { $exists: true } });
// ~500ms(全表スキャン)
// ✅ 高速:インデックスフィールド + 範囲検索
db.users.find({ age: { $gte: 18, $lt: 30 } });
// ~5ms(インデックススキャン)
// ❌ 低速:アンカーなしの正規表現
db.products.find({ title: /phone/i });
// ~300ms(全表スキャン + 正規表現マッチング)
// ✅ 高速:正規表現アンカー
db.products.find({ title: /^iPhone/ });
// ~10ms(プレフィックスインデックス)
❓ よくある質問
{ field: { $in: [A, B] } }は{ $or: [{ field: A }, { field: B }] }と同等です。パフォーマンスは同じですが、$inの方が簡潔なため推奨されます。{ "a.b": 1, "a.c": 2 }は異なる配列要素がbとcにマッチする可能性があります。{ a: { $elemMatch: { b: 1, c: 2 } } }は同一要素が両方の条件を同時に満たす必要があります。^patternを除く)。アンカーなしの正規表現はマッチングのために全ドキュメントをスキャンする必要があり、パフォーマンスが低いです。📖 まとめ
- 比較演算子:$eq/$ne/$gt/$lt/$gte/$lte/$in/$nin
- 論理演算子:$and(暗黙)/$or/$not/$nor
- 要素演算子:$exists(フィールド存在)/$type(フィールド型)
- 配列演算子:$all(全て含む)/$size(長さ)/$elemMatch(要素マッチ)
- 評価演算子:$regex(正規表現)/$expr(集計式)/$mod(剰余)
- パフォーマンス最適化:インデックスフィールド検索 / 正規表現プレフィックス / $expr回避
📝 練習問題
- 基礎問題(⭐):価格が100〜500の範囲で在庫が0より大きいElectronics商品を検索してください。
- 基礎問題(⭐):$inを使用して複数カテゴリ(Electronics/Books/Clothing)の商品を検索してください。
- 応用問題(⭐⭐):$elemMatchを使用して、レビューの中で「評価>= 4かつキーワード'good'を含む」商品を検索してください。
- 応用問題(⭐⭐):$exprを使用して、タグが3つ以上あり、かつ評価が4以上の商品を検索してください。
- チャレンジ(⭐⭐⭐):複雑な検索(5つ以上の演算子の組み合わせ)を構築し、AND/OR/NOT、$elemMatch、$regexアンカーを含め、パフォーマンスの違いをテストしてください。