Mongoose population explained (with examples)

Updated: December 30, 2023 By: Guest Contributor Post a comment

Mongoose is a robust tool for managing data relations in MongoDB. This article guides you through the concept of population in Mongoose, providing clear examples to illustrate this powerful feature effectively.

Understanding Population

Population is a process to automatically replace specified paths within a document with document(s) from other collection(s). In essence, it’s like a join in relational databases.

Basic Population

Let’s start with a simple example where you have two collections: Authors and Books.

const mongoose = require('mongoose');
const { Schema } = mongoose;

const AuthorSchema = new Schema({
  name: String
});
const BookSchema = new Schema({
  title: String,
  author: { type: Schema.Types.ObjectId, ref: 'Author' }
});

const Author = mongoose.model('Author', AuthorSchema);
const Book = mongoose.model('Book', BookSchema);

// To populate the author in the book query:
Book.find().populate('author').exec((err, books) => {
  if (err) return handleError(err);
  console.log('The author is %s', books[0].author.name);
  // Prints the author's name for the first book
});

Advanced Population

Population can go deeper with nested population and additional options like conditions, sorting, and field selection:

// Imagine a Book schema that also references a 'Publisher' model
Book.find()
  .populate({
    path: 'author',
    select: 'name -_id',
    populate: { path: 'publisher', select: 'name' }
  })
  .exec((err, books) => {
    // Handle result
  });

Population with Query Conditions

Applying query conditions to your population can help you fetch more specific data.

Book.find()
  .populate({
    path: 'author',
    match: { age: { $gte: 18 } },
    // Only populate authors older than 18
    select: 'name'
  })
  .exec((err, books) => {
    // books will have a null 'author' field if the condition is not met
  });

Handling Population in Asynchronous Functions

Using the async/await syntax, population becomes more readable:

const getBooks = async () => {
  try {
    const books = await Book.find().populate('author').exec();
    // Handle the populated books
  } catch (err) {
    // Error handling
  }
};

Conclusion

Mongoose population provides a convenient way to handle data relations, turning flat documents into rich, full-structured data. From basic usage to complex nesting, mastering population will immensely enhance your MongoDB applications.