Introduction to Mongoose Populate

In Mongoose, populate lets you pull in referenced documents from another collection. Populate is similar to a left outer join in SQL, but the difference is that populate happens in your Node.js application rather than on the database server. Mongoose executes a separate query under the hood to load the referenced documents.

Basic Populate

Suppose you have two Mongoose models: Movie and Person . Movie documents have a director and an array of actors .

const Person = mongoose.model( 'Person' , mongoose.Schema({ name : String })); const Movie = mongoose.model( 'Movie' , mongoose.Schema({ title : String , director : { type : mongoose.ObjectId, ref : 'Person' }, actors : [{ type : mongoose.ObjectId, ref : 'Person' }] }));

Mongoose queries have a populate() function that lets you load a movie and its corresponding director and actors in one line:

const people = await Person.create([ { name : 'James Cameron' }, { name : 'Arnold Schwarzenegger' }, { name : 'Linda Hamilton' } ]); await Movie.create({ title : 'Terminator 2' , director : people[ 0 ]._id, actors : [people[ 1 ]._id, people[ 2 ]._id] }); let movie = await Movie.findOne().populate( 'director' ); movie.director.name; movie.actors[ 0 ].name; movie = await Movie.findOne().populate( 'director' ).populate( 'actors' ); movie.director.name; movie.actors[ 0 ].name; movie.actors[ 1 ].name;

Populate On Existing Documents

Mongoose documents also have a populate() function. Given an existing movie document, you can populate() any number of paths. Just remember to call Document#execPopulate() to actually execute the populate() call.

let movie = await Movie.findOne(); movie.director.name; movie.actors[ 0 ].name; await movie.populate( 'director' ).execPopulate(); movie.director.name; movie.actors[ 0 ].name; await movie.populate( 'actors' ).execPopulate(); movie.director.name; movie.actors[ 0 ].name; movie.actors[ 1 ].name;

Edge Cases

If you're populating a single document and the referenced document doesn't exist, Mongoose will set the populated property to null .

await Person.deleteOne({ name : 'James Cameron' }); const movie = await Movie.findOne().populate( 'director' ); movie.director;

If you're populating an array and one of the referenced documents doesn't exist, Mongoose will filter that value out of the array by default, returning a shorter array. You can override this with the retainNullValues option.

await Person.deleteOne({ name : 'Arnold Schwarzenegger' }); let movie = await Movie.findOne().populate( 'actors' ); movie.actors.length; movie.actors[ 0 ].name; movie = await Movie.findOne().populate({ path : 'actors' , options : { retainNullValues : true } }); movie.actors.length; movie.actors[ 0 ]; movie.actors[ 1 ].name;

Want to become your team's MongoDB expert? "Mastering Mongoose" distills 8 years of hard-earned lessons building Mongoose apps at scale into 153 pages. That means you can learn what you need to know to build production-ready full-stack apps with Node.js and MongoDB in a few days. Get your copy!

More Mongoose Tutorials