S

Loading…

Filters+

Queries

Filters

A filter chooses documents. find, findOne, countDocuments, distinct, updates and deletes all take one, and they all match documents the same way.

Equality

A filter is an object of fields and values. A document matches when every field equals its value.

await users.find({ name: "Kage", age: 30 }).toArray();
await users.find({}).toArray(); // every document

Equality is exact, so values must have the same type: { age: 30 } does not match "30", and 1 does not match 1n. Nested objects match when they have the same fields with equal values, in any key order.

Comparison operators

OperatorMatches when the field…
$eqequals the value
$nedoes not equal the value, or is missing
$gt, $gteis greater than (or equal to) the value
$lt, $lteis less than (or equal to) the value
$inequals any value in the list
$ninequals none of the values, or is missing
$existsis present (true) or missing (false)
await users.find({ age: { $gte: 18, $lt: 65 } }).toArray();
await users.find({ role: { $in: ["admin", "owner"] } }).toArray();
await users.find({ deletedAt: { $exists: false } }).toArray();

Range operators only compare values of the same type. { age: { $gt: 18 } } never matches a string, and a number never matches a bigint. Strings compare by Unicode code point, with no case folding: "B" comes before "a".

Combining conditions

Fields in one filter are joined by AND. For anything else, use the logical operators:

await users
  .find({
    $or: [{ role: "admin" }, { age: { $gte: 65 } }],
  })
  .toArray();

await users.find({ age: { $not: { $gte: 18 } } }).toArray();
OperatorMatches when
$andevery filter in the list matches
$orat least one filter matches
$norno filter matches
$notthe field does not satisfy the operators inside it

$and, $or and $nor take a non-empty array. $not also matches a missing field, or one of another type, since the operator inside didn't match it.

Nested fields

A dotted path reaches into nested documents:

await users.find({ "address.city": "Paris" }).toArray();

A path through something that isn't a document doesn't match. TypeScript checks dotted paths against your document type, so a typo is a compile error.

Arrays

A filter on an array field looks at the array's elements as well as the array itself:

// tags: ["admin", "beta"]
await users.find({ tags: "admin" }).toArray(); // matches: one element is "admin"
await users.find({ tags: ["admin", "beta"] }).toArray(); // matches: the exact array
await users.find({ scores: { $gt: 90 } }).toArray(); // any element above 90

Each condition on an array is checked on its own. { scores: { $gt: 90, $lt: 95 } } matches [80, 99], because one element is above 90 and another is below 95. To require one element to meet every condition, use $elemMatch:

await users.find({ scores: { $elemMatch: { $gt: 90, $lt: 95 } } }).toArray();

await orders
  .find({
    items: { $elemMatch: { sku: "a-1", qty: { $gte: 2 } } },
  })
  .toArray();
OperatorMatches when
$elemMatchone element satisfies every condition inside it
$sizethe array has exactly this many elements

Paths step through arrays too. "items.sku" checks the sku of every element, and a number picks one position: "items.0.sku" is the first item's sku.

$ne and $nin are the exact opposites of $eq and $in, so { tags: { $ne: "admin" } } does not match ["admin", "beta"].

Not supported yet

$regex, $all, $type, $mod, $expr, $text and $where are rejected by the server. There is no full-text or geospatial search.

The complete matching rules, with a tested example for each case, are in query-semantics.md. The filter types are Filter and FilterOperators.