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

12,762

Solution 1

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.

Solution 2

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);
    }
  }
);
Share:
12,762
Jacob
Author by

Jacob

Updated on June 07, 2022

Comments

  • Jacob
    Jacob almost 2 years

    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.

  • meenachinmay
    meenachinmay about 4 years
    yes it is working but in my code i have to send two request for a query to exexute it. ```