Mongoose Populate: Replacing Paths with Documents in MongoDB

Suppose you got the job of managing all the schools in your city. You have created a collection of schools which contains all the school names, locations etc. Also, each school has its own MongoDB collection which includes course names, timings, fees etc. 

Now you need to populate each school’s data into your main school collection to get the job done. How do you achieve this? Introducing the Mongoose Populate.

The populate method is designed to seamlessly reference and link data across different MongoDB collections through ObjectId references. In this tutorial, let’s look at this method, its very easy to use syntax and demonstrate it in Node.js by creating a real-life application.

Mongoose populate() Method

Populate is used to enhance one-to-many or many-to-one data relationships in MongoDB. The populate() method in Mongoose is used to replace the specified paths in a document with another document from other collections.

Syntax:

collection.findById(id)
  .populate('path')
  .exec(callback);

Parameters:

  • path: This is the name of the field you want to populate. This field must be defined as a reference in the Mongoose schema.
  • callback: A callback function that takes two parameters, err (if an error occurs) and the populated document (if the document is successfully populated).

You can also use then() and catch():

collection.findById(id)
  .populate('path')
  .then(populatedDoc => console.log(populatedDoc))
  .catch(err => console.log(err));

You can also use async & await for the asynchronous approach.

Well now that we have learned all the basics, syntax and parameters, let’s see how to implement it.

Using Populate Across Multiple MongoDB Collections in Node.js

To demonstrate the use of populate, let’s create an application where we have two collections, one for farms and the other for the products sold by those farms.

We will use this in Node.js so if you are completely new to Node.js please check out – Installation, Setup, and Creating a Hello World Application in NodeJS

Step 1: Setup Node.js Project

Open a terminal, create a new project directory and move into it using the following commands:

mkdir farmApp
cd farmApp

Initialize NPM inside the directory to install Mongoose:

npm init -y

Create an “index.js” file where we will write all the code for creating the application:

touch index.js

Install Mongoose using NPM:

npm i mongoose

Step 2: Configuring Packages, Schemas, & Models

Now, open the project file inside a code editor and then open “index.js” to start writing the code.

First, we need to import the Mongoose module, which we can do using require() method:

const mongoose = require('mongoose');
const { Schema } = mongoose; // To get access to mongoose.Schema

Set up a connection to MongoDB using Mongoose:

mongoose.connect('mongodb://localhost:27017/farmStandTake2', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => {
        console.log("MONGO CONNECTION OPEN!!!")
    })
    .catch(err => {
        console.log("OH NO MONGO CONNECTION ERROR!!!!")
        console.log(err)
    })

Here we have connected to the MongoDB local database using Mongoose, if you find any error, make sure that the MongoDB server is up & running in the background.

Create schemas & models for both farms and products, with references between them to enable population:

Farm schema and model

const farmSchema = new Schema({
    name: {
        type: String,
        required: [true, 'Farm must have a name!']
    },
    city: {
        type: String
    },
    email: {
        type: String,
        required: [true, 'Email required']
    },
    products: [
        {
            type: Schema.Types.ObjectId,
            ref: 'Product'
        }
    ]
});
 
const Farm = mongoose.model('Farm', farmSchema);

Product schema and model

const productSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    price: {
        type: Number,
        required: true,
        min: 0
    },
    category: {
        type: String,
        lowercase: true,
        enum: ['fruit', 'vegetable', 'dairy']
    },
    farm: {
        type: Schema.Types.ObjectId,
        ref: 'Farm'
    }
});
 
const Product = mongoose.model('Product', productSchema);

Step 3: Inserting Documents

Now to demonstrate the use of populate, we have to insert some documents of the model we just created, we have a separate tutorial on inserting data in MongoDB if you want to know about it.

Let us take a look at a farm document that we have inserted:

{
  products: 6170854b496fcd1b541f5b1a,
  _id: 6170847b496fcd1b541f5b19,
  name: "Giovanni's Wheat Fields",
  city: 'Marzamemi, Sicily',
  email: '[email protected]',
  __v: 0
}

And, this is what our product looks like:

{
  _id: 6170854b496fcd1b541f5b1a,
  name: 'Whole Wheat Grains',
  price: 1,
  category: 'vegetable',
  farm: 6170847b496fcd1b541f5b19,
  __v: 0
}

Now we don’t want to show them individually rather we want users to see which farm is selling which product and which product is associated with which farm.

We can use the populate() method to achieve that.

Step 4: Using populate() Method

For a product, we want to populate the name and id of the farm associated with it. We can do this by using the populate() method:

Product.find()
.populate('farm', 'name')
.then(product=>console.log(product))
.catch(error=>console.log(error));

Here we have passed two arguments ‘farm’ and ‘name’ which specify that the ‘farm’ field of ‘product’ documents will be populated with the ‘name’ field of referenced ‘farm’ documents.

For the farm, we will populate the entire products documents associated with it:

Farm.find()
.populate('products')
.then(farm=>console.log(farm))
.catch(error=>console.log(error));

Step 5: Querying the Populated Result

Now, let’s query what our farm and products look like after using populate:

Product

{
  _id: 6170854b496fcd1b541f5b1a,
  name: 'Whole Wheat Grains',
  price: 1,
  category: 'vegetable',
  farm: { _id: 6170847b496fcd1b541f5b19, name: "Giovanni's Wheat Fields" },
  __v: 0
}

Farm

{
  products: [
    {
      _id: 6170854b496fcd1b541f5b1a,
      name: 'Whole Wheat Grains',
      price: 1,
      category: 'vegetable',
      farm: 6170847b496fcd1b541f5b19,
      __v: 0
    }
  ],
  _id: 6170847b496fcd1b541f5b19,
  name: "Giovanni's Wheat Fields",
  city: 'Marzamemi, Sicily',
  email: '[email protected]',
  __v: 1
}

In the above output, see the fields are populated, this is how we have successfully demonstrated the populate().

Summary

Populate in Mongoose is helpful when selecting related data from multiple collections and combining it into a single document. The populate() method allows you to replace a field or object of one collection with any other document or document’s field from another collection based on the reference field. 

In this article, we not only look at its syntax but also demonstrate it with a Node.js example. Also, note that this method is not available in the MongoDB native driver, so you must have Mongoose installed before using it.

Confused between Mongoose and MongoDB? Check out – Explaining the Ultimate Difference Between MongoDB vs Mongoose

Reference

https://mongoosejs.com/docs/populate.html

Aneesha S
Aneesha S
Articles: 172