文档菜单
文档首页
/
MongoDB 手册
/ / / /

$ (更新)

本页内容

  • 定义
  • 兼容性
  • 语法
  • 行为
  • 示例
$

位置运算符$运算符用于在数组中标识一个元素以进行更新,而不必显式指定该元素在数组中的位置。

注意

区分

  • 要投影或返回读取操作中的数组元素,请参阅$投影运算符。

  • 要更新数组中的所有元素,请参阅所有位置运算符 $[]

  • 要更新与数组过滤器条件或条件匹配的所有元素,请参阅过滤位置运算符 $[<identifier>]

您可以使用位置$运算符以下环境中托管的部署

位置 $ 运算符的形式为

{ "<array>.$" : value }

当与更新操作一起使用时,例如 db.collection.updateOne()db.collection.findAndModify(),

  • 位置 $ 运算符作为与 查询文档 匹配的 第一个 元素的一个占位符,并且

  • 数组 字段 必须 作为 查询文档 的一部分出现。

例如

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

从MongoDB 5.0开始,更新运算符按照基于字符串的名称的字典顺序处理文档字段。带有数字名称的字段按数字顺序处理。有关详细信息,请参阅 更新运算符行为

不要与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 的元素到 82grades 数组中,如果您不知道元素的位置,请使用位置 $ 操作符

重要

您必须将数组字段作为查询文档的一部分包含在内。

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 字段

重要

您必须将数组字段作为查询文档的一部分包含在内。

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 }
]
}

在下面的示例中,位置运算符 $ 更新第一个嵌套文档中 grade 字段的值小于或等于 90mean 字段的值大于 80std 字段的值

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_idsdeans_listgrades 字段筛选文档,并将 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>]

提示

另请参阅

返回

数组