1

I have read as many articles as I can on how to correctly update an element in an array in a MongoDB (such as this one: Mongoose, update values in array of objects), and thought I had followed all the advice, but I am still getting this wrong, and would be very grateful if someone could spot my error, as I've been trying to debug this for hours!

The problem I am specifically having is that the findOneAndUpdate call seems to be just updating the first element in the array of "itineraryItems", no matter whether it matches my query for a specific element or not.

Data in my user collection (2 array elements in the itineraryItems array on the user document):

db.users.find({_id: ObjectId("5dd65ce7998d626a2c71a547"), {itineraryItems: 1})

{ "_id" : ObjectId("5dd65ce7998d626a2c71a547"), 
  "itineraryItems" : [ 
     { "_id" : ObjectId("5e3d5a301b65f3f9fd1621f8"), 
       "iType" : "event", 
       "startDate" : ISODate("2020-02-07T11:00:00Z"), 
       "endDate" : ISODate("2020-02-07T13:00:00Z"), 
       "includeTravelTime" : false, 
       "travelTimeMinutes" : 0, 
       "item" : null, 
       "event" : ObjectId("5dea66c182d9ac6fb4c6f36e") 
     }, 
     { "_id" : ObjectId("5e3d5a341b65f3f9fd1621ff"), 
       "iType" : "item", 
       "startDate" : ISODate("2020-02-07T11:00:00Z"), 
       "endDate" : ISODate("2020-02-07T13:00:00Z"), 
       "includeTravelTime" : false, 
       "travelTimeMinutes" : 0, 
       "item" : ObjectId("5e29df801f026697b71f7f48"), 
       "event" : null 
     } 
  ] 
}

My query building function: Note that queries that satisfy the first if statement (i.e. I pass in a known id for the element in the array) seem to work fine. It is the other 2 cases that seem to fail.

function getUpdateItineraryElementQuery(user, id, type, startDate, endDate, includeTravelTime, travelTimeMinutes, itemId) {
    let query = {};
    if (
        (id!=null) && 
        (id!='not_set')
    ) {
        query = {
            _id:                    user._id,
            'itineraryItems._id':   mongoose.Types.ObjectId(id)
        };
    } else {

        if (type==='event') {
            query = {
                _id:                                    user._id,
                'itineraryItems.iType':                 type,
                'itineraryItems.startDate':             startDate,
                'itineraryItems.endDate':               endDate,
                'itineraryItems.includeTravelTime':     includeTravelTime,
                'itineraryItems.travelTimeMinutes':     travelTimeMinutes,
                'itineraryItems.event':                 mongoose.Types.ObjectId(itemId)
            };
        } else {
            if (type==='item') {
                query = {
                    _id:                                    user._id,
                    'itineraryItems.iType':                 type,
                    'itineraryItems.startDate':             startDate,
                    'itineraryItems.endDate':               endDate,
                    'itineraryItems.includeTravelTime':     includeTravelTime,
                    'itineraryItems.travelTimeMinutes':     travelTimeMinutes,
                    'itineraryItems.item':                  mongoose.Types.ObjectId(itemId)
                };
            }
        }
    }
    return query;
}

My Mongoose call to findOneAndUpdate:

 User.findOneAndUpdate(
                query,
                {
                    'itineraryItems.$.startDate':           dtNewStartDate,
                    'itineraryItems.$.endDate':             dtNewEndDate,
                    'itineraryItems.$.includeTravelTime':   newIncludeTravelTime,
                    'itineraryItems.$.travelTimeMinutes':   newTravelTimeMinutes
                // eslint-disable-next-line no-unused-vars
                }, (err, doc) => {
                    if (err) {
                        // not found?
                        res.sendStatus(404).end();
                    } else {
                        // ok
                        res.sendStatus(200).end();
                    }     
                }
            );

Thanks a lot if you can tell me what I am doing wrong!

2
  • 1
    If you want up update all elements in an array then the syntax would be like itineraryItems.$[].startDate instead of itineraryItems.$.startDate Commented Feb 7, 2020 at 14:23
  • Thank you Wenfried. I am not looking to do that, but it's good to know that syntax - thanks a lot! Commented Feb 8, 2020 at 11:42

1 Answer 1

1

You need to match your subdocument using $elemMatch if you want to update only the subdocument that matches all the conditions

Like this,

{
    _id: user._id,
    'itineraryItems':{
        $elemMatch:{
            iType: type,
            startDate: startDate,
            endDate: endDate,
            includeTravelTime: includeTravelTime,
            travelTimeMinutes: travelTimeMinutes,
            item: mongoose.Types.ObjectId(itemId)
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! I have just modified my code and it's working. Very much appreciated!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.