Sling Academy
Home/Node.js/Mongoose: How to query after populate() (with examples)

Mongoose: How to query after populate() (with examples)

Last updated: December 30, 2023

Introduction

Mongoose is a popular ODM library for MongoDB that provides a straightforward, schema-based solution to model your application data. Its populate mechanism allows developers to create a reference to other documents in other collections and, when executing a query, replace the references with the actual documents. Although immensely useful, developers often encounter scenarios where they need to further query or filter the resulting populated documents. This tutorial provides an in-depth guide on how to use Mongoose queries after the populate() function, implementing basic to advanced examples utilizing the latest JavaScript and Node.js features like async/await and arrow functions.

Basics of Populate

Example 1: Basic Population

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

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);

async function getBooks() {
  const books = await Book.find().populate('author');
  return books;
}

getBooks().then(books => console.log(books));

We began with a simple mongoose schema where a ‘Book’ has a reference to an ‘Author’. After finding books, we populate the author details inside the book results.

Filtering after Populate

Example 2: Filtering Books by Author’s Name

async function getBooksByAuthorName(authorName) {
  const books = await Book.find()
    .populate({
      path: 'author',
      match: { name: authorName },
      // Here more complex matches or filters could be applied
    });

  // Filter out books whose author field was not populated,
  // which happens when the match fails
  return books.filter(book => book.author);
}

getBooksByAuthorName('J.K. Rowling').then(books => console.log(books));

This example showcases how to filter the populated documents by an author’s name.

Advanced Query Techniques

Example 3: Complex Queries with aggregate()

async function getBooksByAuthorWithAggregate(authorName) {
  return Book.aggregate([
    {
      $lookup: {
        from: 'authors',
        localField: 'author',
        foreignField: '_id',
        as: 'authorDetails'
      }
    },
    {
      $match: { 'authorDetails.name': authorName }
    },
    {
      $unwind: '$authorDetails'
    }
    // Additional aggregation stages can be included here,
    // such as sorting, grouping, etc.
  ]);
}

getBooksByAuthorWithAggregate('Isaac Asimov').then(books => console.log(books));

In a more complex scenario, where you need to perform operations not supported by populate(), you can use aggregate() with a $lookup stage to join collections and follow it with further filtering or transformations.

Conclusion

In conclusion, Mongoose’s populate() can be exceptionally powerful when combined with the right query operators and techniques. Developing an understanding of how to filter and manipulate data post-population results in uncompromised control over your queries, ensuring you can mold the data to fit the needs of your application. By starting with the basics and gradually delving into more complex aggregation procedures, this article aims to equip you with the necessary understanding to proficiently utilize Mongoose in real-world scenarios. Remember that efficiency and performance considerations should also be weighed when deciding between a straightforward populate() and a full-fledged aggregate() approach.

Next Article: Mongoose: 3 Ways to Remove All Documents from a Collection

Previous Article: Mongoose: How to update a nested array in a document

Series: Mongoose.js Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates