2

I'm trying to update a specific field in a subdocument based on its Id and the Id of the parent document. I'm new to this so I may be misunderstanding the mongoDb documentation but as far as I can tell findOneAndUpdate should be working. I've also tried just update, updateOne, as well as findByIdAndUpdate. Nothing I've tried has successfully updated my object.

Im trying to update the rating value in a particular object in the savedBeers array.

Here is my model:

{
    "_id": {
        "$oid": "5c97cf50cc81525a75eb6c19"
    },
    "email": "email@gmail.com",
    "password": "$2b$10$6ixbu2Ka.Zj0eE5kF21MLO6CiMblgu4D6IHiC7Ta52o.cZFMNw1Mm",
    "__v": 0,
    "savedBeers": [
        {
            "id": 2,
            "comment": null,
            "rating": 0
        },
        {
            "id": 4,
            "comment": null,
            "rating": 0
        }
    ]
}

Frontend:

function addRating(beerId, ratingValue){
    let userId= localStorage.userId
    console.log(beerId, userId, ratingValue);
    $.ajax({
        type: "PUT",
        url: `/user/${userId}/${beerId}/${ratingValue}`,
        success: addRatingSuccess,
        error: addRatingError
    });
    function addRatingSuccess(response) {
        console.log("added", response);
    }
    function addRatingError() {
        console.log("error");    
    }
}

Backend userController.js:

 addRating:(req,res)=>{
            console.log(req.params);
            req.params.beerId = parseInt(req.params.beerId)
            req.params.ratingValue = parseInt(req.params.ratingValue)
            db.User.findOneAndUpdate(
                 {_id: ObjectId(req.params.userId), "savedBeers.id": req.params.beerId },
                { $set: { 'savedBeers.$.rating' : req.params.ratingValue } },
                {new:true})
                .exec(function (err, doc){
                    if (err){
                        console.log(err);
                        res.json({
                            "error": err
                        })
                    } else {
                        console.log(doc);
                        res.json({
                            doc
                        })
                    }
                }) 
        }

This is the response I get in the front end:

added 
{doc: {…}}
doc:
email: "email@gmail.com"
savedBeers: Array(2)
0: {id: 2, comment: null, rating: 0}
1: {id: 4, comment: null, rating: 0}
length: 2
__proto__: Array(0)
__v: 0
_id: "5c97cf50cc81525a75eb6c19"
__proto__: Object
__proto__: Object

And the backend:

{ userId: '5c97cf50cc81525a75eb6c19', beerId: 4, ratingValue: 3 }
{ savedBeers:
   [ { id: 2, comment: null, rating: 0 },
     { id: 4, comment: null, rating: 0 } ],
  _id: 5c97cf50cc81525a75eb6c19,
  email: 'email@gmail.com',
  __v: 0 }


Thanks so much for taking the time. Please let me know if there is any other information that would be helpful.

  • I've tried that solution as well. Doesn't work either. Edited my post to reflect this. – Langdon Froker Mar 29 '19 at 14:43
  • Works for the rest of the people in the world and has done so for many years now. Also note you cannot use a multiple field predicate with `findByIdAndUpdate()` since that only accepts an `_id` value **only**. You want `findOneAndUpdate()` instead and you also typically want `new: true` with either. Will not reopen since the linked answer is indeed the answer, it's just that you are still doing things wrong. Read answers and linked documentation again. – Neil Lunn Mar 29 '19 at 23:09
  • Yeah thats weird, cause it's not working. Understood about the findOneAndUpdate, and the new: true. According to my eyes this: ```db.your_collection.update( { _id: ObjectId("your_objectid"), "Statuses.Type": 1 }, { $set: { "Statuses.$.Timestamp": "new timestamp" } } ) ``` looks pretty mush the same as this: ```db.User.findOneAndUpdate( {_id: ObjectId(req.params.userId), "savedBeers.id": req.params.beerId }, { $set: { 'savedBeers.$.rating' : req.params.ratingValue } }, { new:true})}``` – Langdon Froker Mar 30 '19 at 17:05
  • The very last thing you show now in your post is the response you are getting. See the `n: 0`. That means **nothing matched**. **You are using the plain node driver** so things like `findByIdAndUpdate` are not even valid. – Neil Lunn Mar 30 '19 at 22:09
  • `"savedBeers.id": parseIntr(req.params.beerId)` - That is you problem all along as yout need **`parseInt()`**. Just as important as the `ObjectId()` where that is used and for the same reason **`req.params`** are "strings"** so you need to cast them. What was throwing me was your usage of `findByIdAndUpdate()` in the question, which is a mongoose method and not part of this driver. Mongoose has schema definitions that "autocast" values in query predicates to the expected type. For anything else you do it manually. – Neil Lunn Mar 30 '19 at 22:13
  • Ok. Im sorry, I hadn't edited my post since yesterday. In my last reply to you I show that I'm not using findByIdAndUpdate any more. I have edited my post now with parseInt() and my current response. Thank you for your help. – Langdon Froker Mar 31 '19 at 03:56

1 Answers1

1

It seems like that you are missing ".rating" after the "savedBeers.$" in "{ $set: { 'savedBeers.$' : req.params.ratingValue }"

So that line should be "{ $set: { 'savedBeers.$.rating' : req.params.ratingValue }"

Umut
  • 37
  • 4