Mongoose update only the values that have changed

Viewed 8276

I have a PUT route to update value. I am hitting this route from two places. One is sending information about details and one about completed. The problem is that mongoose is updating booth even though it gets value from only one.

So if I send information about completed that it is true and latter I hit this route with new details (that dont have completed value) it will update completed also to false. How do I update just the value that was changed?

router.put('/:id', (req, res) => {
  Todo.findOne({_id:req.body.id}, (err, foundObject) => {
      foundObject.details = req.body.details
      foundObject.completed = req.body.completed
    foundObject.save((e, updatedTodo) => {
      if(err) {
        res.status(400).send(e)
      } else {
        res.send(updatedTodo)
      }
    })
  })
})

EDIT: Thanks to Jackson hint I was managed to do it like this.

router.put('/:id', (req, res) => {
  Todo.findOne({_id:req.body.id}, (err, foundObject) => {
    if(req.body.details !== undefined) {
      foundObject.details = req.body.details
    }
    if(req.body.completed !== undefined) {
      foundObject.completed = req.body.completed
    }
    foundObject.save((e, updatedTodo) => {
      if(err) {
        res.status(400).send(e)
      } else {
        res.send(updatedTodo)
      }
    })
  })
})
3 Answers

Had a function similar to this my approach was this

const _ = require('lodash');

router.put('/update/:id',(req,res, next)=>{
       todo.findById({
        _id: req.params.id
    }).then(user => {
        const obj = {
            new: true
        }
        user = _.extend(user, obj);
        user.save((error, result) => {
            if (error) {
                console.log("Status not Changed")
            } else {
                res.redirect('/')
            }
        })
    }).catch(error => {
        res.status(500);
    })
};

Taking new : true as the value you updating

It gets kinda ugly as the fields to be updated get increased. Say 100 fields. I would suggest using the following approach:

try {

    const schemaProperties = Object.keys(Todo.schema.paths)
    const requestKeys = Object.keys(req.body)
    const requestValues = Object.values(req.body)
    const updateQuery = {}

    // constructing dynamic query        
    for (let i = 0; i < requestKeys.length; i++) {
        // Only update valid fields according to Todo Schema
        if ( schemaProperties.includes(requestKeys[i]) ){
            updateQuery[requestKeys[i]] = requestValues[i]
        }
    }
    const updatedObject = await TOdo.updateOne(
        { _id:req.params.idd},
        { $set: updateQuery }
    );
    res.json(updatedObject)
} catch (error) {
    res.status(400).send({ message: error });
}
Related