node.js - Updating nested array inside array mongodb -
i have following mongodb document structure:
[ { "_id": "04", "name": "test service 4", "id": "04", "version": "0.0.1", "title": "testing", "description": "test", "protocol": "test", "operations": [ { "_id": "99", "oname": "test op 52222222222", "sid": "04", "name": "test op 52222222222", "oid": "99", "description": "testing", "returntype": "test", "parameters": [ { "oname": "param1", "name": "param1", "pid": "011", "type": "582", "description": "testing", "value": "" }, { "oname": "param2", "name": "param2", "pid": "012", "type": "58222", "description": "testing", "value": "" } ] } ] } ]
i have been able use $elemmatch in order update fields in operations, when try same thing (modified) parameters not seem work. wondering other approach should trying in order able update fields in specific parameter, looking pid.
the update code have , not work looks this:
var oid = req.params.operations; var pid = req.params.parameters; collection.update({"parameters":{"$elemmatch": {"pid": pid}}},{"$set": {"parameters.$.name":req.body.name, "parameters.$.description": req.body.description,"parameters.$.oname": req.body.oname,"parameters.$.type": req.body.type} }, function(err, result) { if (err) { console.log('error updating service: ' + err); res.send({'error':'an error has occurred'}); } else { // console.log('' + result + ' document(s) updated'); res.send(result); } });
as @wdberkeley mentioned in comment:
mongodb doesn't support matching more 1 level of array. consider altering document model each document represents operation, information common set of operations duplicated in operation documents.
i concur above , recommend redesigning schema mongodb engine not support multiple positional operators ( see multiple use of positional $
operator update nested arrays)
however, if know index of operations array has parameters object updated beforehand update query be:
db.collection.update( { "_id" : "04", "operations.parameters.pid": "011" }, { "$set": { "operations.0.parameters.$.name": "foo", "operations.0.parameters.$.description": "bar", "operations.0.parameters.$.type": "foo" } } )
edit:
if create $set
conditions on fly i.e. indexes objects , modify accordingly, consider using mapreduce.
currently seems not possible using aggregation framework. there unresolved open jira issue linked it. however, workaround possible mapreduce. basic idea mapreduce uses javascript query language tends slower aggregation framework , should not used real-time data analysis.
in mapreduce operation, need define couple of steps i.e. mapping step (which maps operation every document in collection, , operation can either nothing or emit object keys , projected values) , reducing step (which takes list of emitted values , reduces single element).
for map step, ideally want every document in collection, index each operations
array field , key contains $set
keys.
your reduce step function (which nothing) defined var reduce = function() {};
the final step in mapreduce operation create separate collection operations contains emitted operations array object along field $set
conditions. collection can updated periodically when run mapreduce operation on original collection. altogether, mapreduce method like:
var map = function(){ for(var = 0; < this.operations.length; i++){ emit( { "_id": this._id, "index": }, { "index": i, "operations": this.operations[i], "update": { "name": "operations." + i.tostring() + ".parameters.$.name", "description": "operations." + i.tostring() + ".parameters.$.description", "type": "operations." + i.tostring() + ".parameters.$.type" } } ); } }; var reduce = function(){}; db.collection.mapreduce( map, reduce, { "out": { "replace": "operations" } } );
querying output collection operations
mapreduce operation typically give result:
db.operations.findone()
output:
{ "_id" : { "_id" : "03", "index" : 0 }, "value" : { "index" : 0, "operations" : { "_id" : "96", "oname" : "test op 52222222222", "sid" : "04", "name" : "test op 52222222222", "oid" : "99", "description" : "testing", "returntype" : "test", "parameters" : [ { "oname" : "param1", "name" : "foo", "pid" : "011", "type" : "foo", "description" : "bar", "value" : "" }, { "oname" : "param2", "name" : "param2", "pid" : "012", "type" : "58222", "description" : "testing", "value" : "" } ] }, "update" : { "name" : "operations.0.parameters.$.name", "description" : "operations.0.parameters.$.description", "type" : "operations.0.parameters.$.type" } } }
you can use cursor db.operations.find()
method iterate on , update collection accordingly:
var oid = req.params.operations; var pid = req.params.parameters; var cur = db.operations.find({"_id._id": oid, "value.operations.parameters.pid": pid }); // iterate through results , update using update query object set dynamically using array-index syntax. while (cur.hasnext()) { var doc = cur.next(); var update = { "$set": {} }; // set update query object update["$set"][doc.value.update.name] = req.body.name; update["$set"][doc.value.update.description] = req.body.description; update["$set"][doc.value.update.type] = req.body.type; db.collection.update( { "_id" : oid, "operations.parameters.pid": pid }, update ); };
Comments
Post a Comment