MongoDB 3.6及更高版本
MongoDB3.6及更高版本提供了一项新功能,该功能允许您使用位置过滤的
$[<identifier>]语法来更新嵌套数组,以匹配特定元素并
arrayFilters在update语句中应用不同的条件:
const { oid, pid } = req.params;const { name, oName, description, type } = req.body;collection.update( { "_id": 1, "operations": { "$elemMatch": { oid, "parameters.pid": pid } } }, { "$set": { "operations.$[outer].parameters.$[inner].name": name, "operations.$[outer].parameters.$[inner].description": description, "operations.$[outer].parameters.$[inner].oName": oName, "operations.$[outer].parameters.$[inner].type": type } }, { "arrayFilters": [ { "outer.oid": oid }, { "inner.pid": pid } ] }, (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); }});对于MongoDB 3.4及更早版本:
正如@wdberkeley在他的评论中提到的:
MongoDB不支持匹配多个级别的数组。考虑更改文档模型,以便每个文档都代表一个操作,并具有操作文档中重复的一组操作所共有的信息。
我同意上述观点,并建议重新设计架构,因为MongoDB引擎不支持多个位置运算符
但是,如果您知道具有要预先更新的参数对象的操作数组的索引,则更新查询将为:
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" } })编辑:
如果您想 $set
即时创建条件,即可以帮助您获取对象索引然后进行相应修改的条件,请考虑使用
MapReduce 。
当前,使用聚合框架似乎不可能做到这一点。与之相关的一个未解决的未解决
JIRA问题 。但是,可以使用
MapReduce
解决。MapReduce的基本思想是它使用Javascript作为查询语言,但是它比聚合框架要慢得多,因此不应用于实时数据分析。
在您的MapReduce操作中,您需要定义几个步骤,即映射步骤(将操作映射到集合中的每个文档中,并且该操作不能执行任何操作或使用键和投影值发出某些对象)和减少步骤(它接受发射值列表并将其简化为单个元素)。
对于映射步骤,理想情况下,您希望获取集合中的每个文档,每个
operations数组字段的索引以及包含这些
$set键的另一个键。
您的reduce步骤将是一个简单地定义为的函数(不执行任何操作)
var reduce = function() {};然后,MapReduce操作的最后一步将创建一个单独的集合操作,其中包含发出的操作数组对象以及带有
$set条件的字段。在原始集合上运行MapReduce操作时,可以定期更新此集合。总而言之,此MapReduce方法如下所示:
var map = function(){ for(var i = 0; i < this.operations.length; i++){ emit( { "_id": this._id, "index": i }, { "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" } });operations从MapReduce操作查询输出集合通常会给您结果:
db.operations.findOne()
输出 :
{ "_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" } }}然后,您可以使用
db.operations.find()方法中的游标进行迭代,并相应地更新您的集合:
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 and update using the update query object set dynamically by using the array-index syntax.while (cur.hasNext()) { var doc = cur.next(); var update = { "$set": {} }; // set the 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 );};


