Here i am trying to update the 'rating' field of my document by taking average of previously existing value of the rating field and newly sent rating value.
this is my rating field specification in the model
rating: {
type: Number,
min: 0,
max: 5,
required: true,
},
this is my request body and controller function
const { newRating, bookID, userName, comment } = req.body;
const updateRating = await Book.findByIdAndUpdate(
{ _id: bookID },
{
rating: { $divide: [{ $inc: { rating: Number(newRating) } }, 2] },
$inc: { numOfRatings: 1 },
},
{ new: true }
);
and i am using postman to send client side data
here for example the rating field has previously set value of 4.1 and i am sending 5 as new rating in req.body then i want the rating field to have an updated value of 4.55 ((4.1 5)/2)
and this is the output i am getting in postman
{ "message": "Cast to Number failed for value "{ '$divide': [ { '$inc': [Object] }, 2 ] }" (type Object) at path "rating"", "stack": "CastError: Cast to Number failed for value "{ '$divide': [ { '$inc': [Object] }, 2 ] }" (type Object) at path "rating"\n at model.Query.exec (D:\Programs\VS Code\Web Development\lmsbackend\node_modules\mongoose\lib\query.js:4891:21)\n at model.Query.Query.then (D:\Programs\VS Code\Web Development\lmsbackend\node_modules\mongoose\lib\query.js:4990:15)\n at processTicksAndRejections (node:internal/process/task_queues:96:5)" }
i tried few things seeing mongodb solutions but it is not working out for me. Thank you in advance.
CodePudding user response:
$divide
is only available for Aggregation framework, so you need to change your update
(second) input like this:
db.collection.update({
"_id": 1
},
[
{
"$set": {
"rating": { "$divide": [{ "$sum": ["$rating", 5] }, 2] },
"numOfRatings": { "$sum": ["$numOfRatings", 1 ] }
}
}
])
CodePudding user response:
To update the rating field, you can use the $avg operator instead of $divide:
const updateRating = await Book.findByIdAndUpdate(
{ _id: bookID },
{
rating: { $avg: [ "$rating", newRating ] },
$inc: { numOfRatings: 1 },
},
{ new: true }
);
This will update the rating field with the average value of the existing rating and the new rating.
Note that you should also use the $inc operator to increment the numOfRatings field by 1.
Finally, make sure that the newRating value is cast to a Number before using it in the $avg operator. You can do this by using the Number() function.
const updateRating = await Book.findByIdAndUpdate(
{ _id: bookID },
{
rating: { $avg: [ "$rating", Number(newRating) ] },
$inc: { numOfRatings: 1 },
},
{ new: true }
);
I hope this helps!