Mastering Mongoose Populate: A Guide to Efficiently Querying Related Data in MongoDB

Lakindu Banneheka
2 min readMay 16, 2024

--

Introduction

Working with related data in MongoDB can be a challenge, especially when you need to combine data from multiple collections. Fortunately, Mongoose, an ODM (Object Data Modeling) library for MongoDB and Node.js, offers a powerful feature called populate that makes it easy to work with related documents. In this article, we'll explore how to use populate to efficiently query related data, even when some fields are optional.

Understanding the Basics

First, let’s set up a basic example. Suppose we have an e-commerce application with three collections: products, customers, and shipping. Each product is associated with a customer and optionally with a shipping method. Here are our Mongoose schemas:

const mongoose = require('mongoose');

// Product Schema
const productSchema = new mongoose.Schema({
name: String,
price: Number,
customer_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Customer' },
shipping_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Shipping', required: false }
});

const ProductModel = mongoose.model('Product', productSchema);

// Customer Schema
const customerSchema = new mongoose.Schema({
name: String,
email: String
});

const CustomerModel = mongoose.model('Customer', customerSchema);

// Shipping Schema
const shippingSchema = new mongoose.Schema({
method: String,
cost: Number
});

const ShippingModel = mongoose.model('Shipping', shippingSchema);

Populating Related Data

To retrieve a list of products along with their associated customer and shipping information, we use the populate method. Here's how you can do it:

const products = await ProductModel.find()
.populate('customer_id')
.populate('shipping_id')
.exec();

The populate method tells Mongoose to replace the customer_id and shipping_id fields in the ProductModel documents with the actual documents from the CustomerModel and ShippingModel collections, respectively.

Handling Optional Fields

In our example, the shipping_id field is optional (required: false). This means some products might not have a shipping method associated with them. When you use populate on an optional field, Mongoose will simply return null for those products without a shipping method. Let's see how this works in practice.

Example Output

  1. Product with Shipping:
[
{
"_id": "60c72b2f9b1e8c4b4c4a8341",
"name": "Product 1",
"price": 100,
"customer_id": {
"_id": "60c72b309b1e8c4b4c4a8342",
"name": "Customer 1",
"email": "customer1@example.com"
},
"shipping_id": {
"_id": "60c72b3a9b1e8c4b4c4a8343",
"method": "Express",
"cost": 20
}
}
]

2. Product without Shipping:

[
{
"_id": "60c72b2f9b1e8c4b4c4a8342",
"name": "Product 2",
"price": 150,
"customer_id": {
"_id": "60c72b309b1e8c4b4c4a8344",
"name": "Customer 2",
"email": "customer2@example.com"
},
"shipping_id": null
}
]

Advantages of Using populate:

  • Simplifies Data Retrieval: By automatically joining related documents, populate simplifies the process of fetching related data.
  • Improves Code Readability: Using populate makes your queries cleaner and easier to understand.
  • Handles Optional Relationships Gracefully: As demonstrated, populate can handle optional fields without any issues, returning null when no related document is found.

Call to Action

Try using populate in your next Mongoose project and see how it simplifies your data handling! If you have any questions or need further clarification, feel free to leave a comment below.

--

--

Lakindu Banneheka

I'm a undergraduate in Electronic and computer science in university of Kelaniya, Sri Lanka.