How To Update Multiple Fields Of An Array Object With One Request?
Solution 1:
You need to supply the multiple keys to $set
with the positional $
operator to update both matched keys.
I prefer the modern ES6 way of object manipulation:
let params = { "_id" : "xxxproductid", "name" : "xxx", "img" : "yyy" };
let update = [
{ 'store.products._id': params._id },
{ "$set": Object.keys(params).filter(k => k != '_id')
.reduce((acc,curr) =>
Object.assign(acc,{ [`store.products.$.${curr}`]: params[curr] }),
{ })
}
];
User.update(...update,callback);
Which would produce the call to MongoDB as ( with mongoose.set('debug', true)
) turned on so we see the request:
Mongoose: users.update({ 'store.products._id': 'xxxproductid' }, { '$set': { 'store.products.$.name': 'xxx', 'store.products.$.img': 'yyy' } }, {})
Where basically you take your input params
and supply the _id
as the first argument for the "query" :
{ 'store.products._id': params._id },
The rest takes the "keys" from the object via Object.keys
which makes an "array" which we can "filter" with Array.filter()
and then pass to Array.reduce
to transform those keys into an Object
.
Inside the .reduce()
we call Object.assign()
which "merges" objects with the given keys, generated in this form:
Object.assign(acc,{ [`store.products.$.${curr}`]: params[curr] }),
Using the template syntax to assign the "current" (curr) "key" into the new key name, again using the ES6 key assignment syntax []:
which allows variable names in object literals.
The resulting "merged" object is passed back to be assigned to the "root" object where $set
is used for the key of the update, so the "generated" keys are now children of that.
I use an array for the arguments purely for debugging purposes, but then that also allows cleaner syntax on the actual .update()
using the "spread" ...
operator to assign the arguments:
User.update(...update,callback);
Clean and simple, and some JavaScript techniques that you should learn for object and array manipulation. Mostly since the MongoDB query DSL is basically "Objects" and "Arrays". So learn to manipulate them.
Solution 2:
function updateProducts(params) {
var query = {'store.products': {$elemMatch: {_id: params._id}}}
var updateObject = null;
if (params.name && params.img) {
updateObject = {$set: {'store.products.$': params}}
} else if(params.name && !params.img) {
updateObject = {$set: {'store.products.$.name': params.name}}
} else if (params.img && !params.name) {
updateObject = {$set: {'store.products.$.img': params.img}}
}
User.update(query, updateObject, callback)
}
Solution 3:
The below query will use $
positional operator to locate the array element at index found from matching the array by _id
in the query document followed by updating the fields for the element with the params
values.
var params = {_id:1, name:'xxx',img:'yyy'};
var id = params['_id']; // Get id to locate matching array index
delete params['_id']; // Remove id from the object
Object.keys(params).forEach(function (key){params['store.products.$.'+key] = params[key]; delete params[key];}) // Transforms remaining object keys to include the positional $ placeholder to { "store.products.$.name" : "xxx", "store.products.$.img" : "yyy" }
User.update('store.products._id':id},{$set:params},callback);
Post a Comment for "How To Update Multiple Fields Of An Array Object With One Request?"