Real-world queries need more than exact matches: price ranges, multiple alternatives, missing fields, and combined conditions. MongoDB expresses all of these with query operators, special keys that start with a dollar sign. This CodeYourCraft lesson covers the three operator families you will use daily: comparison, logical, and element operators.
Comparison operators wrap the value you are testing against:
use shop
db.products.find({ price: { $gt: 1000 } }) // greater than
db.products.find({ price: { $gte: 1000, $lte: 5000 } }) // inclusive range
db.products.find({ price: { $ne: 499 } }) // not equal
db.products.find({ name: { $in: ['Desk Lamp', 'Webcam 1080p'] } })
db.products.find({ name: { $nin: ['Desk Lamp'] } }) // not in listThe $in operator is especially useful because it replaces a chain of OR conditions with one readable list, and it can even mix types and regular expressions. Note that $ne and $nin also match documents where the field is missing entirely, which sometimes surprises newcomers.
When implicit AND is not enough, four logical operators combine sub-queries. Each takes an array of filter documents:
// OR: cheap items or audio gear
db.products.find({
$or: [
{ price: { $lt: 1000 } },
{ tags: 'audio' }
]
})
// AND with two conditions on the SAME field
db.products.find({
$and: [
{ tags: { $ne: 'video' } },
{ tags: { $ne: 'audio' } }
]
})
// NOR: neither condition may match
db.products.find({ $nor: [ { price: { $lt: 500 } }, { tags: 'clearance' } ] })
// NOT negates one operator expression
db.products.find({ price: { $not: { $gt: 5000 } } })You only need explicit $and when you must apply the same field or operator twice in one filter; otherwise listing fields side by side is cleaner. Also note the subtlety that $not and $nor match documents where the field does not exist, because the inner condition cannot be true for a missing field.
Because collections are schemaless, two questions come up constantly: does this field exist, and what type is it?
// Documents that have (or lack) a discount field
db.products.find({ discount: { $exists: true } })
db.products.find({ discount: { $exists: false } })
// Documents where price is stored as a double instead of int
db.products.find({ price: { $type: 'double' } })
// Distinguish a null value from a missing field
db.products.find({ discount: null }) // null OR missing
db.products.find({ discount: { $type: 'null' } }) // only explicit nullThe $type operator is a great data-quality audit tool, catching fields that drifted across types as your application evolved.
Operators compose naturally. Here is a realistic storefront query: in-stock accessories under 5000, excluding clearance items, newest first:
db.products.find({
tags: 'accessories',
price: { $lt: 5000 },
'stock.warehouse': { $gt: 0 },
clearance: { $exists: false }
}).sort({ addedAt: -1 }).limit(20)Next we switch from reading to modifying data, with updateOne, updateMany, and the update operator family.