对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

$(更新操作符)

$

使用位置$ 操作符符标识大量中要更新的元素,而无需显式指定该元素在大量中的位置。

注意

消歧

  • 如需从读取操作中投影或返回数组元素,请参阅 $ 投影操作符。

  • 如需更新数组中的所有元素,请参阅全位置操作符 $[]。

  • 如要更新与数组过滤条件匹配的所有元素,请参阅过滤位置操作符 $[<identifier>] 。

对于在以下环境中托管的部署,可以使用位置 $ 运算符:

  • MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务

位置$ 操作符的形式为:

{ "<array>.$" : value }

当您使用更新操作(例如 db.collection.updateOne() 和 db.collection.findAndModify())时,以下两个条件必须为 true:

  • 位置$ 操作符充当与query document 匹配的第一个元素的占位符。

  • array 字段必须作为 query document 的一部分出现。

例如:

db.collection.updateOne(
{ <array>: value ... },
{ <update operator>: { "<array>.$" : value } }
)

从 MongoDB 5.0 开始,更新操作符按字典顺序处理具有基于字符串的名称的文档字段。具有数字名称的字段按数字顺序处理。详情请参阅更新操作符行为。

请勿在更新或插入(upsert)操作中使用 $操作符。如果更新查询与任何现有文档都不匹配,则更新或插入(upsert)将失败,因为 $操作符需要匹配的大量元素。

$对于遍历多个大量的查询(例如遍历嵌套在其他数组中的数组的查询),不能使用位置 操作符,因为$ 占位符的替换项是单个值。

将位置$ 操作符与$unset 操作符一起使用时,位置操作操作符不会从大量中删除匹配元素。相反,它会将元素设置为null 。

如果查询使用否定操作符(例如 $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 ] }
] )

要将 大量中第一个值为80 82的元素更新为grades $,如果不知道该元素在大量中的位置,请使用位置 操作符:

重要

您必须将数组字段包含在 query 文档中。

db.students.updateOne(
{ _id: 1, grades: 80 },
{ $set: { "grades.$" : 82 } }
)

位置$ 操作符充当更新查询文档的第一个匹配项的占位符。

操作完成后,students 集合包含以下文档:

db.students.insertMany ( [
{ _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 元素值是一个嵌入文档的数组:

db.students.insertOne( [
{
_id: 4,
grades: [
{ grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 5 },
{ grade: 85, mean: 85, std: 8 }
]
}
] )

使用位置$ 操作符符更新与std grade等于 条件匹配的第一个大量元素的85 字段:

重要

您必须将数组字段包含在 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 字段值为嵌入式文档数组:

db.students.insertOne( [
{
_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方法与deans_list 上的2021 字段匹配,但位置$ 更新操作符却将2020 值更改为2022 。

为避免在多个数组上匹配时出现意外结果,请使用过滤后的位置操作符$[<identifier>]。

有关使用$ 操作符更新数组的示例,请参阅使用MQL位置操作符更新数组元素。