How to Use .populate() With mongoDB

Written by GK3000 | Published 2022/10/09
Tech Story Tags: mongodb | orm | populate | json | nosql | databases | mysql | postman

TLDRA common example of a common eCommerce website with two collections for the products and the categories. Each product belongs to a certain category like "shoes", "hats", and "pants" – and we want to refer to a corresponding collection from each product. We do not want to include the actual name of the category into a product's record since it will complicate renaming categories. Instead of the default MongoDB random sequence of letters and numbers, we want the corresponding category's name.via the TL;DR App

Let's take a common example of an eCommerce website with two collections for the products and the categories.

Each product belongs to a certain category like "shoes", "hats", and "pants" – and we want to refer to a corresponding collection from each product.

We do not want to include the actual name of the category into a product's record since it will complicate renaming categories – imagine we have thousands of shoes belonging to the "shoes" category and then we decide to rename it to "footwear". To do so we will need to update all the products to replace "shoes" with "footwear".

Instead, if we only refer to the "shoes" record from the categories collection by its id then we only need to update one record in the categories collection and rename it from "shoes" to "footwear". All the products pointing to this category will not be needed to get updated since the id of the category will not change.

And when we fetch data for the products in the category_id field we have an id of the category record from the categories collection. But what we want to get instead is the actual name of this category. This could be easily achieved with .populate() the method. Let's see how.

The product schema:

const mongoose = require("mongoose");

const productSchema = new mongoose.Schema({
  name: { type: String, required: true, unique: true },
  price: { type: Number, required: true },
  color: { type: String, required: true },
  description: { type: String, required: true },
  category_id: { type: mongoose.Types.ObjectId , required: true, unique: true, ref: 'categories' },
});

module.exports = mongoose.model("products", productSchema );

As we can see in the category_id we have an id of the corresponding category from another collection, namely from "categories".

The categories schema:

const mongoose = require("mongoose");

const categorySchema = new mongoose.Schema({
  category: { type: String, required: true, unique: true },
});

module.exports = mongoose.model("categories", categorySchema);

Here we only have the name of the category.

Now what you can do is add .populate() a method when you are getting products from the DB to say that instead of category_idshowing as it is (the default MongoDB random sequence of letters and numbers) we want to fetch the corresponding category's name and use it instead. For example, we want to find all the products:

async findAll (req, res) {
    try {
        let product = await products.find({}).populate('category_id')
        res.send(product)
    }
    catch(e) {
        res.send({e})
    }
}

So now all the products will come with the name of the category included (instead of id reference).

Let's see the results in Postman. First, this is how it will be without .populate(). You can see that in the products data fetched we can only see the id of the category, obviously, we can't use it for rendering purposes:

Now same request but with .populate(). Now instead of id, we can see the actual name of the category which we can render right away:

🤷‍♀️

Update: with nested cross-references, we can also use .populate().

For example, we have products referring to the subcategories and those subcategories are referring to the categories. Then we will use nested .populate():

async findProducts(req, res){
        try{
            const products = await Products.find({}).populate({ 
             path: 'subcategoryId',
             populate: {
               path: 'categoryId',
               model: 'Categories'
           } 
       })

Also Published here


Written by GK3000 | Barcelona Code School
Published by HackerNoon on 2022/10/09