Query Operators
Query operators live inside the filter document you pass to find(), updateOne(), deleteMany(), and friends. They're grouped by purpose: comparison, logical, element, and evaluation operators. Together they cover almost every filtering need without writing raw JavaScript.
Comparison Operators
Operator | Meaning |
|---|---|
$eq | Equal to |
$ne | Not equal to |
$gt / $gte | Greater than / greater than or equal to |
$lt / $lte | Less than / less than or equal to |
$in | Matches any value in a given array |
$nin | Matches none of the values in a given array |
$eq, $ne
db.products.find({ category: { $eq: "widgets" } }) // same as { category: "widgets" }
db.products.find({ category: { $ne: "widgets" } }) // everything except widgets$gt, $gte, $lt, $lte — ranges
db.products.find({ price: { $gt: 10 } })
db.products.find({ price: { $gte: 10, $lte: 50 } }) // range: 10 <= price <= 50
db.orders.find({ createdAt: { $lt: ISODate("2024-01-01") } })$in, $nin
db.products.find({ category: { $in: ["widgets", "gadgets"] } })
db.products.find({ status: { $nin: ["discontinued", "recalled"] } })$in with an array of values is almost always faster and cleaner than $or-ing several equality checks on the same field — MongoDB can use a single index scan for $in across multiple values.Logical Operators
Operator | Meaning |
|---|---|
$and | All conditions must be true |
$or | At least one condition must be true |
$nor | None of the conditions may be true |
$not | Negates a single condition (applied to one operator expression) |
$and — implicit vs explicit
// Implicit AND — separate fields in the same document are already ANDed
db.products.find({ category: "widgets", price: { $lt: 20 } })
// Explicit $and — required when combining multiple conditions on the SAME field
db.products.find({
$and: [
{ price: { $gte: 10 } },
{ price: { $lte: 50 } }
]
})$or, $nor
db.products.find({
$or: [{ category: "widgets" }, { price: { $lt: 5 } }]
})
db.products.find({
$nor: [{ category: "widgets" }, { discontinued: true }]
})
// matches products that are NEITHER widgets NOR discontinued$not — negating a single condition
db.products.find({ price: { $not: { $gt: 100 } } })
// matches price <= 100 OR price missing OR price not a comparable type
// (different from { price: { $lte: 100 } }, which requires the field to exist and be comparable)$and. You only need an explicit $and when you must apply two or more conditions to the same field, since a plain object can't repeat a key twice.Element Operators
Operator | Meaning |
|---|---|
$exists | Matches documents that have (or lack) a given field |
$type | Matches documents where a field is a specific BSON type |
$exists
db.users.find({ middleName: { $exists: true } }) // field is present (even if null)
db.users.find({ middleName: { $exists: false } }) // field is absent entirely$type
db.products.find({ price: { $type: "string" } }) // catches price stored as a string by mistake
db.products.find({ price: { $type: ["int", "double", "decimal"] } }) // any numeric typeEvaluation Operators
Operator | Meaning |
|---|---|
$regex | Pattern-matches a string field |
$expr | Allows aggregation expressions inside a query filter, e.g. comparing two fields |
$mod | Matches when a field, divided by a divisor, has a given remainder |
$where | Runs arbitrary JavaScript per document — avoid in modern code |
$regex — pattern matching strings
db.users.find({ email: { $regex: /^alice/, $options: "i" } })
db.users.find({ email: /@example\.com$/ }) // native regex literal works directly, no $regex needed$expr — comparing two fields in the same document
// Find orders where the amount paid is less than the total owed
db.orders.find({ $expr: { $lt: ["$amountPaid", "$total"] } })$mod
// Even-numbered batch ids
db.batches.find({ batchId: { $mod: [2, 0] } })$where in new code. It executes arbitrary JavaScript per document on the server — far slower than native operators, unable to use indexes, and a historical injection surface if any part of the JS string comes from user input. Almost anything you'd reach for $where to do can be expressed with $expr and native aggregation operators instead.Combining Operators
A realistic combined filter
db.products.find({
category: { $in: ["widgets", "gadgets"] },
price: { $gte: 5, $lte: 100 },
stock: { $gt: 0 },
$or: [{ featured: true }, { rating: { $gte: 4.5 } }]
})Comparison:
$eq,$ne,$gt/$gte,$lt/$lte,$in,$nin.Logical:
$and,$or,$nor,$not— top-level fields are already ANDed implicitly.Element:
$exists,$type.Evaluation:
$regex,$expr,$mod— avoid$where.