Skip to content

Comparison and logical operators

An exact-match filter — field equals value — only gets you so far. Real questions are ranges and sets: “who owes more than three fines?”, “who joined in 2022 or 2023?”, “which records are missing a field entirely?”. MongoDB answers these with query operators, special keys that begin with a dollar sign and live inside the value position of a field. The pattern is always the same: instead of giving a field a plain value, you give it a small document whose key is an operator.

We keep editing our library members. Each example shows the filter and the documents it would return.

The comparison family compares a field against a value: $eq (equal), $ne (not equal), $gt (greater than), $gte (greater-or-equal), $lt (less than), and $lte (less-or-equal). Writing a plain value is just shorthand for $eq. Here we ask for every member who owes more than two fines:

db.members.find({ fines: { $gt: 2 } })

Only members above the threshold come back:

[
{ "_id": "65f0b3d4e4b0a1c2e4b0a1d1", "name": "Linus", "joined": 2023, "fines": 5 },
{ "_id": "65f0b3d4e4b0a1c2e4b0a1d4", "name": "Grace", "joined": 2021, "fines": 3 }
]

You can combine two comparisons on the same field by putting both operators in one document — here, members who joined in a window of years:

db.members.find({ joined: { $gte: 2022, $lte: 2023 } })

$in matches when a field equals any value in a list, and $nin matches when it equals none of them. This is far cleaner than chaining $or of equalities. Here we pull the members who joined in either 2021 or 2023:

db.members.find({ joined: { $in: [2021, 2023] } })

Both matching years are returned together:

[
{ "_id": "65f0b3d4e4b0a1c2e4b0a1d1", "name": "Linus", "joined": 2023, "fines": 5 },
{ "_id": "65f0b3d4e4b0a1c2e4b0a1d4", "name": "Grace", "joined": 2021, "fines": 3 }
]

Combining conditions: $and, $or, $not, $nor

Section titled “Combining conditions: $and, $or, $not, $nor”

Several field/value pairs in one filter already mean AND, so $and is only needed when you would otherwise write the same field twice. $or is the everyday one — it takes a list of filters and matches if any holds. Here we find members who either owe nothing or joined in 2021:

db.members.find({
$or: [{ fines: 0 }, { joined: 2021 }]
})

Anyone matching either branch appears:

[
{ "_id": "65f0b3d4e4b0a1c2e4b0a1d2", "name": "Margaret", "joined": 2023, "fines": 0 },
{ "_id": "65f0b3d4e4b0a1c2e4b0a1d4", "name": "Grace", "joined": 2021, "fines": 3 }
]

$not inverts a single operator expression, and $nor matches documents where none of its branches hold — the logical opposite of $or. As an example, “owes a non-zero amount AND did not join in 2021” reads naturally with $nor:

db.members.find({ $nor: [{ fines: 0 }, { joined: 2021 }] })

Documents in one collection need not all share the same fields. $exists asks whether a field is present at all, regardless of its value, and $type asks what BSON type the value has. Here we find members who have no fines field recorded yet:

db.members.find({ fines: { $exists: false } })

Only the document that never got a fines field comes back:

[
{ "_id": "65f0b3d4e4b0a1c2e4b0a1d5", "name": "Ada", "joined": 2024 }
]

To match by type instead — say, every document where joined is stored as a number rather than a string someone typed by mistake — name the type:

db.members.find({ joined: { $type: "int" } })

In Compass: type any of these filters straight into the filter bar at the top of the Documents tab, for example { fines: { $gt: 2 } }, and press Find. Compass parses the same operator syntax the shell uses.

  • A plain value is $eq. Writing { fines: 0 } and { fines: { $eq: 0 } } are identical. Reach for the explicit operators only when you need a comparison other than equality.
  • $ne and $nin also match missing fields. A document with no fines field is “not equal to 5”, so it satisfies { fines: { $ne: 5 } }. If you mean “has a fines field and it is not 5”, combine with $exists.
  • Two operators on one field go in one document. Range queries like { $gte: 2022, $lte: 2023 } are a single value document, not two separate filters — you cannot repeat the same field key at the top level.
  • Prefer $in over a pile of $or equalities. They mean the same thing, but $in is shorter, reads better, and lets the query planner use an index more directly.
How do you find documents where fines is greater than 2?
A document has no fines field. Does it match { fines: { $ne: 5 } }?
Which operator matches when a field equals any value in a list?
Where do logical operators like $or belong in a filter?