A cheatsheet of methods you can use to implement common patterns in mongoose data-fetching(eg pagination, filtering etc).
We'll work with a couple of queries which have not been resolved. Since each method returns the overall instance, we can chain methods
const todosQuery = Todo.find({});
const userQuery = User.find({});
Using the where()
method in conjugation with some other methods, based on value we can filter out certain results.
The following methods are usually used with the where()
method:
equals()
- equal to
userQuery.where('age').equals(13); // results where user age is 18
gt()
- greater than
userQuery.where('age').equals(13); // results where user age is more than 18
lt()
- less than
userQuery.where('age').equals(13); // results where user age is less than 18
gte()
- greater than or equal to
userQuery.where('age').gte(13); // results where user age is greater than or equal to 18
lte()
- less than or equal to
userQuery.where('age').lte(18); // results where user age is less than or equal to 18
mod()
- reminder
userQuery.where('age').mod([2, 1]); // all users where add is odd
ne()
- not equal to
userQuery.where('age').ne(13); // results where age is not 13
size()
- size of array
userQuery.where('projects').size(); // 3
regex()
- use regex
userQuery.where('name').regex(/abc/i); // results where the name matches the pattern
slice()
- JavaScript slice on the array of results
// projects is an array
userQuery.where('projects').slice(2); // results after doing the slice on each result
all()
- all elements in the argument array is present
// projects is an array
userQuery.where('projects').all(['p1', 'p2', 'p3']); // results where the field has all the specified elements
The field name is the key, and the value states whether it's ascending or descending. There are different ways to implement this:
// -1 or 1
todosQuery.sort({ title: -1, description: 1 });
// 'ascending' or 'descending'
todosQuery.sort({ title: 'descending', description: 'ascending' });
// 'asc' or 'desc'
todosQuery.sort({ title: 'desc', description: 'asc' });
// shorthand - title is ascending and description is descending
todosQuery.sort('title -description');
We can use a combination of limit
and skip
to implement pagination easily.
Before that, let's look at how these two methods work.
todosQuery.limit(100);
This will skip the first x
results of the find
query.
todosQuery.skip(20);
Let's say that we are on page 2
and each page contains 10 results.
We'll create a variable called page
which is 2
and number
which is 10
.
So, we'll skip the first (page - 1) * number
which will send the results 11 and above.
const page = 2;
const number = 10;
todosQuery.skip((page - 1) * number);
Until now, we've skipped the first 10 results, but we still show all of remaining results.
Therefore, let's use the limit()
method to limit the number of results.
const page = 2;
const number = 10;
// skip = 2-1 * 10 = first 10 results
// limit = 2 * 10 = first 20 results
// the results are limited to the first 11 to 20 results
todosQuery.skip((page - 1) * number).limit(page * 10);
This method returns execution stats instead of the data. It can be useful when comparing different approaches and analyzing which one is more performative.
const stats = await todosQuery.explain();
Lean removes all the getters
, setters
and the virtuals
from the document that is returned by mongoose.
The object returned is a plain JavaScript object and not a mongoose query compared to other query methods.
const user = await userQuery.lean();
Chaining further query methods will not work as the mongoose query is not returned, the result is. It's like calling the
exec()
method.
#### cursor() Cursors are the native way mongodb navigates through the database. With mongoose, an array is returned as a result of `find()` but the native driver returns a `Cursor`.
Mongoose allows us to get the data in the form a cursor/stream because it may be more performant in some cases.
// There are 2 ways to use a cursor. First, as a stream:
Thing.
find({ name: /^hello/ }).
cursor().
on('data', function(doc) { console.log(doc); }).
on('end', function() { console.log('Done!'); });
// Or you can use `.next()` to manually get the next doc in the stream.
// `.next()` returns a promise, so you can use promises or callbacks.
const cursor = Thing.find({ name: /^hello/ }).cursor();
cursor.next(function(error, doc) {
console.log(doc);
});
// Because `.next()` returns a promise, you can use co
// to easily iterate through all documents without loading them
// all into memory.
const cursor = Thing.find({ name: /^hello/ }).cursor();
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
console.log(doc);
}