Mongoose Plugins
- Usage
- mongoose-autopopulate plugin
- It supports an autopopulate option in schemas
- It supports document arrays
- It has a couple caveats with projections
- It can specify an options argument
- It can specify a function that returns options
- It can disable autopopulate for individual queries
- It can disable autopopulate in
populate()options - It requires an option to work with lean
- It can limit the depth using
maxDepth - It can pass a list or regular expression of functions to apply hooks to
- Changelog
- 0.14.0 / 2021-05-14
- 0.13.0 / 2021-04-15
- 0.12.2 / 2020-04-19
- 0.12.1 / 2020-03-29
- 0.12.0 / 2020-02-05
- 0.11.0 / 2020-01-24
- 0.10.0 / 2019-12-20
- 0.9.1 / 2019-01-02
- 0.9.0 / 2018-11-08
- 0.8.2 / 2018-10-10
- 0.8.1 / 2018-09-02
- 0.8.0 / 2018-07-01
- 0.7.0 / 2018-05-10
- 0.5.0 / 2016-11-29
- 0.4.0 / 2015-09-03
- 0.3.0 / 2015-07-30
- 0.2.0 / 2015-06-27
mongoose-autopopulate
Mongoose Plugins
- Usage
- mongoose-autopopulate plugin
- It supports an autopopulate option in schemas
- It supports document arrays
- It has a couple caveats with projections
- It can specify an options argument
- It can specify a function that returns options
- It can disable autopopulate for individual queries
- It can disable autopopulate in
populate()options - It requires an option to work with lean
- It can limit the depth using
maxDepth - It can pass a list or regular expression of functions to apply hooks to
- Changelog
- 0.14.0 / 2021-05-14
- 0.13.0 / 2021-04-15
- 0.12.2 / 2020-04-19
- 0.12.1 / 2020-03-29
- 0.12.0 / 2020-02-05
- 0.11.0 / 2020-01-24
- 0.10.0 / 2019-12-20
- 0.9.1 / 2019-01-02
- 0.9.0 / 2018-11-08
- 0.8.2 / 2018-10-10
- 0.8.1 / 2018-09-02
- 0.8.0 / 2018-07-01
- 0.7.0 / 2018-05-10
- 0.5.0 / 2016-11-29
- 0.4.0 / 2015-09-03
- 0.3.0 / 2015-07-30
- 0.2.0 / 2015-06-27
Usage
The mongoose-autopopulate module exposes a single function that you can
pass to Mongoose schema's plugin() function.
const schema = new mongoose.Schema({
populatedField: {
type: mongoose.Schema.Types.ObjectId,
ref: 'ForeignModel',
// The below option tells this plugin to always call `populate()` on
// `populatedField`
autopopulate: true
}
});
schema.plugin(require('mongoose-autopopulate'));
Only apply this plugin to top-level schemas. Don't apply this plugin to child schemas.
// Don't do `nestedSchema.plugin(require('mongoose-autopopulate'))`.
// You only need to add mongoose-autopopulate to top-level schemas.
const nestedSchema = mongoose.Schema({
child: { type: Number, ref: 'Child', autopopulate: true }
});
const topSchema = mongoose.Schema({ nested: nestedSchema });
topSchema.plugin(require('mongoose-autopopulate'));
mongoose-autopopulate plugin
It supports an autopopulate option in schemas
Suppose you have two collections, "people" and "bands". The People model
looks like this:
var personSchema = new Schema({ name: String, birthName: String });
Person = mongoose.model('people', personSchema, 'people');
Suppose your "people" collection has one document:
{
name: 'Axl Rose',
birthName: 'William Bruce Rose, Jr.',
_id: '54ef3f374849dcaa649a3abc'
};
And your "bands" collection has one document:
{
_id: '54ef3f374849dcaa649a3abd',
name: "Guns N' Roses",
lead: '54ef3f374849dcaa649a3abc',
members: ['54ef3f374849dcaa649a3abc']
}
You can set the autopopulate option for the lead field.
This means that, every time you call find() or findOne(),
mongoose-autopopulate will automatically call .populate('lead')
for you.
var bandSchema = new Schema({
name: String,
lead: { type: ObjectId, ref: 'people', autopopulate: true }
});
bandSchema.plugin(autopopulate);
var Band = mongoose.model('band3', bandSchema, 'bands');
Band.findOne({ name: "Guns N' Roses" }, function(error, doc) {
assert.ifError(error);
assert.equal('Axl Rose', doc.lead.name);
assert.equal('William Bruce Rose, Jr.', doc.lead.birthName);
done();
});
It supports document arrays
mongoose-autopopulate also works on arrays.
var bandSchema = new Schema({
name: String,
members: [{ type: ObjectId, ref: 'people', autopopulate: true }]
});
bandSchema.plugin(autopopulate);
var Band = mongoose.model('band4', bandSchema, 'bands');
Band.findOne({ name: "Guns N' Roses" }, function(error, doc) {
assert.ifError(error);
assert.equal('Axl Rose', doc.members[0].name);
assert.equal('William Bruce Rose, Jr.', doc.members[0].birthName);
done();
});
It has a couple caveats with projections
By default, Mongoose 5.x automatically projects in populated properties.
That means you need a little extra work to exclude autopopulated fields.
Either explicitly deselect the path
in your projection, or set the selectPopulatedPaths schema option
to false.
// Mongoose adds `members: 1` and `lead: 1` to the projection
let band = yield Band.findOne().select({ name: 1 });
assert.equal(band.members[0].name, 'Axl Rose');
assert.equal(band.lead.name, 'Axl Rose');
// You can also tell Mongoose to not project in populated paths by default
// using the `selectPopulatedPaths` schema option.
const newSchema = Band.schema.clone();
newSchema.options.selectPopulatedPaths = false;
let Band2 = mongoose.model('Band2', newSchema, 'bands');
band = yield Band2.findOne().select({ name: 1 });
assert.ok(!band.members);
assert.ok(!band.lead);
It can specify an options argument
Advanced users of populate() may want to specify additional
options, such as selecting fields. If you set the autopopulate
option to an object, mongoose-autopopulate will merge the object
into populate options. The findOne() below is equivalent to
Band.findOne({ name: "Guns N' Roses" }).populate({ path: 'lead', select: 'name });
var bandSchema = new Schema({
name: String,
lead: { type: ObjectId, ref: 'people', autopopulate: { select: 'name' } }
});
bandSchema.plugin(autopopulate);
var Band = mongoose.model('band5', bandSchema, 'bands');
Band.findOne({ name: "Guns N' Roses" }, function(error, doc) {
assert.ifError(error);
assert.equal('Axl Rose', doc.lead.name);
assert.ok(!doc.lead.birthName);
done();
});
It can specify a function that returns options
You can also set the autopopulate option to be a function.
Then mongoose-autopopulate will call the function with
the query object as the context and use the return value.
The below populate() uses the same options as the previous
example.
var numCalls = 0;
var optionsFunction = function() {
++numCalls;
return { select: 'name' };
};
var bandSchema = new Schema({
name: String,
lead: { type: ObjectId, ref: 'people', autopopulate: optionsFunction }
});
bandSchema.plugin(autopopulate);
var Band = mongoose.model('band6', bandSchema, 'bands');
Band.find({ name: "Guns N' Roses" }, function(error, docs) {
assert.ifError(error);
assert.equal(1, docs.length);
assert.equal(1, numCalls);
var doc = docs[0];
assert.equal('Axl Rose', doc.lead.name);
assert.ok(!doc.lead.birthName);
done();
});
It can disable autopopulate for individual queries
If you set the autopopulate option to false on a query, autopopulate
will be disabled. This is handy if you want to autopopulate by default,
but opt-out for special cases.
var bandSchema = new Schema({
name: String,
lead: { type: ObjectId, ref: 'people', autopopulate: true }
});
bandSchema.plugin(autopopulate);
var Band = mongoose.model('band7', bandSchema, 'bands');
Band.findOne({ name: "Guns N' Roses" }, {}, { autopopulate: false }, function(error, doc) {
assert.ifError(error);
assert.ok(doc.lead instanceof mongoose.Types.ObjectId);
assert.ok(!doc.populated('lead'));
done();
});
It can disable autopopulate in populate() options
Say you have a model User that has the autopopulate plugin and you're
populating users from a different model. To disable autopopulate, you
need to set autopopulate: false as a populate option, not a query
option.
const userSchema = new Schema({
name: String,
friends: [{
type: Schema.Types.ObjectId,
ref: 'User',
autopopulate: { maxDepth: 2 }
}]
});
userSchema.plugin(autopopulate);
const responseSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
});
const User = mongoose.model('User', userSchema);
const Response = mongoose.model('Response', responseSchema);
return co(function*() {
const axl = new User({ name: 'Axl' });
const slash = new User({ name: 'Slash', friends: [axl._id] });
axl.friends.push(slash._id);
yield [axl.save(), slash.save()];
let r = yield Response.create({ user: axl._id });
r = yield Response.findById(r._id).
// Because `User` is the foreign model, you need to disable autopopulate
// in the populate options below, not the query options
populate({ path: 'user', options: { autopopulate: false } });
});
It requires an option to work with lean
Setting the Mongoose lean option
will disable autopopulate for all paths, unless you add autopulate: true
to your lean option.
// acquit:ignore:start
return co(function*() {
// acquit:ignore:end
let band = yield Band.findOne().lean();
// Won't autopopulate because `lean()` is set
assert.ok(band.lead instanceof mongoose.Types.ObjectId);
// To turn on `autopopulate` with lean, use `lean({ autopulate: true })`
band = yield Band.findOne().lean({ autopopulate: true });
assert.equal(band.lead.name, 'Axl Rose');
It can limit the depth using maxDepth
Recursive populate can lead to messy infinite recursion, so this plugin
supports a maxDepth option that limits how deep recursive population
will go. The maxDepth option is 10 by default
return co(function*() {
const accountSchema = new mongoose.Schema({
name: String,
friends: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Account',
// This is a recursive relationship, `friends` points to a list
// of accounts. If we didn't limit the depth, this would result
// in infinite recursion!
autopopulate: { maxDepth: 2 }
}]
});
accountSchema.plugin(autopopulate);
const Account = mongoose.model('Account', accountSchema);
const axl = new Account({ name: 'Axl' });
const slash = new Account({ name: 'Slash', friends: [axl._id] });
axl.friends.push(slash._id);
yield axl.save();
yield slash.save();
const doc = yield Account.findById(axl._id);
assert.equal(doc.friends[0].name, 'Slash');
assert.equal(doc.friends[0].friends[0].name, 'Axl');
// Only populate 2 levels deep, 3rd level will still be an `_id`
assert.equal(doc.friends[0].friends[0].friends[0].toString(),
slash._id.toHexString());
});
It can pass a list or regular expression of functions to apply hooks to
By default, autopopulate applies to the results of find(), findOne(),
findOneAndUpdate(), and save(). You can pick which functions
you want autopopulate to handle using the functions option. For example,
the below code disables autopopulating on save().
return co(function*() {
const bandSchema = new Schema({
name: String,
lead: { type: ObjectId, ref: 'people', autopopulate: true }
});
bandSchema.plugin(autopopulate, {
// Apply this plugin to all functions except for `save()`
functions: ['find', 'findOne', 'findOneAndUpdate']
});
const Band = mongoose.model('band8', bandSchema, 'bands');
let band = yield Band.findOne({ name: "Guns N' Roses" });
assert.ok(band.populated('lead'));
band = yield Band.findOne({ name: "Guns N' Roses" }).setOptions({ autopopulate: false });
assert.ok(!band.populated('lead'));
// `save()` doesn't autopopulate
yield band.save();
assert.ok(!band.populated('lead'));
});
Changelog
0.14.0 / 2021-05-14
- feat: add
functionsoption to list out functions to register hooks on
0.13.0 / 2021-04-15
- fix: autopopulate nested array fields containing embedded discriminator #82
- feat: autopopulate embedded discriminators #82
0.12.2 / 2020-04-19
- fix: avoid error in post('save') handler when this plugin is registered on a child schema #10
- docs: explain that this plugin should only be registered on top-level schemas
0.12.1 / 2020-03-29
- fix: handle autopopulate within nested document array when top-level array is empty #70
0.12.0 / 2020-02-05
- feat: autopopulate discriminators post
find(),findOne(), andfindOneAndUpdate()#26
0.11.0 / 2020-01-24
- feat: allow override of maxDepth param via query options #62 jmikrut
0.10.0 / 2019-12-20
- feat: autopopulate paths after
save()if they aren't already populated #8
0.9.1 / 2019-01-02
- docs: add note about selectPopulatedPaths option #50
0.9.0 / 2018-11-08
- feat: support turning on autopopulate with lean #48 #27 #14
0.8.2 / 2018-10-10
- docs: link to new plugins site on plugins.mongoosejs.io
0.8.1 / 2018-09-02
- fix: call function with options and include refPath in options #15
0.8.0 / 2018-07-01
- fix: add support for findOneAndUpdate() with autopopulate #42
0.7.0 / 2018-05-10
- BREAKING CHANGE: drop support for Node.js < 4.0.0
- feat: add
maxDepthoption, set to 10 by default #37 #20 #11
0.5.0 / 2016-11-29
0.4.0 / 2015-09-03
- added; support for arrays #9 rerthal
0.3.0 / 2015-07-30
- added; support of autopopulate in nested schemas #5 eloytoro
- fixed; changed mongoose peer dependency to 4.x #4
0.2.0 / 2015-06-27
- added; support for nested schemas #3 siboulet