mongodbasynchronousmongoose

How to change a field in a collection according to another collection fields in mongoDB


This is my product schema

const mongoose = require('mongoose');
    const productSchema = new mongoose.Schema({
        name: {
            type: String,
            required: true
        },
        price: {
            type: Number,
            required: true
        },
        create_at: {
            type: Date,
            default: Date.now,
            required: true
        },
        quantity: {
            type: Number, 
            required: true
        },
        brand: {
            type: String,
            default: '',
        },
        size: {
            type: String, 
        },
        images : {
            type : [String],
        },
        color: [{
            type: String, 
        }],
        description: {
            type: String, 
        },
        details: {
            type: String, 
        },
        category: {
            type: mongoose.Schema.Types.ObjectId,
            required: true,
            ref: 'category'
        },
        status: {
            type: Boolean,
            default: true,
            required: true
        },
        modified_at: {
            type: Date, 
            default: Date.now
        },
        deleted_at: {
            type: Date, 
        }
    }, {
        toJSON: { virtuals: true },
        toObject: { virtuals: true }
    });

This is my offer Schema

const mongoose = require('mongoose');
    const offerSchema = new mongoose.Schema({
        product: {
            type:mongoose.Schema.Types.ObjectId,
            ref: "Product",
        },
        category: {
            type:mongoose.Schema.Types.ObjectId,
            ref: "category",
        },
        offerType: {
            type: String,
            enum: [ "percentage", "price"],
        },
        offerValue: {
            type: Number,
        },
        startDate: {
            type: Date,
        },
        endDate: {
            type: Date,
        },
        description: {
            type: String,
        },
        createdAt: {
            type: Date,
            default: Date.now
        }
    });
    const offerModel = new mongoose.model("Offers", offerSchema );
    module.exports = offerModel;

I want to change the price of the product according to the offerModel. If there is an offer for a product then the price should change according to the offer. But it is should not update in the database. I need it to be changed when I am retrieving it in the server side (all the time).

I use two ways to manipulate the data when querying it.

  1. using virtual method in mongoose.
productSchema.virtual('discountPrice').get( async function() {
        console.log('Inside the product schema virtual offer module');
        const offer = await offerModel.find({});
        console.log(offer);
        return this.price - 100;
    });

It works fine if I am not using the async. Virtual doesn't support the asynchronous functions.

But it is taking data from the database. So I can't write the code with out async or promise. The result is [object promise] not the product (when using async and promise).

  1. Using pre('find')

    It is not working as I expected. I don't know why.

productSchema.pre('find', async function() {
        const offer = await offerModel.find();
        console.log(offer);
        if(offer){
            this.price = this.price - 500;
            console.log(this.price)
        }else {
            console.log('error in pre find in product schema')
        }
    });
I got the offer when querying but not changing the price in the product.

In short : how can I change the product price or add a new field discountPrice. The price should be change based on if there is a offer for that particular product or category in the offerModel.js


Solution

  • Since you are storing a reference to product in your offerSchema you can populate a virtual whenever you do a Product.find() like so:

    productSchema.virtual('offer', { 
       ref: 'Offer', //< You have named the model Offers 
       localField: '_id', 
       foreignField: 'product' 
    });
    

    When you do your query just append populate like so:

    const products = await Product.find().populate('offer');
    const product  = await Product.findOne({name: 'Hat'}).populate('offer');
    

    This will add an array consisting of one object to each Product document. Therefore to calculate the discountPrice of a product you just need to access the 0 indexed array element of offer like so:

    productSchema.virtual('discountPrice').get(function() {
       // Implement logic to determine if offerType == percentage || price
       return this.price - this.offer[0].offerValue;
    });