更新数组mongodb内的嵌套数组[重复]
Posted
技术标签:
【中文标题】更新数组mongodb内的嵌套数组[重复]【英文标题】:Updating nested array inside array mongodb [duplicate] 【发布时间】:2015-06-20 11:09:33 【问题描述】:我有以下 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);
);
【问题讨论】:
MongoDB 不支持匹配到多个级别的数组。考虑改变您的文档模型,使每个文档代表一个操作,其中包含操作文档中重复的一组操作的通用信息。 【参考方案1】:MongoDB 3.6 及更新版本
MongoDB 3.6 及更高版本提供了一项新功能,允许您使用the positional filtered $\[<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);
);
对于 MongoDB 3.4 及更早版本:
正如@wdberkeley 在他的评论中提到的那样:
MongoDB 不支持匹配多个级别的数组。 考虑改变你的文档模型,让每个文档代表一个 操作,具有重复的一组操作的公共信息 在操作文档中。
我同意上述观点,并建议重新设计您的架构,因为 MongoDB 引擎不支持多个位置运算符(请参阅 Multiple use of the positional $
operator to update nested arrays)
但是,如果您事先知道要更新参数对象的操作数组的索引,那么更新查询将是:
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 issue 链接到它。但是,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"
);
从 MapReduce 操作查询输出集合 operations
通常会给您结果:
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
);
;
【讨论】:
这确实有效,但是我有什么方法可以即时创建 $set 条件吗?可以帮助我获取对象的索引然后进行相应修改的东西? 我想知道我是否有可能实现他们在这里建议的东西***.com/questions/28756041/… @SantiagoEsquivel 很有可能。稍后将提供解决方案,但本质上是使用 MapReduce 获取带有数组索引的投影,并使用该投影中的索引值来动态更新数组元素。 好的,我一直在尝试复制他们在该答案中解释的内容,但没有取得多大成功。如果您今天晚些时候有时间回答,我会继续尝试,并希望我能接近您的解决方案。再次感谢。 @SantiagoEsquivel 我已经更新了答案以包含面向 MapReduce 解决方案的方法。【参考方案2】:从 mongo 3.6 版开始,您可以将 $[] 与 $[] 结合使用来更新嵌套数组
使用 $[] 更新嵌套数组
$[] 过滤位置运算符,与 所有 $[] 位置运算符都可用于更新嵌套数组。
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#position-nested-arrays-filtered
【讨论】:
【参考方案3】:我们将尝试找到外部数组(i)和内部数组(j)的索引,然后更新
collection.findById(04)
.then(result =>
for(let i = 0; i<result.operations.length; i++)
if(result.operation[i]._id == "99")
let parameters = result.operations[i].parameters;`enter code here`
for(let j = 0; j<parameters.length; j++)
if(parameters[j].pid == "011")
console.log("i", i);
console.log("j", j);
let data =
data["operations." + i + ".parameters." + j + ".oName"] = updateoName
data["operations." + i + ".parameters." + j + ".name"] = updatename
data["operations." + i + ".parameters." + j + ".pid"] = updatepid
data["operations." + i + ".parameters." + j + ".description"] = updatedescription
data["operations." + i + ".parameters." + j + ".value"] = updatevalue
console.log(data)
collection.update(
"_id": "04"
,
$set: data
)
.then(dbModel => res.json(dbModel))
)
【讨论】:
【参考方案4】:如果是经常变化的数据,你应该把结构扁平化,把变化很大的数据和没有变化的数据分开。
如果是不经常变化的数据,而且整个数据对象不是海量的,只要在客户端修改对象,更新整个对象即可。
【讨论】:
以上是关于更新数组mongodb内的嵌套数组[重复]的主要内容,如果未能解决你的问题,请参考以下文章