Mongoose v5.10.4 - SubDocuments PDF
Mongoose v5.10.4 - SubDocuments PDF
Subdocuments
Subdocuments are documents embedded in other documents. In Mongoose, this means you can nest
schemas in other schemas. Mongoose has two distinct notions of subdocuments: arrays of
subdocuments and single nested subdocuments.
Aside from code reuse, one important reason to use subdocuments is to create a path where there
would otherwise not be one to allow for validation over a group of elds (e.g. dateRange.fromDate <=
dateRange.toDate).
What is a Subdocument?
Subdocuments versus Nested Paths
Finding a Subdocument
Adding Subdocs to Arrays
Removing Subdocs
Parents of Subdocs
Alternate declaration syntax for arrays
Alternate declaration syntax for single subdocuments
What is a Subdocument?
Subdocuments are similar to normal documents. Nested schemas can have middleware, custom
validation logic, virtuals, and any other feature top-level schemas can use. The major di erence is that
subdocuments are not saved individually, they are saved whenever their top-level parent document is
saved.
childSchema.pre('validate', function(next) {
console.log('2');
next();
});
childSchema.pre('save', function(next) {
console.log('3');
next();
});
parentSchema.pre('validate', function(next) {
console.log('1');
next();
});
parentSchema.pre('save', function(next) {
console.log('4');
next();
});
In Mongoose, nested paths are subtly di erent from subdocuments. For example, below are two
schemas: one with child as a subdocument, and one with child as a nested path.
// Subdocument
const subdocumentSchema = new mongoose.Schema({
child: new mongoose.Schema({ name: String, age: Number })
});
const Subdoc = mongoose.model('Subdoc', subdocumentSchema);
// Nested path
const nestedSchema = new mongoose.Schema({
child: { name: String, age: Number }
});
const Nested = mongoose.model('Nested', nestedSchema);
These two schemas look similar, and the documents in MongoDB will have the same structure with
both schemas. But there are a few Mongoose-speci c di erences:
First, instances of Nested never have child === undefined . You can always set subproperties of
child , even if you don't set the child property. But instances of Subdoc can have child ===
undefined .
Secondly, in Mongoose 5, Document#set() merges when you call it on a nested path, but overwrites
when you call it on a subdocument.
Finding a Subdocument
Each subdocument has an _id by default. Mongoose document arrays have a special id method for
searching a document array to nd a document with a given _id .
MongooseArray methods such as push, unshift, addToSet, and others cast arguments to their proper
types transparently:
// create a comment
parent.children.push({ name: 'Liesl' });
var subdoc = parent.children[0];
console.log(subdoc) // { _id: '501d86090d371bab2c0341c5', name: 'Liesl' }
subdoc.isNew; // true
parent.save(function (err) {
if (err) return handleError(err)
console.log('Success!');
});
Subdocs may also be created without adding them to the array by using the create method of
MongooseArrays.
Removing Subdocs
Each subdocument has it's own remove method. For an array subdocument, this is equivalent to calling
.pull() on the subdocument. For a single nested subdocument, remove() is equivalent to setting the
subdocument to null .
// Equivalent to `parent.children.pull(_id)`
parent.children.id(_id).remove();
// Equivalent to `parent.child = null`
parent.child.remove();
parent.save(function (err) {
if (err) return handleError(err);
console.log('the subdocs were removed');
});
Parents of Subdocs
Sometimes, you need to get the parent of a subdoc. You can access the parent using the parent()
function.
If you have a deeply nested subdoc, you can access the top-level document using the ownerDocument()
function.
If you create a schema with an array of objects, Mongoose will automatically convert the object to a
schema for you:
Unlike document arrays, Mongoose 5 does not convert an objects in schemas into nested schemas. In
the below example, nested is a nested path rather than a subdocument.
This leads to some surprising behavior when you attempt to de ne a nested path with validators or
getters/setters.
Surprisingly, declaring nested with an object type makes nested into a path of type Mixed. To
instead make Mongoose automatically convert type: { prop: String } into type: new Schema({
prop: String }) , set the typePojoToMixed option to false .
Next Up