mongodb数组更新运算符($、$[]、$[2020-03-14 本文已影响0人
小胖学编程
系列文章:
mongodb数组更新运算符($、$[]、$[<identifier>])
Spring Data mongodb中数组更新运算符([]、$[<identifier>])
-
$可以在不显示指定数组元素位置的情况下标识要更新的数组中的元素,$
只是单个占位符。
-
$[]要更新数组中的所有元素。
-
$[<identifier>]会使用一个或多个数组作为过滤条件进行匹配。
1. $运算符
$
运算符可在不显式指定数组中元素位置的情况下标识要更新的数组中的元素。
语法:
{ "<array>.$" : value }
当我们使用更新运算符,例如:db.collection.update() 或者db.collection.findAndModify(),时
存在两个限制条件:
-
$
只会更新数组中第一个匹配的值;
-
$
的数组必须是查询条件的一部分;
db.collection.update(
{<array>:value}, //查询条件中必须含有array
{<update operator>:{"<array>.$":value}} //只会更新第一个匹配的文档的值。
)
注意:更新操作分为两步(1)匹配(2)更换。使用$
等位置描述符的目的就是在不确定内置数组需要更换元素位置的前提下,进行占位处理。
1.1 $的使用案例
1.1.1 更新内置数组的值
db.students.insert([
{ "_id" : 1, "grades" : [ 85, 80, 80 ] },
{ "_id" : 2, "grades" : [ 88, 90, 92 ] },
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }
])
需求:将id为1的文档grades
数组中80修改为90。
失败的解决方案.png
可以显示的指定具体的下标{"grades.1":82}
来进行修改。
修改下标为1的数组元素.png
现在的问题是不知道数组中更换元素的具体位置。那么使用$
进行占位:
并且占的位是query document
中进行声明的grades:80
。也就是说必须在query document
包含数组字段
image.png
db.students.updateOne(
//定位到查询的文档。grades:80的含义是数组中只要存在80,便可匹配。
{"_id":1,grades:80},
//修改操作。
{$set:{"grades.$":82}}
);
错误案例.png
1.1.2 更新内置数组的文档
语法:
db.collection.update(
{ <query selector> },
{ <update operator>: { "array.$.field" : value } }
)
$
是数组文档的占位符。有助于更新包含嵌入式文档的数组。
db.student1.insert({
"_id" : 4,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 8 },
{ "grade" : 85, "mean" : 90, "std" : 6 },
{ "grade" : 85, "mean" : 85, "std" : 8 }
]
})
db.student1.update(
{"_id":4,"grades.grade":85}, //查询条件
{$set:{"grades.$.std":5}}, //更新后的值
{multi:true} //全部更新
)
执行结果:
/* 1 */
{
"_id" : 4,
"grades" : [{
"grade" : 80,
"mean" : 75,
"std" : 8
}, {
"grade" : 85,
"mean" : 90,
"std" : 5
}, {
"grade" : 85,
"mean" : 85,
"std" : 8
}]
}
$
只会更新数组中第一个匹配的值。
案例三:使用多个字段个字段更新嵌入式文档
db.student2.insert(
{
_id: 5,
grades: [
{ grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 5 },
{ grade: 90, mean: 85, std: 3 }
]
})
db.students.updateOne(
{
_id: 5,
grades: { $elemMatch: { grade: { $lte: 90 }, mean: { $gt: 80 } } }
},
{ $set: { "grades.$.std" : 6 } }
)
执行结果:
{
_id: 5,
grades: [
{ grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 6 },
{ grade: 90, mean: 85, std: 3 }
]
}
2. $[]占位符
注:3.6版本后的新功能:
功能:在查询条件匹配的情况下,$[]
会修改指定数组字段中的所有元素。
因为$[]
是修改整个数组。所以无需在查询条件中指定数组。
语法:
db.collection.updateMany(
{ <query conditions> },
{ <update operator>: { "<array>.$[]" : value } }
)
案例
案例1:替换数组全部内容:
db.testupdateinner2.insert([
{name:"数组1",myArray:[5,8]},
{name:"数组2",myArray:[8,5]},
{name:"数组3",myArray:[5]}
]);
只是修改了一条结果.png
最终结果:将符合条件的数组中的内容全部进行了替换。
{
"_id" : ObjectId("5e6d8b661ec58cc4b94b7b3c"),
"name" : "数组1",
"myArray" : [10, 10]
}
/* 2 */
{
"_id" : ObjectId("5e6d8b661ec58cc4b94b7b3d"),
"name" : "数组2",
"myArray" : [8, 5]
}
/* 3 */
{
"_id" : ObjectId("5e6d8b661ec58cc4b94b7b3e"),
"name" : "数组3",
"myArray" : [5]
}
案例二:若使用$
的情况
执行删除命令,重新插入数据testupdateinner2
文档
db.testupdateinner2.remove({})
使用$符会产生什么情况.png
案例三: 查询条件的值不是数组的情况
执行结果.png
案例四:显式指定更新多个文档
显式更新多个条件下,会更新多个文档.png
案例五:在查询条件中不指定数组
在查询条件中不指定数组.png
执行结果:
/* 1 */
{
"_id" : ObjectId("5e6d90101ec58cc4b94b7b48"),
"name" : "数组1",
"myArray" : [10, 10]
}
/* 2 */
{
"_id" : ObjectId("5e6d90101ec58cc4b94b7b49"),
"name" : "数组2",
"myArray" : [8, 5]
}
/* 3 */
{
"_id" : ObjectId("5e6d90101ec58cc4b94b7b4a"),
"name" : "数组3",
"myArray" : [5]
}
案例:更新数组文档
语法:
db.collection.update(
{ <query selector> },
{ <update operator>: { "array.$[].field" : value } }
)
案例:
db.student2.insert([
{
"_id" : 1,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 8 },
{ "grade" : 85, "mean" : 90, "std" : 6 },
{ "grade" : 85, "mean" : 85, "std" : 8 }
]
},
{
"_id" : 2,
"grades" : [
{ "grade" : 90, "mean" : 75, "std" : 8 },
{ "grade" : 87, "mean" : 90, "std" : 5 },
{ "grade" : 85, "mean" : 85, "std" : 6 }
]
}
])
处理语句:
db.student2.update(
{"grades.grade":85},
{$set:{"grades.$[].std":0}}
)
执行结果:
/* 1 */
{
"_id" : 1,
"grades" : [{
"grade" : 80,
"mean" : 75,
"std" : 0
}, {
"grade" : 85,
"mean" : 90,
"std" : 0
}, {
"grade" : 85,
"mean" : 85,
"std" : 0
}]
}
/* 2 */
{
"_id" : 2,
"grades" : [{
"grade" : 90,
"mean" : 75,
"std" : 8
}, {
"grade" : 87,
"mean" : 90,
"std" : 5
}, {
"grade" : 85,
"mean" : 85,
"std" : 6
}]
}
可以看到"_id":1
的内嵌数组文档全部被更新。
对比:
db.student2.update(
{"grades.grade":85},
{$set:{"grades.$.std":0}},
{multi:true}
)
执行结果:
/* 1 */
{
"_id" : 1,
"grades" : [{
"grade" : 80,
"mean" : 75,
"std" : 8
}, {
"grade" : 85,
"mean" : 90,
"std" : 0
}, {
"grade" : 85,
"mean" : 85,
"std" : 8
}]
}
/* 2 */
{
"_id" : 2,
"grades" : [{
"grade" : 90,
"mean" : 75,
"std" : 8
}, {
"grade" : 87,
"mean" : 90,
"std" : 5
}, {
"grade" : 85,
"mean" : 85,
"std" : 0
}]
}
每个元素中的内嵌数组中第一个匹配文档进行了更新。
3. $[<identifier>]
在monodb3.6版本后才可使用。
过滤后的位置运算符$[<identifier>]
标识与arrayFilters
条件匹配的数组元素来进行更新操作。
语法:
{ <update operator>: { "<array>.$[<identifier>]" : value } },
{ arrayFilters: [ { <identifier>: <condition> } ] }
注意:在<identifier>必须以小写字母开头,并且只包含字母数字字符。
案例:
db.student3.insert([
{ "_id" : 1, "grades" : [ 95, 92, 90 ] },
{ "_id" : 2, "grades" : [ 98, 100, 102 ] },
{ "_id" : 3, "grades" : [ 95, 110, 100 ] }
])
命令:
db.student3.update(
{},
{$set:{"grades.$[elem]":100}},
{multi:true,
arrayFilters:[{"elem":{$gte:100}}]}
)
结果:
/* 1 */
{
"_id" : 1,
"grades" : [95, 92, 90]
}
/* 2 */
{
"_id" : 2,
"grades" : [98, 100, 100]
}
/* 3 */
{
"_id" : 3,
"grades" : [95, 100, 100]
}
位置$[identifier]
运算符充当数组字段中arrayFilters
中指定条件匹配的所有元素的占位符。
db.collection.update(
{ <query selector> },
{ <update operator>: { "array.$[<identifier>].field" : value } },
{ arrayFilters: [ { <identifier>: <condition> } } ] }
)
案例二:
db.student4.insert([
{
"_id" : 1,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 6 },
{ "grade" : 85, "mean" : 90, "std" : 4 },
{ "grade" : 85, "mean" : 85, "std" : 6 }
]
},
{
"_id" : 2,
"grades" : [
{ "grade" : 90, "mean" : 75, "std" : 6 },
{ "grade" : 87, "mean" : 90, "std" : 3 },
{ "grade" : 85, "mean" : 85, "std" : 4 }
]
}
]);
修改grade
元素的std
属性为0.
db.student4.update(
{},
{$set:{"grades.$[elem].std":0}},
{
multi:true,
arrayFilters:[{"elem.grade":85}]
}
);
执行结果:
/* 1 */
{
"_id" : 1,
"grades" : [{
"grade" : 80,
"mean" : 75,
"std" : 6
}, {
"grade" : 85,
"mean" : 90,
"std" : 0
}, {
"grade" : 85,
"mean" : 85,
"std" : 0
}]
}
/* 2 */
{
"_id" : 2,
"grades" : [{
"grade" : 90,
"mean" : 75,
"std" : 6
}, {
"grade" : 87,
"mean" : 90,
"std" : 3
}, {
"grade" : 85,
"mean" : 85,
"std" : 0
}]
}
案例二:嵌套数组
db.student5.insert(
{ "_id" : 1,
"grades" : [
{ type: "quiz", questions: [ 10, 8, 5 ] },
{ type: "quiz", questions: [ 8, 9, 6 ] },
{ type: "hw", questions: [ 5, 8, 3 ] },
{ type: "exam", questions: [ 25, 10, 23, 0 ] },
]
}
)
修改内嵌数组中的值$[t]
代指的是grades
数组的元素。$[score]
代指的是questions
数组元素。
db.student5.update(
{},
{$set:{"grades.$[t].questions.$[score]":0}},
{arrayFilters:[{"t.type":"quiz"},{"score":8}]}
);
修改结果:
{
"_id" : 1,
"grades" : [{
"type" : "quiz",
"questions" : [10, 0, 5]
}, {
"type" : "quiz",
"questions" : [0, 9, 6]
}, {
"type" : "hw",
"questions" : [5, 4, 3]
}, {
"type" : "exam",
"questions" : [25, 10, 23, 0]
}]
}
案例二:
执行代码:
db.student5.update(
{},
{$set:{"grades.$[].questions.$[score]":0}},
{arrayFilters:[{"score":8}]}
);
得到案例:
{
"_id" : 1,
"grades" : [{
"type" : "quiz",
"questions" : [10, 0, 5]
}, {
"type" : "quiz",
"questions" : [0, 9, 6]
}, {
"type" : "hw",
"questions" : [5, 0, 3]
}, {
"type" : "exam",
"questions" : [25, 10, 23, 0]
}]
}
推荐阅读
系列文章:
mongodb数组更新运算符($、$[]、$[<identifier>])
Spring Data mongodb中数组更新运算符([]、$[<identifier>])
-
$可以在不显示指定数组元素位置的情况下标识要更新的数组中的元素,
$
只是单个占位符。 -
$[]要更新数组中的所有元素。
-
$[<identifier>]会使用一个或多个数组作为过滤条件进行匹配。
1. $运算符
$
运算符可在不显式指定数组中元素位置的情况下标识要更新的数组中的元素。
语法:
{ "<array>.$" : value }
当我们使用更新运算符,例如:db.collection.update() 或者db.collection.findAndModify(),时
存在两个限制条件:
-
$
只会更新数组中第一个匹配的值; -
$
的数组必须是查询条件的一部分;
db.collection.update(
{<array>:value}, //查询条件中必须含有array
{<update operator>:{"<array>.$":value}} //只会更新第一个匹配的文档的值。
)
注意:更新操作分为两步(1)匹配(2)更换。使用$
等位置描述符的目的就是在不确定内置数组需要更换元素位置的前提下,进行占位处理。
1.1 $的使用案例
1.1.1 更新内置数组的值
db.students.insert([
{ "_id" : 1, "grades" : [ 85, 80, 80 ] },
{ "_id" : 2, "grades" : [ 88, 90, 92 ] },
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }
])
需求:将id为1的文档grades
数组中80修改为90。
可以显示的指定具体的下标{"grades.1":82}
来进行修改。
现在的问题是不知道数组中更换元素的具体位置。那么使用$
进行占位:
并且占的位是query document
中进行声明的grades:80
。也就是说必须在query document
包含数组字段
db.students.updateOne(
//定位到查询的文档。grades:80的含义是数组中只要存在80,便可匹配。
{"_id":1,grades:80},
//修改操作。
{$set:{"grades.$":82}}
);
错误案例.png
1.1.2 更新内置数组的文档
语法:
db.collection.update(
{ <query selector> },
{ <update operator>: { "array.$.field" : value } }
)
$
是数组文档的占位符。有助于更新包含嵌入式文档的数组。
db.student1.insert({
"_id" : 4,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 8 },
{ "grade" : 85, "mean" : 90, "std" : 6 },
{ "grade" : 85, "mean" : 85, "std" : 8 }
]
})
db.student1.update(
{"_id":4,"grades.grade":85}, //查询条件
{$set:{"grades.$.std":5}}, //更新后的值
{multi:true} //全部更新
)
执行结果:
/* 1 */
{
"_id" : 4,
"grades" : [{
"grade" : 80,
"mean" : 75,
"std" : 8
}, {
"grade" : 85,
"mean" : 90,
"std" : 5
}, {
"grade" : 85,
"mean" : 85,
"std" : 8
}]
}
$
只会更新数组中第一个匹配的值。
案例三:使用多个字段个字段更新嵌入式文档
db.student2.insert(
{
_id: 5,
grades: [
{ grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 5 },
{ grade: 90, mean: 85, std: 3 }
]
})
db.students.updateOne(
{
_id: 5,
grades: { $elemMatch: { grade: { $lte: 90 }, mean: { $gt: 80 } } }
},
{ $set: { "grades.$.std" : 6 } }
)
执行结果:
{
_id: 5,
grades: [
{ grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 6 },
{ grade: 90, mean: 85, std: 3 }
]
}
2. $[]占位符
注:3.6版本后的新功能:
功能:在查询条件匹配的情况下,$[]
会修改指定数组字段中的所有元素。
因为$[]
是修改整个数组。所以无需在查询条件中指定数组。
语法:
db.collection.updateMany(
{ <query conditions> },
{ <update operator>: { "<array>.$[]" : value } }
)
案例
案例1:替换数组全部内容:
db.testupdateinner2.insert([
{name:"数组1",myArray:[5,8]},
{name:"数组2",myArray:[8,5]},
{name:"数组3",myArray:[5]}
]);
只是修改了一条结果.png
最终结果:将符合条件的数组中的内容全部进行了替换。
{
"_id" : ObjectId("5e6d8b661ec58cc4b94b7b3c"),
"name" : "数组1",
"myArray" : [10, 10]
}
/* 2 */
{
"_id" : ObjectId("5e6d8b661ec58cc4b94b7b3d"),
"name" : "数组2",
"myArray" : [8, 5]
}
/* 3 */
{
"_id" : ObjectId("5e6d8b661ec58cc4b94b7b3e"),
"name" : "数组3",
"myArray" : [5]
}
案例二:若使用$
的情况
执行删除命令,重新插入数据testupdateinner2
文档
db.testupdateinner2.remove({})
使用$符会产生什么情况.png
案例三: 查询条件的值不是数组的情况
执行结果.png案例四:显式指定更新多个文档
显式更新多个条件下,会更新多个文档.png案例五:在查询条件中不指定数组
在查询条件中不指定数组.png执行结果:
/* 1 */
{
"_id" : ObjectId("5e6d90101ec58cc4b94b7b48"),
"name" : "数组1",
"myArray" : [10, 10]
}
/* 2 */
{
"_id" : ObjectId("5e6d90101ec58cc4b94b7b49"),
"name" : "数组2",
"myArray" : [8, 5]
}
/* 3 */
{
"_id" : ObjectId("5e6d90101ec58cc4b94b7b4a"),
"name" : "数组3",
"myArray" : [5]
}
案例:更新数组文档
语法:
db.collection.update(
{ <query selector> },
{ <update operator>: { "array.$[].field" : value } }
)
案例:
db.student2.insert([
{
"_id" : 1,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 8 },
{ "grade" : 85, "mean" : 90, "std" : 6 },
{ "grade" : 85, "mean" : 85, "std" : 8 }
]
},
{
"_id" : 2,
"grades" : [
{ "grade" : 90, "mean" : 75, "std" : 8 },
{ "grade" : 87, "mean" : 90, "std" : 5 },
{ "grade" : 85, "mean" : 85, "std" : 6 }
]
}
])
处理语句:
db.student2.update(
{"grades.grade":85},
{$set:{"grades.$[].std":0}}
)
执行结果:
/* 1 */
{
"_id" : 1,
"grades" : [{
"grade" : 80,
"mean" : 75,
"std" : 0
}, {
"grade" : 85,
"mean" : 90,
"std" : 0
}, {
"grade" : 85,
"mean" : 85,
"std" : 0
}]
}
/* 2 */
{
"_id" : 2,
"grades" : [{
"grade" : 90,
"mean" : 75,
"std" : 8
}, {
"grade" : 87,
"mean" : 90,
"std" : 5
}, {
"grade" : 85,
"mean" : 85,
"std" : 6
}]
}
可以看到"_id":1
的内嵌数组文档全部被更新。
对比:
db.student2.update(
{"grades.grade":85},
{$set:{"grades.$.std":0}},
{multi:true}
)
执行结果:
/* 1 */
{
"_id" : 1,
"grades" : [{
"grade" : 80,
"mean" : 75,
"std" : 8
}, {
"grade" : 85,
"mean" : 90,
"std" : 0
}, {
"grade" : 85,
"mean" : 85,
"std" : 8
}]
}
/* 2 */
{
"_id" : 2,
"grades" : [{
"grade" : 90,
"mean" : 75,
"std" : 8
}, {
"grade" : 87,
"mean" : 90,
"std" : 5
}, {
"grade" : 85,
"mean" : 85,
"std" : 0
}]
}
每个元素中的内嵌数组中第一个匹配文档进行了更新。
3. $[<identifier>]
在monodb3.6版本后才可使用。
过滤后的位置运算符$[<identifier>]
标识与arrayFilters
条件匹配的数组元素来进行更新操作。
语法:
{ <update operator>: { "<array>.$[<identifier>]" : value } },
{ arrayFilters: [ { <identifier>: <condition> } ] }
注意:在<identifier>必须以小写字母开头,并且只包含字母数字字符。
案例:
db.student3.insert([
{ "_id" : 1, "grades" : [ 95, 92, 90 ] },
{ "_id" : 2, "grades" : [ 98, 100, 102 ] },
{ "_id" : 3, "grades" : [ 95, 110, 100 ] }
])
命令:
db.student3.update(
{},
{$set:{"grades.$[elem]":100}},
{multi:true,
arrayFilters:[{"elem":{$gte:100}}]}
)
结果:
/* 1 */
{
"_id" : 1,
"grades" : [95, 92, 90]
}
/* 2 */
{
"_id" : 2,
"grades" : [98, 100, 100]
}
/* 3 */
{
"_id" : 3,
"grades" : [95, 100, 100]
}
位置$[identifier]
运算符充当数组字段中arrayFilters
中指定条件匹配的所有元素的占位符。
db.collection.update(
{ <query selector> },
{ <update operator>: { "array.$[<identifier>].field" : value } },
{ arrayFilters: [ { <identifier>: <condition> } } ] }
)
案例二:
db.student4.insert([
{
"_id" : 1,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 6 },
{ "grade" : 85, "mean" : 90, "std" : 4 },
{ "grade" : 85, "mean" : 85, "std" : 6 }
]
},
{
"_id" : 2,
"grades" : [
{ "grade" : 90, "mean" : 75, "std" : 6 },
{ "grade" : 87, "mean" : 90, "std" : 3 },
{ "grade" : 85, "mean" : 85, "std" : 4 }
]
}
]);
修改grade
元素的std
属性为0.
db.student4.update(
{},
{$set:{"grades.$[elem].std":0}},
{
multi:true,
arrayFilters:[{"elem.grade":85}]
}
);
执行结果:
/* 1 */
{
"_id" : 1,
"grades" : [{
"grade" : 80,
"mean" : 75,
"std" : 6
}, {
"grade" : 85,
"mean" : 90,
"std" : 0
}, {
"grade" : 85,
"mean" : 85,
"std" : 0
}]
}
/* 2 */
{
"_id" : 2,
"grades" : [{
"grade" : 90,
"mean" : 75,
"std" : 6
}, {
"grade" : 87,
"mean" : 90,
"std" : 3
}, {
"grade" : 85,
"mean" : 85,
"std" : 0
}]
}
案例二:嵌套数组
db.student5.insert(
{ "_id" : 1,
"grades" : [
{ type: "quiz", questions: [ 10, 8, 5 ] },
{ type: "quiz", questions: [ 8, 9, 6 ] },
{ type: "hw", questions: [ 5, 8, 3 ] },
{ type: "exam", questions: [ 25, 10, 23, 0 ] },
]
}
)
修改内嵌数组中的值$[t]
代指的是grades
数组的元素。$[score]
代指的是questions
数组元素。
db.student5.update(
{},
{$set:{"grades.$[t].questions.$[score]":0}},
{arrayFilters:[{"t.type":"quiz"},{"score":8}]}
);
修改结果:
{
"_id" : 1,
"grades" : [{
"type" : "quiz",
"questions" : [10, 0, 5]
}, {
"type" : "quiz",
"questions" : [0, 9, 6]
}, {
"type" : "hw",
"questions" : [5, 4, 3]
}, {
"type" : "exam",
"questions" : [25, 10, 23, 0]
}]
}
案例二:
执行代码:
db.student5.update(
{},
{$set:{"grades.$[].questions.$[score]":0}},
{arrayFilters:[{"score":8}]}
);
得到案例:
{
"_id" : 1,
"grades" : [{
"type" : "quiz",
"questions" : [10, 0, 5]
}, {
"type" : "quiz",
"questions" : [0, 9, 6]
}, {
"type" : "hw",
"questions" : [5, 0, 3]
}, {
"type" : "exam",
"questions" : [25, 10, 23, 0]
}]
}