Filter concepts by levelShowing all levels.

MongoDB · Query Language

Query Language

Filter shapes, the four operator families, dot notation, and the compound-index prefix rule

  • { f: v } is equalityno operator needed — everything else is explicit
  • $or/$nor take a list$not wraps one operator instead
  • Only ^-anchored $regex indexesunanchored patterns scan every value
  • Index prefix rulea compound index only serves its leading field(s), in order

1.Equality, range and array queries

A filter document is equality by default: { status: "shipped" } means exactly that value. Range queries add an operator like $gte. Querying a field that holds an array matches if any element matches.

Key features

  • { field: value } is equality — no operator needed for "equals"
  • { field: { $gte: 10, $lte: 20 } } combines two range bounds on one field
  • Querying an array field with a scalar matches if any element equals it
  • A range query on an array field matches if any element satisfies the range
  • Equality on a whole array (field: [1, 2, 3]) requires an exact, same-order match

The three basic query shapes

The three basic query shapes
ShapeFilterMatches
Equality{ status: "shipped" }status is exactly "shipped"
Range{ price: { $gte: 10, $lt: 50 } }price is 10 to just under 50
Array, scalar match{ tags: "sale" }tags array contains "sale"
Array, exact match{ tags: ["sale", "new"] }tags is exactly this array, in this order
Same filter shape, three matching rules

equality

{ status: "shipped" }

range

{ price: { $gte, $lt } }

array

matches any element

  1. equality — { status: "shipped" }
  2. range — { price: { $gte, $lt } }
  3. array — matches any element

Command

text
db.<collection>.find({ <field>: <value> | { <operator>: <value> } })

Equality is a bare value, range is an operator document, and a scalar filter on an array field matches if any element matches.

2.Comparison operators

Eight operators compare a field to a value or a list of values: $eq/$ne for equal/not-equal, $gt/$gte/$lt/$lte for ranges, and $in/$nin for "is one of" / "is none of" a list.

Key features

  • $eq/$ne: equal / not equal — $eq is implicit in { field: value }
  • $gt/$gte/$lt/$lte: greater/greater-or-equal/less/less-or-equal than
  • $in: field matches any value in the given array
  • $nin: field matches none of the values in the given array
  • $in with one array argument replaces a chain of $or clauses on the same field

The eight comparison operators

The eight comparison operators
OperatorMeaningExample
$eqequal to{ status: { $eq: "shipped" } }
$nenot equal to{ status: { $ne: "cancelled" } }
$gtgreater than{ price: { $gt: 100 } }
$gtegreater than or equal{ price: { $gte: 100 } }
$ltless than{ price: { $lt: 100 } }
$lteless than or equal{ price: { $lte: 100 } }
$inmatches any value in the array{ status: { $in: ["pending", "shipped"] } }
$ninmatches none of the values in the array{ status: { $nin: ["cancelled", "refunded"] } }
Equality, range, and membership

$eq / $ne

equal / not equal

$gt/$gte/$lt/$lte

range comparisons

$in / $nin

matches any / none of a list

  • $eq / $ne — equal / not equal
  • $gt/$gte/$lt/$lte — range comparisons
  • $in / $nin — matches any / none of a list

Command

text
db.<collection>.find({ <field>: { <$eq|$ne|$gt|$gte|$lt|$lte|$in|$nin>: <value or array> } })

$eq/$ne, $gt/$gte/$lt/$lte, $in/$nin — $in/$nin always take an array, even for one value.

3.Logical operators: $and, $or, $nor, $not

$and and $or combine a list of condition documents; $nor matches documents that fail every listed condition. $not is different — it wraps a single operator to invert it, not a list of conditions.

Key features

  • A plain filter object is an implicit $and across its top-level fields
  • Explicit $and is needed for repeated conditions on the same field, or nested logic
  • $or: [ {...}, {...} ] matches a document satisfying at least one condition
  • $nor: [ {...}, {...} ] matches a document satisfying none of the conditions
  • $not wraps a single operator expression, inverting it — not a list like the other three

The four logical operators

The four logical operators
OperatorShapeMatches
$and{ $and: [ {...}, {...} ] }every listed condition is true
$or{ $or: [ {...}, {...} ] }at least one listed condition is true
$nor{ $nor: [ {...}, {...} ] }none of the listed conditions is true
$not{ field: { $not: { $gt: 10 } } }the wrapped condition is false
Implicit AND, explicit everything else

