Overview
This paper describes in mongodb in how to update the value of a nested array.
Use $ array update
- Basic syntax { " . <Array> $ " : value}
- It may be used to: update, findAndUpdate the like
- $ Is the same as the existence of a placeholder. Represents an element in the array is matched
- Can match an array, a plurality of matching is abnormally index 0: 2 - Too MANY Positional (IE '$') Elements found in path ... , that is: one can only be used in a nested $ array
- Examples
db.collection.update( { <array>: value ... }, { <update operator>: { "<array>.$" : value } } )
test
- Create a test data
for (let i = 0; i < 3; i++) { let data = { name1_1: 'test' + i, arr_1: [{ a: i, b: 2 }, { a: i + 1, b: 2 }] }; db.nestedUpdate.insert(data); }
Data capture:
- I want to update arr_1 array, a = 1, the object is updated to {a: 11, b: 12} update operation code as follows:
db.nestedUpdate.updateMany({ 'arr_1.a': 1 }, { $set: { 'arr_1.$.a': 11, 'arr_1.$.b': 12, } })
运行后数据截图:
- 针对上述结构,更新 a= 11 的对象值(与上面不同,上面是更新对象里面的一个值),运行一下代码:
db.nestedUpdate.updateMany({ 'arr_1.a': 11 }, { $set: {
'arr_1.$': {a:11, c:[1,2,3]}
} })运行结果:
-
继续编辑,修改 arr_1.c 的元素,很容易想到如下:
db.nestedUpdate.updateMany({ 'arr_1.c': 1 }, { $set: { 'arr_1.$.$': 11, } })
然而,最终的运行结果却是: [Error] index 0: 2 - Too many positional (i.e. '$') elements found in path 'arr_1.$.$'
那么,我想更新数组中的数组下的一个元素这么办呢?下面介绍两种方法:1、遍历数组修改,2、使用 arrayFilter。个人推荐 arrayFilter 方式。
.find.foreach + save (循环判断保存法)
- 通过 .find 找到满足条件的集合,(但只能找到根节点)
- 遍历需要修改的节点,修改其值,(先遍历arr_1, 在遍历 arr_1.c)
- 把修改完成的对象,通过 save 方法更新回数据库。
- 代码如下
// 查找所有 var all1 = db.nestedUpdate.find({}); all1.forEach(function(it) { var modified = false; // 遍历 arr_1 for (var i = 0; i < it.arr_1.length; ++i) { var ac1 = it.arr_1[i]; // 判断需要修改的 if (ac1.c && ac1.c.length > 0 && ac1.c[0] == 1) { ac1.c[0] = 1111; modified = true; } } if (modified) { db.nestedUpdate.save(it); } })
利用arrayFilter
- 基本语法
db.collection.updateMany( { <query conditions> }, { <update operator>: { "<array>.$[<identifier>]" : value } }, { arrayFilters: [ { <identifier>: <condition> } ] } )
- 官方文档地址:https://docs.mongodb.com/manual/reference/operator/update-array/
-
如上,建立一个示例,把 arr_1.c的值改回去
注意
- arrayFilter 数组中的顶级字段不能重复,如下:出现了两个 idx0,运行报错 index 0: 9 - Found multiple array filters with the same top-level field name idx0
db.nestedUpdate.updateMany({}, { $set: { 'arr_1.$[idx0].c.$[idx1]': 1 } }, { arrayFilters: [ { // idx0 满足条件: 需存在 c 字段 'idx0.c': { $exists: true }, }, { 'idx0.a': 1, }, { // idx1: 满足 值为 111 'idx1': 1111 } ] }); > [Error] index 0: 9 - Found multiple array filters with the same top-level field name idx0 at line 1, column 1
- arrayFilter 中可以嵌套条件,如:
db.nestedUpdate.updateMany({}, { $set: { 'arr_1.$[idx0].c.$[idx1]': 1 } }, { arrayFilters: [ { // idx0 满足条件: 需存在 c 字段 'idx0.c': { $exists: true }, 'idx0.a': 1, }, { // idx1: 满足 值为 111 'idx1': 1111 } ] }); // 或 db.nestedUpdate.updateMany({}, { $set: { 'arr_1.$[idx0].c.$[idx1]': 1 } }, { arrayFilters: [ { // idx0 满足条件: 需存在 c 字段 idx0: { c: { $exists: true }, a: 1 } }, { // idx1: 满足 值为 111 'idx1': 1111 } ] });
- arrayFilter 必须包含所有的索引的条件。否则出现错误 [Error] index 0: 2 - No array filter found for identifier 'idx2' in path 'arr_1.$[].arr_1_1.$[idx1].arr1_1_1.$[idx2]'
db.nestedUpdate1.updateMany({}, { $set: { 'arr_1.$[].arr_1_1.$[idx1].arr1_1_1.$[idx2]': null } }, { arrayFilters: [ { // idx1: 满足 name <= 1 'idx1.name': { $lte: 1 } }, ] }) > [Error] index 0: 2 - No array filter found for identifier 'idx2' in path 'arr_1.$[].arr_1_1.$[idx1].arr1_1_1.$[idx2]' at line 1, column 1 > 时间: 0.003s
- $[idx] 中的idx 可以自定义名字,只需要arrayFilter中名字一样就可以,如 $[i], $[j]
- 不止updateMany可以用,update、findAndUpdate、findAndModify 等也可以用
- 可以与$[] 一起使用,需保证数组中的所有元素都满足后面的条件,如:
db.nestedUpdate1.updateMany({}, { $set: { 'arr_1.$[].arr_1_1.$[idx1].arr1_1_1.$[idx2]': null } }, { arrayFilters: [ { // idx1: 满足 name <= 1 'idx1.name': { $lte: 1 } }, { idx2: 1 } ] })
运行示意: