Andrew
Andrew

Reputation: 15

updating just one element in a nested json document with mongoose

How can I update just one piece in a document with Mongoose, when the json elements are nested?

I would like to update a document and just set the "darkMode" from false to true, without GETing and PUTing the whole "settins" part. Is this possible with Mongoose?

        "settings": {
            "darkMode": false,
            "tipOfTheDay": true,
            "paperFormat": "DINA4",
        },

If I PUT this, I will lose everything in the "settings" part of the document:

        "settings": {
            "darkMode": true,
        },

My NodeJS backend looks like this. I am using the findByIdAndUpdate at the moment. Maybe there is a better method?

exports.updateProfile = async (req, res, next) => {
    const profile = await Profile.findByIdAndUpdate(req.params.id, req.body, {
        new: true,
        runValidators: true
    });

    if (!profile) {
        return res.status(400).json({success: false});
    }

    res.status(200).json({success: true, data: profile});
}

Upvotes: 1

Views: 640

Answers (1)

MaieonBrix
MaieonBrix

Reputation: 1624

In order to update a field in an embbeded document you must use the a $set operator (since your document is an object we are dealing with an object within an object)

From the mongodb documentation :

https://docs.mongodb.com/v2.4/reference/method/db.collection.update/#update-specific-fields-in-embedded-documents

Update Specific Fields in Embedded Documents Use dot notation to update values in embedded documents.

So for you, if we assume that req.body here is { darkMode: true } it would be

    const profile = await Profile.findByIdAndUpdate(
     req.params.id, 
     { $set: { 'settings.darkMode': req.body.darkMode } }, 
     {
        new: true,
        runValidators: true
    });

But it all depends on how you send the data from the client, if you send an object containing only modified fields then you must specifically $set them to keep the other value intact like above. To automate this you could also build your query using a fancy loop :

let query = { $set: {} }

Object.keys(req.body).forEach(requestBodyKey => {
    let requestBodyFieldValue = req.body[requestBodyKey];
    query.$set[`settings.${requestBodyKey}`] = requestBodyFieldValue;
})

Or you can first query your document to get the current_settings, then using javascript you would merge the two objects (e.g. {...myCurrentSettings, ...myNewerSettings } see spread operator or Object.assign()) and then update it to ensure that you keep the previous, untouched, field values.

Upvotes: 1

Related Questions