我有以下mongodb文件结构:
[ { "_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": "" } ] } ] } ]
我已经能够使用$ elemMatch来更新操作中的字段,但是当我尝试对参数执行相同的操作(修改)时,它似乎不起作用。我想知道我应该尝试哪种其他方法,以便能够成功更新特定参数中的字段(通过其pid查找)。
我当前拥有但不起作用的更新代码如下所示:
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); } });
MongoDB3.6及更高版本提供了一项新功能,该功能允许您使用位置过滤的$\[<identifier>\]语法来更新嵌套数组,以匹配特定元素并arrayFilters在update语句中应用不同的条件:
$\[<identifier>\]
arrayFilters
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); } });
正如@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 。
$set
当前,使用聚合框架似乎不可能做到这一点。与之相关的一个未解决的未解决 JIRA问题 。但是,可以使用 MapReduce 解决。MapReduce的基本思想是它使用JavaScript作为查询语言,但是它比聚合框架要慢得多,因此不应用于实时数据分析。
在您的MapReduce操作中,您需要定义几个步骤,即映射步骤(将操作映射到集合中的每个文档中,并且该操作不能执行任何操作或使用键和投影值发出某些对象)和减少步骤(它接受发射值列表并将其简化为单个元素)。
对于映射步骤,理想情况下,您希望获取集合中的每个文档,每个operations数组字段的索引以及包含这些$set键的另一个键。
operations
您的reduce步骤将是一个简单地定义为的函数(不执行任何操作) var reduce = function() {};
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()方法中的游标进行迭代,并相应地更新您的集合:
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 ); };