0% found this document useful (0 votes)
18K views6 pages

Mongoose v5.10.4 - SubDocuments PDF

Subdocuments are documents embedded in other documents in Mongoose. There are two types: arrays of subdocuments and single nested subdocuments. Subdocuments allow for validation across related fields and code reuse through nested schemas. While similar to nested paths, subdocuments are saved with their parent document and can be undefined, whereas nested paths always exist. Subdocuments have their own validation, middleware, and can be found, added to arrays, and removed from the parent document.

Uploaded by

Arjun Gautam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18K views6 pages

Mongoose v5.10.4 - SubDocuments PDF

Subdocuments are documents embedded in other documents in Mongoose. There are two types: arrays of subdocuments and single nested subdocuments. Subdocuments allow for validation across related fields and code reuse through nested schemas. While similar to nested paths, subdocuments are saved with their parent document and can be undefined, whereas nested paths always exist. Subdocuments have their own validation, middleware, and can be found, added to arrays, and removed from the parent document.

Uploaded by

Arjun Gautam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

mongoose

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.

var childSchema = new Schema({ name: 'string' });

var parentSchema = new Schema({


// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
child: childSchema
});

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.

var Parent = mongoose.model('Parent', parentSchema);


var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
parent.children[0].name = 'Matthew';

// `parent.children[0].save()` is a no-op, it triggers middleware but


// does **not** actually save the subdocument. You need to save the parent
// doc.
parent.save(callback);
Subdocuments have save and validate middleware just like top-level documents. Calling save() on
the parent document triggers the save() middleware for all its subdocuments, and the same for
validate() middleware.

childSchema.pre('save', function (next) {


if ('invalid' == this.name) {
return next(new Error('#sadpanda'));
}
next();
});

var parent = new Parent({ children: [{ name: 'invalid' }] });


parent.save(function (err) {
console.log(err.message) // #sadpanda
});

Subdocuments' pre('save') and pre('validate') middleware execute before the top-level


document's pre('save') but after the top-level document's pre('validate') middleware. This is
because validating before save() is actually a piece of built-in middleware.

// Below code will print out 1-4 in order


var childSchema = new mongoose.Schema({ name: 'string' });

childSchema.pre('validate', function(next) {
console.log('2');
next();
});

childSchema.pre('save', function(next) {
console.log('3');
next();
});

var parentSchema = new mongoose.Schema({


child: childSchema
});

parentSchema.pre('validate', function(next) {
console.log('1');
next();
});

parentSchema.pre('save', function(next) {
console.log('4');
next();
});

Subdocuments versus Nested Paths

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 .

const doc1 = new Subdoc({});


doc1.child === undefined; // true
doc1.child.name = 'test'; // Throws TypeError: cannot read property...

const doc2 = new Nested({});


doc2.child === undefined; // false
console.log(doc2.child); // Prints 'MongooseDocument { undefined }'
doc2.child.name = 'test'; // Works

Secondly, in Mongoose 5, Document#set() merges when you call it on a nested path, but overwrites
when you call it on a subdocument.

const doc1 = new Subdoc({ child: { name: 'Luke', age: 19 } });


doc1.set({ child: { age: 21 } });
doc1.child; // { age: 21 }

const doc2 = new Nested({ child: { name: 'Luke', age: 19 } });


doc2.set({ child: { age: 21 } });
doc2.child; // { name: Luke, age: 21 }

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 .

var doc = parent.children.id(_id);

Adding Subdocs to Arrays

MongooseArray methods such as push, unshift, addToSet, and others cast arguments to their proper
types transparently:

var Parent = mongoose.model('Parent');


var parent = new Parent;

// 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.

var newdoc = parent.children.create({ name: 'Aaron' });

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.

const schema = new Schema({


docArr: [{ name: String }],
singleNested: new Schema({ name: String })
});
const Model = mongoose.model('Test', schema);

const doc = new Model({


docArr: [{ name: 'foo' }],
singleNested: { name: 'bar' }
});

doc.singleNested.parent() === doc; // true


doc.docArr[0].parent() === doc; // true

If you have a deeply nested subdoc, you can access the top-level document using the ownerDocument()
function.

const schema = new Schema({


level1: new Schema({
level2: new Schema({
test: String
})
})
});
const Model = mongoose.model('Test', schema);

const doc = new Model({ level1: { level2: 'test' } });

doc.level1.level2.parent() === doc; // false


doc.level1.level2.parent() === doc.level1; // true
doc.level1.level2.ownerDocument() === doc; // true

Alternate declaration syntax for arrays

If you create a schema with an array of objects, Mongoose will automatically convert the object to a
schema for you:

var parentSchema = new Schema({


children: [{ name: 'string' }]
});
// Equivalent
var parentSchema = new Schema({
children: [new Schema({ name: 'string' })]
});

Alternate declaration syntax for single nested subdocuments

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.

const schema = new Schema({


nested: {
prop: String
}
});

This leads to some surprising behavior when you attempt to de ne a nested path with validators or
getters/setters.

const schema = new Schema({


nested: {
// Do not do this! This makes `nested` a mixed path in Mongoose 5
type: { prop: String },
required: true
}
});

const schema = new Schema({


nested: {
// This works correctly
type: new Schema({ prop: String }),
required: true
}
});

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 .

const schema = new Schema({


nested: {
// Because of `typePojoToMixed`, Mongoose knows to
// wrap `{ prop: String }` in a `new Schema()`.
type: { prop: String },
required: true
}
}, { typePojoToMixed: false });

Next Up

Now that we've covered Subdocuments, let's take a look at querying.

You might also like