1

I am implementing a function using node.js to update a value in mongoDB. Below is my mongoDB collection,

enter image description here

I want to update the values of the totalRating and totalVotes inside the ratings array. Therefore, I implemented the function as below in node.js.

router.post('/addReview', (req, res) => { Product.findOneAndUpdate( {_id: req.body.productId}, { "$set" : { "ratings.totalRating": req.body.rating, "ratings.totalVotes" : req.body.votes } }, {new : true}, (err, productInfo) => { if(err) return res.json({success:false, err}); res.status(200).json(productInfo) } ) }); 

Below is the Product Schema,

const mongoose = require('mongoose'); const Schema = mongoose.Schema; const AddItemsSchema = new Schema({ _id: {type:String, required:true}, img: {type:String, required: true}, category: {type:String, required: true}, name: {type:String, required: true}, description: {type:String, required: true}, price: {type:String, required: true}, quantity: {type:String, required: true}, size: {type:String, required: true}, meterial: {type:String, required: true}, discount:{type:Number, default: 0}, comment:{type:Array, default: []}, ratings:{type:Array, required: true} }); const Products = mongoose.model('additems', AddItemsSchema); module.exports = Products; 

When I sent a post request using postman, it does not update the db values and it returns null. Can someone please point me out where I am doing wrong? Thanks!

Postman request body,

{ "productId": "5eb82cd3e8b80155fc3ee021", "rating" : 4.0, "votes" : 1 } 

2 Answers 2

6

There are couple of issues in your code :

1) First & major issue is not with update part but with filter part of query :

.findOneAndUpdate(filterPart, updatePart) 

So from basic functionality of .findOneAndUpdate() - It will return null if it doesn't find any document with input filter. If it finds a document & not able to update it for any reason then it would either returns an error or old document (If no issue with query syntax and no update is performed).

Also just as a note : When option { upsert : true } is used without { new : true } & if there is no matching doc - then result will still be null though it creates a new document. It's because as originally findOneAndUpdate didn't find any document.

So your issue is with input req.body.productId which is of type string but your _id in database is of type ObjectId(), You need to convert it prior to querying DB like this :

const mongoose = require("mongoose"); router.post("/addReview", (req, res) => { Product.findOneAndUpdate( { _id: mongoose.Types.ObjectId(req.body.productId) }, { $set: { "ratings.0.totalRating": req.body.rating, "ratings.0.totalVotes": req.body.votes, }, }, { new: true }, (err, productInfo) => { if (err) return res.json({ success: false, err }); res.status(200).json(productInfo); } ); }); 

2) There is an issue with update part as well :

As ratings is an array of objects [{},{}], you can not do like below :

{ "$set" : { "ratings.totalRating": req.body.rating, "ratings.totalVotes" : req.body.votes } } 

cause in $set operator ratings.totalRating & ratings.totalVotes is trying to set/create fields totalRating & totalVotes under ratings - if it's an object then it would work. Since it's not - then you need to say to which object in array you should be doing this update operation as like below :

{ $set: { "ratings.0.totalRating": req.body.rating, "ratings.0.totalVotes": req.body.votes, }, } 

From above we're updating first object in ratings array. In general if there'll always be only one object then make ratings as an object rather than array, else if it will have multiple objects then keep it as an array & add a unique value to each object. Which helps to identify which particular object needs to be updated while positional operators are being used on updates.

Sign up to request clarification or add additional context in comments.

Comments

0

You need to provide the indexing to update array

 router.post('/addReview', (req, res) => { Product.findOneAndUpdate( {_id: req.body.productId}, { "$set" : { "ratings.0.totalRating": req.body.rating, "ratings.0.totalVotes" : req.body.votes } }, {new : true}, (err, productInfo) => { if(err) return res.json({success:false, err}); res.status(200).json(productInfo) } ) }); 

2 Comments

Still the same issue. BTW thanx for helping
@MarketMAD I have updated the answer, if changing schema doesn't work, keep the schema as it is and use the array index in the query