msmfsd msmfsd - 5 months ago 63
Node.js Question

mongoose findOneAndUpdate deletes nested vars

In my express/mongoose code when I update a nested var it deletes any other nested vars in the object I didn't update?

My schema:

var MySchema = new Schema({
active: { type: Boolean, required: true, default: true },
myvar: {
useDefault: { type: Boolean, required: true },
custom: { type: String },
default: { type: String }
}
});


My express middleware update function:

var updateData = req.body;
MySchema.findOneAndUpdate({ active: true }, updateData, function(err, myschema) {
if (err) throw err;
if (!myschema) { response(403, { success:false, message: "myschema not updated." }, res); }
// success
response(200, { success:true, message: "myschema updated." }, res);
});


The record initially look like this:

{
"myvar": {
"useDefault": true,
"custom": "some custom var",
"default": "some value"
}
}


When I submit an update to say the myvar.useDefault value the record ends up like this:

{
"myvar": {
"useDefault": false
}
}


Can anyone advise how to update only the targeted var and leave any other vars as they were?

Answer

The answer was to convert the response.body object to dot notation and pass that modified object to mongoose findOneAndUpdate. Thanks to @btkostner on Gitter for the answer and his toDot function. Here is the relevant bits of code:

function(req, res) {
  // convert response to dot notation so objects retain other values
  var dotData = toDot(req.body, '.');
  MySchema.findOneAndUpdate({ active: true }, dotData, function(err, myschema) {
    ...
}

// author: @btcostner 
function toDot (obj, div, pre) {
  if (typeof obj !== 'object') {
    throw new Error('toDot requires a valid object');
  }
  if (pre != null) {
    pre = pre + div;
  } else {
    pre = '';
  }
  var iteration = {};
  Object.keys(obj).forEach(function(key) {
    if (_.isPlainObject(obj[key])) {
      Object.assign(iteration, _this.toDot(obj[key], div, pre + key));
    } else {
      iteration[pre + key] = obj[key];
    }
  });
  return iteration;
};

NOTE: I had to convert the toDot() function to ES5 for my project, here is the original ES6 version: github.com/elementary/houston/blob/master/src/lib/helpers/dotNotation.js