Cannot Read property '_id' of undefined while using mongoose findOneandUpdate

bring2dip picture bring2dip · Sep 14, 2014 · Viewed 11k times · Source

I am using mean.io to make sports event management system.I am trying to update a model Player with rest api and it throws this error from mongoose:

  { _id: 5411c79895d600440c698fa1,
  email: '[email protected]',
  name: 'James Bond',
  username: 'james.bond',
  games: [ 5411bd54786cfe2420f1e27a ],
  teams: [],
  roles: [ 'authenticated' ] }

[TypeError: Cannot read property '_id' of undefined]

PUT /api/players/5411c79895d600440c698fa1 500 199.402 ms - 36

I also tried to delete the _id proterty from player but it doesnot works either. the method i have used to update the model player is :

exports.update = function(req, res) {
    var player = req.player;
    player = _.extend(player, req.body);        
    console.log(player);

    Player.findOneAndUpdate(req.params.playerId, player,{new:true}, function (err, updatedPlayer) {
        console.log(err);
        if (err) {
            return res.status(500).json({
                error: 'Cannot update the player'
            });
        }
        res.json(updatedPlayer);
    });

And also if i used the model.save method provided in the default package article of mean.io, it shows another error. I have extended the user model in player package app.js file. So whenever I try to update one field, the field that I have declared in app.js are required and the path required error from mongoose is thrown.

Answer

Mattias Farnemyhr picture Mattias Farnemyhr · Sep 14, 2014

You have two issues in your update request.

First, the findOneAndUpdate expects a dict as the query and not just the id, so you should give it {_id: req.params.playerId} instead.

Second, passing a mongoose object as the update data is risky, instead you should convert it to a dict like this var _player = player.toObject() and then have _player be passed to the update request. Remember that you need to remove the _id param of_player because you can't change the _id of a document. Before doing the update just do delete _player._id and you should be fine. Also, new is set to true by default so you won't need the options dict.

Here is your working code:

var player = _.extend(req.player, req.body);        
var _player = player.toObject();
delete _player._id;

var query = {_id: req.params.playerId};
Player.findOneAndUpdate(query, _player, function (err, updatedPlayer) {
    ...
});

But in this case you shouldn't even have to do the first operation. Since you just want to update the data of req.body you can do:

var query = {_id: req.params.playerId};
var data = {
    $set: req.body,
};
Player.findOneAndUpdate(query, data, function (err, updatedPlayer) {
    ...
});

This will update all the fields of the player matching the query with the values from req.body.