Queries

Documents can be retrieved through several static helper methods of models.

Any model method which involves specifying query conditions can be executed two ways:

When a callback function:

  • is passed, the operation will be executed immediately with the results passed to the callback.
  • is not passed, an instance of Query is returned, which provides a special query builder interface.

In mongoose 4, a Query has a .then() function, and thus can be used as a promise.

When executing a query with a callback function, you specify your query as a JSON document. The JSON document’s syntax is the same as the MongoDB shell.

  1. var Person = mongoose.model('Person', yourSchema);
  2. // find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields
  3. Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function (err, person) {
  4. if (err) return handleError(err);
  5. console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host.
  6. })

Here we see that the query was executed immediately and the results passed to our callback. All callbacks in Mongoose use the pattern: callback(error, result). If an error occurs executing the query, the error parameter will contain an error document, and result will be null. If the query is successful, the error parameter will be null, and the result will be populated with the results of the query.

Anywhere a callback is passed to a query in Mongoose, the callback follows the pattern callback(error, results). What results is depends on the operation: For findOne() it is a potentially-null single document, find() a list of documents, count() the number of documents, update() the number of documents affected, etc. The API docs for Models provide more detail on what is passed to the callbacks.

Now let’s look at what happens when no callback is passed:

  1. // find each person with a last name matching 'Ghost'
  2. var query = Person.findOne({ 'name.last': 'Ghost' });
  3. // selecting the `name` and `occupation` fields
  4. query.select('name occupation');
  5. // execute the query at a later time
  6. query.exec(function (err, person) {
  7. if (err) return handleError(err);
  8. console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host.
  9. })

In the above code, the query variable is of type Query. A Query enables you to build up a query using chaining syntax, rather than specifying a JSON object. The below 2 examples are equivalent.

  1. // With a JSON doc
  2. Person.
  3. find({
  4. occupation: /host/,
  5. 'name.last': 'Ghost',
  6. age: { $gt: 17, $lt: 66 },
  7. likes: { $in: ['vaporizing', 'talking'] }
  8. }).
  9. limit(10).
  10. sort({ occupation: -1 }).
  11. select({ name: 1, occupation: 1 }).
  12. exec(callback);
  13. // Using query builder
  14. Person.
  15. find({ occupation: /host/ }).
  16. where('name.last').equals('Ghost').
  17. where('age').gt(17).lt(66).
  18. where('likes').in(['vaporizing', 'talking']).
  19. limit(10).
  20. sort('-occupation').
  21. select('name occupation').
  22. exec(callback);

A full list of Query helper functions can be found in the API docs.

Setters are not executed by default in 4.x. For example, if you lowercase emails in your schema:

  1. var personSchema = new Schema({
  2. email: {
  3. type: String,
  4. lowercase: true
  5. }
  6. });

Mongoose will not automatically lowercase the email in your queries, so Person.find({ email: 'Val@karpov.io' }) would return no results. Use the runSettersOnQuery option to turn on this behavior:

  1. var personSchema = new Schema({
  2. email: {
  3. type: String,
  4. lowercase: true
  5. }
  6. }, { runSettersOnQuery: true });

References to other documents

There are no joins in MongoDB but sometimes we still want references to documents in other collections. This is where population comes in. Read more about how to include documents from other collections in your query results here.

Streaming

You can stream query results from MongoDB. You need to call the Query#cursor() function instead of Query#exec to return an instance of QueryCursor.

  1. var cursor = Person.find({ occupation: /host/ }).cursor();
  2. cursor.on('data', function(doc) {
  3. // Called once for every document
  4. });
  5. cursor.on('close', function() {
  6. // Called when done
  7. });

Next Up

Now that we’ve covered Queries, let’s take a look at validation.