9

I am going to update some fields of mongoose document according to provided keys. For example, When we present mongoose document in json.

user: {
  address: {
    city: "city"
    country: "country"
  }
}

And update params is given like this.

address: {
   city: "city_new"
}

when I run the mongoose api like this.

let params = {
   address: {
      city: "city_new"
   }
}
User.set(param)

It replace whole address object and final result is

user: {
  address: {
    city: "city_new"
  }
}

it just replace address field, but I want to only update city field. This is desired result.

user: {
  address: {
    city: "city_new"
    country: "country"
  }
}

How to do this in mongoose?

When nested object has more complex hierarchy, how can we solve this without manually indicate field like address.city.field1.field2. ...

Thanks

Nomura Nori
  • 4,689
  • 8
  • 47
  • 85

2 Answers2

12

When nested object has more complex hierarchy, how can we solve this without manually indicate field like address.city.field1.field2.

As most answers intimated, you have to use the dot notation to update embedded documents and to answer your above question, use the following helper method which applies recursion to convert a given object to its dot notation representation:

function convertToDotNotation(obj, newObj={}, prefix="") {

  for(let key in obj) {
      if (typeof obj[key] === "object") {
          convertToDotNotation(obj[key], newObj, prefix + key + ".");
      } else {
          newObj[prefix + key] = obj[key];
      }
  }

  return newObj;
}


let params = {
   address: {
      city: {
         location: {
            street: "new street"
         }
      }  
   }
};

const dotNotated = convertToDotNotation(params);
console.log(JSON.stringify(dotNotated, null, 4));
Community
  • 1
  • 1
chridam
  • 100,957
  • 23
  • 236
  • 235
  • This issue is a duplicate of many others, but the most common solution I've seen is to use the 'flat' npm package and call something like: `Model.update(filter, flat(update))` – Jack Oct 08 '22 at 13:00
5

This should work:

let params = {
   "address.city": "city_new"
}
User.set(param)

In the documentation on $set you'll also find the following remark:

To specify a <field> in an embedded document or in an array, use dot notation.

dnickless
  • 10,733
  • 1
  • 19
  • 34
  • yes, but I am not going to indicate fields by dot. so if we use more deep hierarchy, we don't have to use dot operator. – Nomura Nori Aug 14 '18 at 18:36
  • There is no other way that I'm aware of. Kindly see the following links, too: https://stackoverflow.com/questions/15874750/partial-update-of-a-subdocument-with-nodejs-mongoose https://stackoverflow.com/questions/10290621/how-do-i-partially-update-an-object-in-mongodb-so-the-new-object-will-overlay https://jira.mongodb.org/browse/SERVER-21094 – dnickless Aug 14 '18 at 18:47