{ a, b }

implicit $and

$or / $nor

array of conditions

$not

inverts one operator

  1. { a, b } — implicit $and
  2. $or / $nor — array of conditions
  3. $not — inverts one operator

Command

text
db.<collection>.find({ $and: [ <filter>, ... ] })
db.<collection>.find({ $or: [ <filter>, ... ] })
db.<collection>.find({ field: { $not: <operator expression> } })

A plain filter is already $and. $or/$nor take a list of conditions; $not wraps one operator to invert it.

4.Element operators: $exists, $type

$exists tests whether a field is present at all, regardless of its value. $type tests what BSON type a field holds. Both matter in a schema-flexible database, where two documents in the same collection can differ on either.

Key features

  • { field: { $exists: true } } matches when the field is present, even with value null
  • { field: { $exists: false } } matches documents where the field is entirely absent
  • { field: { $type: "string" } } matches only documents where field holds that BSON type
  • $type accepts either the string alias ("string", "int") or the numeric BSON type code
  • $type can take an array of types to match any one of several

$exists and $type

$exists and $type
OperatorQueryMatches
$exists{ discount: { $exists: true } }discount field is present (any value, incl. null)
$exists{ discount: { $exists: false } }discount field is entirely absent
$type{ price: { $type: "double" } }price holds a BSON double
$type{ price: { $type: ["double", "int"] } }price holds either type

Command

text
db.<collection>.find({ <field>: { $exists: <boolean> } })
db.<collection>.find({ <field>: { $type: <"alias" | code | [alias, ...]> } })

$exists checks presence, not value — { f: null } is not the same as $exists: false. $type checks BSON type, string alias or numeric code.

5.Array operators: $all, $elemMatch, $size

$all matches an array containing every listed value, in any order. $elemMatch matches when one single array element satisfies several conditions at once. $size matches by exact array length.

Key features

  • $all: [ v1, v2 ] matches an array containing both values, anywhere, any order
  • $elemMatch requires multiple conditions to all be true of the same array element
  • Without $elemMatch, separate conditions on an array can match across different elements
  • $size: n matches only arrays with exactly n elements — no $gt/$lt form for $size directly
  • $elemMatch also works as a query-level operator wrapping a whole document's conditions

The three array operators

The three array operators
OperatorQueryMatches
$all{ tags: { $all: ["sale", "summer"] } }array contains both values
$elemMatch{ scores: { $elemMatch: { $gt: 80, $lt: 90 } } }one element satisfies both bounds
$size{ tags: { $size: 3 } }array has exactly 3 elements
$elemMatch forces conditions onto one element

Without $elemMatch

{ score: 95, grade: "B" }

satisfies score >= 90

{ score: 60, grade: "A" }

satisfies grade == "A" — different element!

With $elemMatch

one element only

must satisfy score >= 90 AND grade == "A" together

  • Without $elemMatch
    • { score: 95, grade: "B" } — satisfies score >= 90
    • { score: 60, grade: "A" } — satisfies grade == "A" — different element!
  • With $elemMatch
    • one element only — must satisfy score >= 90 AND grade == "A" together

Command

text
db.<collection>.find({ <arrayField>: { $all: [<v1>, ...] } })
db.<collection>.find({ <arrayField>: { $elemMatch: { <cond1>, <cond2>, ... } } })
db.<collection>.find({ <arrayField>: { $size: <n> } })

$all: contains every listed value. $elemMatch: one element satisfies every condition. $size: exact length only, no range.

6.Evaluation operators and regular expressions

Evaluation operators run a computation while matching: $regex for patterns, $mod for modulo, $expr for aggregation-style expressions, $text for full-text search. Only an anchored $regex ("^prefix") can use an index.

Key features

  • { field: { $regex, $options } }, or a native regex literal, tests a pattern match
  • A ^-anchored regex can use an index like a range scan; unanchored cannot
  • $mod: [ divisor, remainder ] tests a numeric field against a modulo condition
  • $expr allows aggregation-style expressions in find(), incl. comparing two fields
  • $where runs JS per document — no index use, the slowest option

The evaluation operators

The evaluation operators
OperatorPurposeIndex-friendly?
$regexpattern match a string fieldonly if anchored at the start (^prefix)
$moddivisor/remainder check on a numberno
$expraggregation-style expression, incl. field-to-field comparisonsometimes, depends on the expression
$textfull-text search against a text indexyes — requires a text index
$wherearbitrary JavaScript predicate per documentno — slowest, evaluates every document

