so, the problem is that I’m trying to add reviews for the related product. But whenever I tried to hit API through Postman it gave me an error
ValidationError: Product validation failed: rating: Cast to Number failed for value "NaN" (type number) at path "rating"
[0] at Document.invalidate (E:\Web\Shop.co\node_modules\mongoose\lib\document.js:3197:32)
[0] at model.$set (E:\Web\Shop.co\node_modules\mongoose\lib\document.js:1456:12)
[0] at model.set [as rating] (E:\Web\Shop.co\node_modules\mongoose\lib\helpers\document\compile.js:205:19)
[0] at E:\Web\Shop.co\backend\controller\productController.js:108:20
[0] at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
This is the Product Schema
const mongoose = require("mongoose");
const reviewSchema = mongoose.Schema(
{
name: { type: String, required: true },
rating: { type: Number, required: true, default: 0 },
comment: { type: String, required: true },
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
},
{
timestamps: true,
}
);
const productSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
name: {
type: String,
required: true,
},
image: {
type: String,
required: true,
},
brand: {
type: String,
required: true,
},
category: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
default: 0,
},
review: [reviewSchema],
rating: {
type: Number,
required: true,
default: 0,
},
numReviews: {
type: Number,
required: true,
default: 0,
},
countInStock: {
type: Number,
required: true,
default: 0,
},
},
{
timestamps: true,
}
);
const Product = mongoose.model("Product", productSchema);
module.exports = Product;
and this is the createReview function in the Product Controller
const createReview = asyncHandler(async (req, res) => {
const { rating, comment } = req.body;
const product = await Product.findById(req.params.id);
if (product) {
const alreadyReviewed = product.review.find(
(r) => r.user.toString() === req.user._id.toString()
);
if (alreadyReviewed) {
res.status(400).json({ message: " Product already reviewed" });
throw new Error("Product already reviewed");
}
const reviews = {
name: req.user.name,
rating: Number(rating),
comment,
user: req.user._id,
};
product.review.push(reviews);
product.numReviews = product.review.length;
product.rating =
product.review.reduce((item, acc) => item.rating + acc, 0) /
product.review.length;
await product.save();
res.status(100).json({ message: " Review Added" });
} else {
res.status(400).json({ message: " Product not found" });
throw new Error("Product not found");
}
});
routes are
router.route("/:id/reviews").post(protect, createReview);
although the rating is defined as Number type and given Number during review creation, it still gives NaN error.
If I’m wrong or my code is wrong, please give me advice.
>Solution :
The error occurs because the reduce function is not correctly summing up the ratings of the reviews.
To fix this, you need to adjust the calculation of the product’s rating by correctly summing up the ratings of all reviews. Here’s the corrected version of your createReview function
const createReview = asyncHandler(async (req, res) => {
const { rating, comment } = req.body;
const product = await Product.findById(req.params.id);
if (product) {
const alreadyReviewed = product.review.find(
(r) => r.user.toString() === req.user._id.toString()
);
if (alreadyReviewed) {
res.status(400).json({ message: "Product already reviewed" });
throw new Error("Product already reviewed");
}
const review = {
name: req.user.name,
rating: Number(rating),
comment,
user: req.user._id,
};
product.review.push(review);
product.numReviews = product.review.length;
// Calculate new average rating
const totalRating = product.review.reduce((acc, item) => acc + item.rating, 0);
product.rating = totalRating / product.review.length;
await product.save();
res.status(200).json({ message: "Review Added" });
} else {
res.status(404).json({ message: "Product not found" });
throw new Error("Product not found");
}
});