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!

DanJHill
  • 167
  • 2
  • 12
  • 1
    If you want up update **all** elements in an array then the syntax would be like `itineraryItems.$[].startDate` instead of `itineraryItems.$.startDate` – Wernfried Domscheit Feb 07 '20 at 14:23
  • Thank you Wenfried. I am not looking to do that, but it's good to know that syntax - thanks a lot! – DanJHill Feb 08 '20 at 11:42

1 Answers1

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)
        }
    }
}
Sarfraaz
  • 1,273
  • 4
  • 15