Command

text
db.<collection>.find({ <field>: { $regex: <pattern>, $options: <flags> } })
db.<collection>.find({ <field>: { $mod: [ <divisor>, <remainder> ] } })
db.<collection>.find({ $expr: { <aggregation expression> } })

$regex/$mod/$expr/$where run logic, not plain comparison. Only a ^-anchored $regex can use an index — $where never can.

7.Dot notation for nested fields

Dot notation reaches into an embedded document or a specific array index using a quoted string like "address.city" or "tags.0" — the query still targets one field path, just a nested one.

Key features

  • "parent.child" reaches a field inside an embedded document
  • "arrayField.N" (zero-based) reaches one specific array element by position
  • The dotted path must be quoted — it is a string key, not real object nesting in the query
  • Dot notation walks arbitrarily deep: "a.b.c" is valid for triply-nested documents
  • Without an index, "arrayField.city" (no number) checks every element for a matching city

Dot notation paths

Dot notation paths
PathTargetsExample
"a.b"field b inside embedded document a{ "address.city": "Austin" }
"a.b.c"field c inside a.b, two levels deep{ "contact.phone.number": "555-1234" }
"arr.0"the first (index 0) element of array arr{ "tags.0": "featured" }
"arr.field"field on any element of an array of documents{ "results.subject": "math" }
Reading a dotted path

"contact.phone.number"

contact

top-level field — an embedded document on the target collection

phone

nested field — an embedded document inside contact

number

leaf field — the value actually being matched

  • Whole: "contact.phone.number"
  • contact — top-level field: an embedded document on the target collection
  • phone — nested field: an embedded document inside contact
  • number — leaf field: the value actually being matched

Command

text
db.<collection>.find({ "<parent>.<child>": <value> })  // or "<array>.<index>"

"parent.child" and "array.N" are quoted string paths MongoDB walks — not real object nesting in the filter.

8.How compound query predicates interact with compound indexes

A compound index on { a: 1, b: 1, c: 1 } efficiently serves queries on a, a+b, or a+b+c, in that order — the prefix rule. Skipping the first field(s) means the index cannot narrow the search well, or at all.

Key features

  • An index on { a: 1, b: 1, c: 1 } has valid prefixes: { a }, { a, b }, { a, b, c }
  • A query on the leading prefix fields can use the index efficiently
  • Skipping the first field (querying only b, or only b and c) gets no efficient index use
  • Querying a and c (skipping b) still uses the index, but only up to the a prefix
  • Index field order should match the most common query pattern, not alphabetical order

Prefix rule for index { item: 1, location: 1, stock: 1 }

Prefix rule for index { item: 1, location: 1, stock: 1 }
Query filters onUses index efficiently?
itemyes — matches the { item } prefix
item, locationyes — matches the { item, location } prefix
item, location, stockyes — matches the full index
locationno — location alone is not a prefix
location, stockno — skips the leading item field
item, stockpartially — uses the { item } prefix, then filters stock without index help
Prefix rule — sorted by item, then location, then stock

{ item }

valid prefix — usable alone

{ item, location }

valid prefix — usable together

{ item, location, stock }

the full index

  1. { item } — valid prefix — usable alone
  2. { item, location } — valid prefix — usable together
  3. { item, location, stock } — the full index

Command

text
db.<collection>.createIndex({ <field1>: 1, <field2>: 1, <field3>: 1 })  // order defines the valid prefixes

A compound index only helps queries matching its leading field(s), in the order it was defined — skipping the first field gets little or no benefit.

Remember

  • Equality is a bare value; range/comparison need an operator; a scalar on an array field matches any element.
  • $and/$or/$nor take a condition list; $not wraps a single operator to invert it.
  • { f: null } is not the same as $exists: false — one allows a stored null, the other requires absence.
  • $elemMatch forces multiple conditions onto the same array element, instead of matching independently.

Pro tip

  • A ^-anchored $regex can use an index like a prefix range scan; anything else forces a full scan of the field.
  • A compound index only helps a query matching its leading prefix, in the order the index was created.

Next up

  • Data Modeling
  • Indexing Fundamentals
  • Aggregation Framework

Sources

  • Query operators — mongodb.com/docs/manual/reference/operator/query/
  • Compound indexes — mongodb.com/docs/manual/core/index-compound/
  • Checked 2026-08-21

Spotted a mistake on this page? Tell us.