$ (update)
定义
$
位置
$
操作符确定数组中要更新的元素,而不显式指定元素在数组中的位置。注意
消歧
要投影或返回读取操作中的数组元素,请参阅
$
投影操作符。如需更新数组中的所有元素,请参阅全位置操作符
$[]
。如要更新与数组过滤条件匹配的所有元素,请参阅过滤位置操作符
$[<identifier>]
。
兼容性
对于在以下环境中托管的部署,可以使用位置 $
运算符:
MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务
MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本
MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本
语法
位置 $
操作符采用以下形式:
{ "<array>.$" : value }
与更新操作一起使用时,例如db.collection.updateOne()
和 db.collection.findAndModify()
,
位置
$
操作符充当与query document
匹配的第一个元素的占位符,并且array
字段必须作为query document
的一部分出现。
例如:
db.collection.updateOne( { <array>: value ... }, { <update operator>: { "<array>.$" : value } } )
行为
从 MongoDB 5.0 开始,更新操作符按字典顺序处理具有基于字符串的名称的文档字段。具有数字名称的字段按数字顺序处理。详情请参阅更新操作符行为。
upsert
嵌套数组
取消设置
否定
如果查询使用否定操作符(例如 $ne
、$not
或 $nin
)匹配数组,则不能使用位置操作符更新该数组中的值。
但是,如果查询的否定部分位于$elemMatch
表达式中,则可以使用位置操作符更新该字段。
多个数组匹配
对多个数组字段进行过滤时,位置 $
更新操作符就会表现得不明确。
当服务器执行某一更新方法时,它会先运行查询以确定要更新的文档。如果此更新会过滤针对多个数组字段的文档,则随后对位置 $
更新运算符的调用并不总会更新数组中的所需位置。
更多信息,请参阅示例。
示例
更新数组中的值
使用以下文档创建集合 students
:
db.students.insertMany( [ { "_id" : 1, "grades" : [ 85, 80, 80 ] }, { "_id" : 2, "grades" : [ 88, 90, 92 ] }, { "_id" : 3, "grades" : [ 85, 100, 90 ] } ] )
要将 grades
数组中第一个值为 80
的元素更新为 82
,在不知道该元素在数组中的位置时,可使用位置 $
操作符:
重要
您必须将数组字段包含在 query
文档中。
db.students.updateOne( { _id: 1, grades: 80 }, { $set: { "grades.$" : 82 } } )
操作完成后,students
集合包含以下文档:
{ "_id" : 1, "grades" : [ 85, 82, 80 ] } { "_id" : 2, "grades" : [ 88, 90, 92 ] } { "_id" : 3, "grades" : [ 85, 100, 90 ] }
更新数组中的文档
位置 $
操作符可简化对包含嵌入式文档的数组的更新。使用位置 $
操作符访问嵌入式文档中对 $
操作符采用点表示法的字段。
db.collection.updateOne( { <query selector> }, { <update operator>: { "array.$.field" : value } } )
考虑下面 students
集合中的文档,其 grades
元素值是一个嵌入文档的数组:
{ _id: 4, grades: [ { grade: 80, mean: 75, std: 8 }, { grade: 85, mean: 90, std: 5 }, { grade: 85, mean: 85, std: 8 } ] }
使用位置 $
运算符来更新与“grade
等于 85
条件”相匹配的第一个数组元素的 std
字段:
重要
您必须将数组字段包含在 query
文档中。
db.students.updateOne( { _id: 4, "grades.grade": 85 }, { $set: { "grades.$.std" : 6 } } )
操作完成后,文档更新后的值如下:
{ "_id" : 4, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 8 }, { "grade" : 85, "mean" : 90, "std" : 6 }, { "grade" : 85, "mean" : 85, "std" : 8 } ] }
使用多字段匹配项更新嵌入文档
$
操作符可以更新与 $elemMatch
操作符指定的多个查询条件相匹配的第一个数组元素。
以 students
集合中的以下文档为例;其中,grades
字段值为嵌入式文档数组:
{ _id: 5, grades: [ { grade: 80, mean: 75, std: 8 }, { grade: 85, mean: 90, std: 5 }, { grade: 90, mean: 85, std: 3 } ] }
在以下示例中,$
操作符更新了第一个嵌入文档中 std
字段的值,该文档中 grade
字段的值小于或等于 90
,mean
字段的值大于 80
:
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 } ] }
用多个数组匹配更新
当查询有多个数组字段用于过滤集合中的文档时,位置 $
更新操作符就会表现得不明确。
以 students_deans_list
集合中的某一文档为例,其中包含学生信息数组:
db.students_deans_list.insertMany( [ { _id: 8, activity_ids: [ 1, 2 ], grades: [ 90, 95 ], deans_list: [ 2021, 2020 ] } ] )
在以下示例中,用户尝试修改deans_list
字段,使用activity_ids
、 deans_list
和grades
字段筛选文档,并将deans_list
字段中的 2021 值更新为 2022:
db.students_deans_list.updateOne( { activity_ids: 1, grades: 95, deans_list: 2021 }, { $set: { "deans_list.$": 2022 } } )
当服务器执行上述 updateOne
方法时,它会使用所提供数组字段中的值过滤可用文档。尽管过滤器中使用了 deans_list
字段,但它不是位置 $
更新操作符用于确定待更新数组中位置的字段:
db.students_deans_list.find( { _id: 8 } )
示例输出:
{ _id: 8, activity_ids: [ 1, 2 ], grades: [ 90, 95 ], deans_list: [ 2021, 2022 ] }
updateOne
方法与 2021 上的 deans_list
字段匹配,但位置 $
更新操作符却将 2020 值改为 2022。
为避免在多个数组上匹配时出现意外结果,请使用过滤后的位置操作符 $[<identifier>]
。