Documentation
Introduction
Queries are objects in the MongoDB query language. find(query) returns a cursor,
findOne(query) the first match, and count(query) the number of matches.
people.find({ city: 'Recife', age: { $gte: 18 } });
people.findOne({ email: 'ana@example.com' });
people.count({ active: true });
people.find({}); // everything
The examples on this page use this collection:
const app = new sorla();
app.useDb('docs');
app.db.createCollection('people');
const people = app.db.people;
people.insertMany([
{ _id: 1, name: 'Ana', age: 31, city: 'Recife', tags: ['dev', 'ops'], address: { zip: '50000-000' } },
{ _id: 2, name: 'Bruno', age: 17, city: 'Curitiba', tags: ['design'] },
{ _id: 3, name: 'Carla', age: 45, city: 'Recife', tags: [], manager: null },
{ _id: 4, name: 'Davi', age: 28, city: 'São Paulo' }
]);
find
people.find(query, projection?) // → Cursor
The cursor holds the matches; read them with toArray(), iterate with for…of, sort, page and
more, see Cursors. projection picks fields: people.find({}, { name: 1 }).
findOne
people.findOne({ city: 'Recife' }); // copy of Ana, the first match in insertion order
people.findOne({ name: 'Nobody' }); // null
people.findOne({ _id: 3 }, { name: 1 }); // { _id: 3, name: 'Carla' }
count
people.count(); // 4, every document
people.count({ city: 'Recife' }); // 2, without copying anything
Field conditions
A plain value means equality; an object of operators means conditions that must all hold:
people.find({ city: 'Recife' }); // equality
people.find({ age: { $gte: 18, $lt: 40 } }); // 18 ≤ age < 40: Ana, Davi
people.find({ city: 'Recife', age: { $gt: 40 } }); // several fields: all must match (AND)
Comparison operators
| Operator | Matches when the field… | Example |
|---|---|---|
$eq |
equals the value | { age: { $eq: 31 } } (same as { age: 31 }) |
$ne |
does not equal it (also when the field is missing) | { city: { $ne: 'Recife' } } |
$gt, $gte |
is greater (or equal) | { age: { $gt: 30 } } |
$lt, $lte |
is smaller (or equal) | { age: { $lte: 17 } } |
$in |
equals one of the values (or, for an array field, contains one) | { city: { $in: ['Recife', 'Curitiba'] } } |
$nin |
equals none of them (also when missing) | { tags: { $nin: ['dev'] } } |
$exists |
exists (true) or not (false) |
{ manager: { $exists: true } } → Carla |
$regex |
is a string matching a pattern | { name: { $regex: '^c', $options: 'i' } } |
$not |
does not satisfy the condition inside | { age: { $not: { $gte: 18, $lte: 40 } } } → Bruno, Carla |
Comparisons work on numbers, strings (by code point: 'B' < 'a') and Date objects (by time).
$in and $nin
people.find({ city: { $in: ['Recife', 'São Paulo'] } }); // Ana, Carla, Davi
people.find({ tags: { $in: ['ops', 'design'] } }); // Ana, Bruno: array fields contain one
people.find({ name: { $in: [/^a/i, 'Davi'] } }); // regular expressions match strings
people.find({ city: { $nin: ['Recife'] } }); // Bruno, Davi
$in and $nin need an array: The $in operator must be an array.
$regex
people.find({ name: { $regex: 'an' } }); // case sensitive: none
people.find({ name: { $regex: 'an', $options: 'i' } }); // Ana
people.find({ name: /^b/i }); // a RegExp value works directly
people.find({ tags: { $regex: '^de' } }); // arrays match when one string matches
$options accepts i (ignore case), m (multiline) and s (dot matches new lines). Non-string
values never match. To search for text typed by a user, escape it:
const escape = text => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
people.find({ name: { $regex: escape(input), $options: 'i' } });
$exists, missing fields and null
| Query | Ana (no manager) |
Carla (manager: null) |
|---|---|---|
{ manager: null } |
✔ | |
{ manager: { $exists: true } } |
✔ | |
{ manager: { $exists: false } } |
✔ | |
{ manager: { $eq: 'x' } }, $gt, $in… |
||
{ manager: { $ne: 'x' } }, $nin |
✔ | ✔ |
A missing field matches $ne and $nin, like MongoDB. Unlike MongoDB, { field: null } matches only
documents where the field is null; to find documents without the field, use
{ field: { $exists: false } }, and for both, { $or: [{ field: null }, { field: { $exists: false } }] }.
Logical operators
// OR: anyone from Curitiba, or older than 40
people.find({ $or: [{ city: 'Curitiba' }, { age: { $gt: 40 } }] }); // Bruno, Carla
// AND written explicitly (useful to repeat a field)
people.find({ $and: [{ age: { $gte: 18 } }, { age: { $ne: 31 } }] }); // Carla, Davi
// NOR: none of the conditions
people.find({ $nor: [{ city: 'Recife' }, { age: { $lt: 18 } }] }); // Davi
// nest at any depth, mix with fields
people.find({
age: { $gte: 18 },
$or: [
{ city: 'Recife', tags: { $in: ['dev'] } },
{ $and: [{ city: 'São Paulo' }, { name: { $regex: '^d', $options: 'i' } }] }
]
}); // Ana, Davi
| Operator | Value | Matches when |
|---|---|---|
$and |
array of queries | all match |
$or |
array of queries | at least one matches |
$nor |
array of queries | none matches |
$not |
a condition, inside a field | the condition does not hold |
Logical operators must receive an array; unknown operators throw. Evaluation stops early: $and at
the first false, $or at the first true.
Nested fields
Dot paths reach into objects:
people.find({ 'address.zip': '50000-000' }); // Ana
people.find({ 'address.zip': { $regex: '^5' } });
Arrays
people.find({ tags: { $in: ['dev'] } }); // contains 'dev'
people.find({ tags: { $exists: true } }); // has the field (Carla's empty array counts)
people.find({ tags: [] }); // equality on arrays is not supported: use $in / $exists
Equality is strict: { tags: 'dev' } does not match tags: ['dev', 'ops']. Use $in.
Queries by _id are indexed
{ _id: x }, { _id: { $eq: x } } and { _id: { $in: [...] } } jump straight to the documents
through the collection's index instead of scanning, in find, findOne, count, every update and
delete, and bulkWrite. Other conditions in the same query are still checked:
people.find({ _id: { $in: [3, 1, 99] } }).ids(); // [1, 3], in collection order
people.find({ _id: 1, age: { $gt: 40 } }).length; // 0
Sharing queries with a server
Queries are plain JSON (except RegExp values: use { $regex, $options } instead), so you can send
the same object to a MongoDB backend, store it, or build it with a query builder UI:
const query = { $and: [{ age: { $gte: 30 } }, { $or: [{ name: 'Ana' }, { city: { $in: ['A', 'B'] } }] }] };
fetch('/api/people?where=' + encodeURIComponent(JSON.stringify(query)));
people.find(query); // the same result locally