Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js - Mongoose - Update nested array with all values in req.body

I have an object that looks like this.

{
  _id: '577fe7a842c9b447',
  name: 'Jacob\'s Bronze Badges',
  competitors: [
    {
      _id: '577fe7a842c9bd6d',
      name: 'Peter\'s Silver Badges',
      sites: [
        {
          _id: '577fe7a842c9bd6d',
          name: 'Facebook',
          url: 'fb.com/peter'
        },
        {
          _id: '577fe7a842c9bd6d'
          name: 'Google',
          url: 'google.com/peter'
        }
      ]
    },
    {
      _id: '599fe7a842c9bd6d',
      name: 'Paul\'s Gold Badges',
      sites: [
        {
          '_id': '577fe7a842c9bd6d',
          name: 'Facebook',
          url: 'fb.com/paul'
        },
        {
          _id: '577fe7a842c9bd6d',
          name: 'Google',
          url: 'google.com/paul'
        }
      ]
    }
  ]
}

My goal is to reference the competitors array and update items inside with all of the values from req.body. I based this code off of this answer, as well as this other one.

Location.update(
  { 'competitors._id': req.params.competitorId, },
  { $set: { 'competitors.$': req.body, }, },
  (err, result) => {
    if (err) {
      res.status(500)
      .json({ error: 'Unable to update competitor.', });
    } else {
      res.status(200)
      .json(result);
    }
  }
);

I send my HTTP PUT to localhost:3000/competitors/577fe7a842c9bd6d to update Peter's Silver Badges. The request body is:

{
  "name": "McDonald's"
}

The problem is that when I use $set to set the competitor with _id: req.params.competitorId, I don't know what is in req.body. I want to use the entire req.body to update the object in the array, but when I do, that object is overwritten, so instead of getting a new name, Peter's Silver Badges becomes:

{
  name: 'McDonald\'s',
  sites: []
}

How can I update an object within an array when I know the object's _id with all of the fields from req.body without removing fields that I want to keep?

I believe that the sites array is empty because the object was reinitialized. In my schema I have sites: [sitesSchema] to initialize it. So I am assuming that the whole competitors[_id] object is getting overwritten with the new name and then the sites: [sitesSchema] from myschema.

like image 611
Jacob Avatar asked Jul 08 '16 19:07

Jacob


2 Answers

You would need to use the $ positional operator in your $set. In order to assign those properties dynamically, based on what is in your req.body, you would need to build up your $set programmatically.

If you want to update the name you would do the following:

Location.update(
  { 'competitors._id': req.params.competitorId },
  { $set:  { 'competitors.$.name': req.body.name }},
  (err, result) => {
    if (err) {
      res.status(500)
      .json({ error: 'Unable to update competitor.', });
    } else {
      res.status(200)
      .json(result);
    }
 }
);

One way you might programatically build up the $set using req.body is by doing the following:

let updateObj = {$set: {}};
for(var param in req.body) {
  updateObj.$set['competitors.$.'+param] = req.body[param];
 }

See this answer for more details.

like image 159
Thomas Wolfe Avatar answered Nov 06 '22 14:11

Thomas Wolfe


To update embedded document with $ operator, in most of the cases, you have to use dot notation on the $ operator.

Location.update(
  { _id: '577fe7a842c9b447', 'competitors._id': req.params.competitorId, },
  { $set: { 'competitors.$.name': req.body.name, }, },
  (err, result) => {
    if (err) {
      res.status(500)
      .json({ error: 'Unable to update competitor.', });
    } else {
      res.status(200)
      .json(result);
    }
  }
);
like image 42
Boris Siscanu Avatar answered Nov 06 '22 14:11

Boris Siscanu