DocsHub
Querying

Logical Operators

Learn how to combine multiple conditions in MongoDB queries using $and, $or, $not, and $nor.

Logical Operators

Comparison operators filter by a single field. But real queries are rarely that simple. You often need to combine multiple conditions — find students who are in 10th grade and enrolled, or teachers who teach Math or Physics.

That is what logical operators are for. They let you combine multiple conditions into one query.

OperatorMeaning
$andAll conditions must be true
$orAt least one condition must be true
$notCondition must be false
$norAll conditions must be false

$and

$and takes an array of conditions. A document matches only if all conditions are true.

Syntax

db.collection.find({
  $and: [ condition1, condition2, ... ]
})

Example — Students in 10th grade AND enrolled

db.students.find({
  $and: [
    { grade: "10th" },
    { enrolled: true }
  ]
})

Implicit $and

When you pass multiple fields in a plain filter object, MongoDB automatically applies $and between them. So the query above is exactly the same as:

db.students.find({
  grade: "10th",
  enrolled: true
})

Both queries return the same results. The shorthand is cleaner and you should prefer it.

When you must use explicit $and

The only time you need to write $and explicitly is when you need to apply multiple conditions on the same field:

// Students older than 15 AND younger than 18
// You cannot write { age: { $gt: 15 }, age: { $lt: 18 } }
// because duplicate keys are not allowed in a JS object

db.students.find({
  $and: [
    { age: { $gt: 15 } },
    { age: { $lt: 18 } }
  ]
})

Actually for range queries on the same field, the cleaner approach is:

db.students.find({
  age: { $gt: 15, $lt: 18 }
})

But $and becomes truly necessary when combining $or conditions together — we will see that below.


$or

$or takes an array of conditions. A document matches if at least one condition is true.

Syntax

db.collection.find({
  $or: [ condition1, condition2, ... ]
})

Example — Students in 9th grade OR 11th grade

db.students.find({
  $or: [
    { grade: "9th" },
    { grade: "11th" }
  ]
})

For simple cases like this where you are checking the same field against multiple values, $in is cleaner than $or:

  db.students.find({ grade: { $in: ["9th", "11th"] } })

Use $or when the conditions are on different fields.

Example — Students who are unenrolled OR younger than 15

db.students.find({
  $or: [
    { enrolled: false },
    { age: { $lt: 15 } }
  ]
})

Example — Teachers who teach Math OR have been working since before 2016

db.teachers.find({
  $or: [
    { subject: "Math" },
    { joinedDate: { $lt: new Date("2016-01-01") } }
  ]
})

Combining $and with $or

This is where logical operators get powerful. You can nest them to build complex conditions.

Example — Students in 10th grade AND (enrolled OR older than 16)

db.students.find({
  $and: [
    { grade: "10th" },
    {
      $or: [
        { enrolled: true },
        { age: { $gt: 16 } }
      ]
    }
  ]
})

A student matches this query if they are in 10th grade, and either enrolled or older than 16.

Shorthand version

Since the outer $and has conditions on different fields, you can use the implicit shorthand:

db.students.find({
  grade: "10th",
  $or: [
    { enrolled: true },
    { age: { $gt: 16 } }
  ]
})

This is cleaner and returns the same results.


$not

$not inverts a condition. A document matches if the condition is false or if the field does not exist.

Syntax

db.collection.find({
  field: { $not: { $operator: value } }
})

Example — Students who are NOT older than 16

db.students.find({
  age: { $not: { $gt: 16 } }
})

This returns students where age is 16 or less — including documents where the age field does not exist at all.

Example — Courses that do NOT have more than 25 students

db.courses.find({
  totalStudents: { $not: { $gt: 25 } }
})

$not is different from $ne. $ne only matches documents where the field exists and has a different value. $not also matches documents where the field does not exist at all.


$nor

$nor takes an array of conditions. A document matches only if all conditions are false. It is the opposite of $or.

Syntax

db.collection.find({
  $nor: [ condition1, condition2, ... ]
})

Example — Students who are neither in 9th grade NOR unenrolled

db.students.find({
  $nor: [
    { grade: "9th" },
    { enrolled: false }
  ]
})

This returns students who are not in 9th grade and are enrolled. Both conditions must be false for a document to match.

Example — Teachers who neither teach Math NOR Biology

db.teachers.find({
  $nor: [
    { subject: "Math" },
    { subject: "Biology" }
  ]
})

School System Examples

Here are practical queries for our school system combining logical operators:

// Students in 10th grade who have paid their fees
db.students.find({
  grade: "10th",
  hasPaidFees: true
})

// Students who are either unenrolled or have not paid fees
db.students.find({
  $or: [
    { enrolled: false },
    { hasPaidFees: false }
  ]
})

// Active teachers who joined before 2018 OR teach Math
db.teachers.find({
  active: true,
  $or: [
    { joinedDate: { $lt: new Date("2018-01-01") } },
    { subject: "Math" }
  ]
})

// Students in 10th or 11th grade who are between 15 and 17 years old
db.students.find({
  grade: { $in: ["10th", "11th"] },
  age: { $gte: 15, $lte: 17 }
})

// Courses that are not in 9th grade and do not have fewer than 20 students
db.courses.find({
  $nor: [
    { grade: "9th" },
    { totalStudents: { $lt: 20 } }
  ]
})

// Students who are enrolled AND (in 10th grade OR older than 16)
db.students.find({
  enrolled: true,
  $or: [
    { grade: "10th" },
    { age: { $gt: 16 } }
  ]
})

Quick Reference

OperatorMatches whenUse it for
$andAll conditions are trueFiltering by multiple requirements
$orAt least one condition is trueFlexible matching across fields
$notThe condition is falseInverting a single condition
$norAll conditions are falseExcluding multiple conditions at once

Keep your queries readable. If you find yourself deeply nesting $and and $or, step back and think about whether your data model could be simpler. Very complex queries are often a sign that the data structure needs rethinking — something we cover in the Schema Design section.

On this page