node.js - findByIdAndUpdate With Multiple Subdocuments -
so i'm working nodejs , mongodb, , i'm making endpoint lets clients update user profiles several optional data fields. so, 1 of update queries can this:
{ name: { givenname: 'first' }, about: 'whatever', auth: { password: 'hashedpw' } }
the mongoose api docs state following info findbyidandupdate
: top level update keys not atomic operation names treated set operations.
so top level key, about
, works fine update. however, nested keys, name
, auth
overwritten update values, rather having values set.
now go through , manually change each of fields $set
key, there lot of different fields, pretty annoying. there easy way apply $set rule subdocuments well? i.e. transform statement this, mongoose option or something:
{ $set : { name: { givenname: 'first' } }, $set : { about: 'whatever' }, $set : { auth: { password: 'hashedpw' } } }
you need tranform input object "dot notation" form in order avoid overwring other possible sub-keys in update. quite simple really:
var obj = { name: { givenname: 'first' }, about: 'whatever', auth: { password: 'hashedpw' } }; var target = {}; function dotnotate(obj,prefix) { prefix = (typeof(prefix) === 'undefined') ? "" : prefix; object.keys(obj).foreach(function(key) { if ( typeof(obj[key]) === "object" ) { dotnotate(obj[key],key + ".") } else { target[prefix + key] = obj[key]; } }); } dotnotate(obj);
now target
object looks this:
{ "name.givenname" : "first", "about" : "whatever", "auth.password" : "hashedpw" }
so update block of statement merely written as:
{ "$set": target }
for reference, dotnotate()
function can bit more refined , self contained. including shorter default assignments valid input considered "truthy". "prefix" should have been pre-pended on each call make work @ arbitrary depth:
function dotnotate(obj,target,prefix) { target = target || {}, prefix = prefix || ""; object.keys(obj).foreach(function(key) { if ( typeof(obj[key]) === "object" ) { dotnotate(obj[key],target,prefix + key + "."); } else { return target[prefix + key] = obj[key]; } }); return target; }
then can use either inline:
var update = { "$set": dotnotate(obj) };
or pass in defined object if prefer:
var update = { "$set": {} }; dotnotate(obj,update["$set"]);
with same results.
also fine arrays , nested depth:
{ "things" : [ { "a" : 1, "b" : 2 }, { "a" : 3, "b" : 4 } ], "bool" : false }
with output:
{ "things.0.a" : 1, "things.0.b" : 2, "things.1.a" : 3, "things.1.b" : 4, "bool" : false }
Comments
Post a Comment