Search code examples
javascriptnode.jsmongodbmongodb-querymongoose-populate

why Virtual Populate not working on Node js and mongoose? Scenario : Product and Review by user


I have review and product model.If user give review on specific product(id) then it is stored in review model database but i donot like to store user review in product model database .so, i used virtual populate in product model instead of child referencing.After using virtual properties,if we use product id to see details,we can see review of user in json format but not saved in database.But the problem is my virtual properties (In Product Model) not working as it doesnt show review of user in json format when i send the request in that product id which already have review by user(stored in review model database).what is the problem here?

User Review on Product (id) stored in database enter image description here

Sending Request of that product id to see review of user in json format using virtual properties(but no review found in json) enter image description here

In Product Model

const productSchema = new Schema({

    name: {
        type: String,
        required: true,
        trim: true,
    },

    slug: {
        type: String,
        required: true,
        unique: true,
    },
    price: {
        type: String,
        required: true,
    },
    quantity: {
        type: Number,
        required: true,
    },
    description: {
        type: String,
        required: true,
        trim: true,
    },
    offer: {
        type: Number,
    },
    discount: {
        type: Number,
    },
    productPictures: [{
        img: {
            type: String,
        },
    }, ],

   
    mainCategory: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "category",
        required: [true, "It is a required field"],
    },
    sub1Category: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "category",
        required: [true, "It is a required field"],
    },
    sub2Category: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "category",
        required: [true, "It is a required field"],
    },
    createdBy: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "admin",
        required: true,
    },
    vendor: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "vendor",
    },
    createdAt: {
        type: String,
        default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
    },
    updatedAt: {
        type: String,
        default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
    },
},

{
    toJson: { virtuals: true },
    toObject: { virtuals: true },
}
);

    productSchema.virtual("reviews", {

ref: "review",

foreignField: "product",

localField: "_id",

// justOne: true
}); 

const Product = mongoose.model("product", productSchema);                   
           module.exports = Product;

In Review Model

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const moment = require("moment");



const reviewSchema = new Schema({

user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "user",
    required: [true, "Review must belong to user"],
},
product: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "product",
    required: [true, "Review must belong to the product"],
},
review: {
    type: String,
    required: [true, "Review cannot be empty"],
},
rating: {
    type: Number,
    min: 1,
    max: 5,
},
createdAt: {
    type: String,
    default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
updateddAt: {
    type: String,
    default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
}, {
toJson: { virtuals: true },
toObject: { virtuals: true },
});


// pre middleware and populating user and product(we can also do populate in getAllReview in controller)

reviewSchema.pre(/^find/, function(next) {
// ^find here is we use regex and can able to find,findOne ...etc
this.populate({
    path: "product",
    select: " _id name",
}).populate({
    path: "user",
    select: " _id fullName",
});
next()
});


const Review = mongoose.model("review", reviewSchema);

module.exports = Review;

In Review.js

const Review = require("../../models/Review.Models")
exports.createReview = async(req, res) => {
const review = await Review.create(req.body)

return res.status(201).json({
    status: true,
    review
})
}


exports.getAllReviews = async(req, res) => {
try {
    const reviews = await Review.find()

    return res.status(200).json({
        status: true,
        totalReviews: reviews.length,
        reviews
    })
} catch (error) {
    return res.status(400).json({
        status: false,
        error
    })
}}

In Product.js

const Product = require("../../models/Product.Models");
exports.getProductDetailsById = async(req, res) => {
try {
    const { productId } = req.params;

    // const { productId } = req.body;

    if (productId) {
        const products = await Product.findOne({ _id: productId })
            .populate('reviews')

        return res.status(200).json({
            status: true,
            products,
        });
    } else {
        console.log("error display");
        return res.status(400).json({
            status: false,
            error: "params required...",
        });
    }
} catch (error) {
    return res.status(400).json({
        status: false,

        error: error,
    });
}

Solution

  • try this in Product.js

     try {
    if (productId) {
      const products = await Product.findOne({ _id: productId }).populate(
        "reviews"
      );
    
    
      console.log(products);
      if (products) {
        return res.status(200).json({
          status: true,
          message: "Products is listed",
          products,
          reviw: products.reviews,
        });
    

    only need to add on response sending

     return res.status(200).json({
          status: true,
          message: "Products is listed",
          products,
          reviw: products.reviews,
        });