Documents

Sponsor #native_company# — #native_desc#

Mongoose documents represent a one-to-one mapping to documents as stored in MongoDB. Each document is an instance of its Model.

Documents vs Models

Document and Model are distinct classes in Mongoose. The Model class is a subclass of the Document class. When you use the Model constructor, you create a new document.

  1. const MyModel = mongoose.model('Test', new Schema({ name: String }));
  2. const doc = new MyModel();
  3. doc instanceof MyModel; // true
  4. doc instanceof mongoose.Model; // true
  5. doc instanceof mongoose.Document; // true

In Mongoose, a “document” generally means an instance of a model. You should not have to create an instance of the Document class without going through a model.

Retrieving

When you load documents from MongoDB using model functions like findOne(), you get a Mongoose document back.

  1. const doc = await MyModel.findOne();
  2. doc instanceof MyModel; // true
  3. doc instanceof mongoose.Model; // true
  4. doc instanceof mongoose.Document; // true

Updating Using save()

Mongoose documents track changes. You can modify a document using vanilla JavaScript assignments and Mongoose will convert it into MongoDB update operators.

  1. doc.name = 'foo';
  2. // Mongoose sends an `updateOne({ _id: doc._id }, { $set: { name: 'foo' } })`
  3. // to MongoDB.
  4. await doc.save();

The save() method returns a promise. If save() succeeds, the promise resolves to the document that was saved.

  1. doc.save().then(savedDoc => {
  2. savedDoc === doc; // true
  3. });

If the document with the corresponding _id is not found, Mongoose will report a DocumentNotFoundError:

  1. const doc = await MyModel.findOne();
  2. // Delete the document so Mongoose won't be able to save changes
  3. await MyModel.deleteOne({ _id: doc._id });
  4. doc.name = 'foo';
  5. await doc.save(); // Throws DocumentNotFoundError

Updating Using Queries

The save() function is generally the right way to update a document with Mongoose. With save(), you get full validation and middleware.

For cases when save() isn’t flexible enough, Mongoose lets you create your own MongoDB updates with casting, middleware, and limited validation.

  1. // Update all documents in the `mymodels` collection
  2. await MyModel.updateMany({}, { $set: { name: 'foo' } });

Note that update(), updateMany(), findOneAndUpdate(), etc. do not execute save() middleware. If you need save middleware and full validation, first query for the document and then save() it.

Validating

Documents are casted and validated before they are saved. Mongoose first casts values to the specified type and then validates them. Internally, Mongoose calls the document’s validate() method before saving.

  1. const schema = new Schema({ name: String, age: { type: Number, min: 0 } });
  2. const Person = mongoose.model('Person', schema);
  3. let p = new Person({ name: 'foo', age: 'bar' });
  4. // Cast to Number failed for value "bar" at path "age"
  5. await p.validate();
  6. let p2 = new Person({ name: 'foo', age: -1 });
  7. // Path `age` (-1) is less than minimum allowed value (0).
  8. await p2.validate();

Mongoose also supports limited validation on updates using the runValidators option. Mongoose casts parameters to query functions like findOne(), updateOne() by default. However, Mongoose does not run validation on query function parameters by default. You need to set runValidators: true for Mongoose to validate.

  1. // Cast to number failed for value "bar" at path "age"
  2. await Person.updateOne({}, { age: 'bar' });
  3. // Path `age` (-1) is less than minimum allowed value (0).
  4. await Person.updateOne({}, { age: -1 }, { runValidators: true });

Read the validation guide for more details.

Overwriting

There are 2 different ways to overwrite a document (replacing all keys in the document). One way is to use the Document#overwrite() function followed by save().

  1. const doc = await Person.findOne({ _id });
  2. // Sets `name` and unsets all other properties
  3. doc.overwrite({ name: 'Jean-Luc Picard' });
  4. await doc.save();

The other way is to use Model.replaceOne().

  1. // Sets `name` and unsets all other properties
  2. await Person.replaceOne({ _id }, { name: 'Jean-Luc Picard' });

Next Up

Now that we’ve covered Documents, let’s take a look at Subdocuments.