Mongodb数组元素更新之使用$定位数组第一个元素

学习mongodb,体会mongodb的每一个使用细节,欢迎阅读威赞的文章。这是威赞发布的第63篇mongodb技术文章,欢迎浏览本专栏威赞发布的其他文章。

阅读了不少Mongodb的文章,也和同事交流过。Mongodb数组更新是比较难理解的地方,而且使用到的频率也相对较低。但Mongodb官网对这部分知识介绍的很详细,深入阅读和实践后对工作和学习也有很大的启发。所以整理出来,共大家研究参考。也希望可以和您一起探讨Mongodb的知识。

定义

位置操作符$, 指定了数组中需要更新的元素。使用$符号,避免了显示指定数组中需要更新元素的位置。

语法

位置操作符$,要按照下面的形式使用

{"<array>.$": value}

其中,位置操作符$符号,表示符合查询结果的第一个元素。而需要更新的数组字段名称,必须存在于查询过滤条件当中。

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

行为

  • 自mongodb5.0开始,UPDATE操作按照字段名称的字典顺序更新字段。当字段中包含数字时,按照数字顺序依次更新字段。当然,对一个文档的多个字段操作,是原子性的。
  • 使用upsert时,不可以使用位置操作符$。插入数据时,$符号会被看做字段名称。
  • 位置$操作符不能用于遍历多个数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$占位符的替换是单个值
  • 在$unset操作中,位置操作符更新数据时,不会删除数组元素,而是将该数组元素设置为null。
  • 位置操作符不能用在反向操作中,如$ne, $not, $nin. 但在$elemMatch操作中使用反向过滤条件时,用户可以使用位置操作符更新字段。
  • 在对多个数组字段进行过滤时,位置$ update操作符的行为是有歧义的。当服务器执行update方法时,它首先运行一个查询来确定要更新哪些文档。如果update过滤了多个数组字段,那么后续对位置$ update操作符的调用并不总是更新数组中所需的位置。

应用

更新数组中的元素

创建集合students并插入数据。其中字段grades是包含三个元素的数组。

db.students.insertMany([
   { "_id" : 1, "grades" : [ 85, 80, 80 ] },
   { "_id" : 2, "grades" : [ 88, 90, 92 ] },
   { "_id" : 3, "grades" : [ 85, 100, 90 ] }
])

将grades数组中第一个为80的元素值更新到82. 若用户不清楚80在数组中的位置是,可以使用位置操作符$。 但要注意,查询条件中必须包含要更新的数组。

db.students.updateOne(
    { _id: 1, grades:  80}, //查询_id是1,数组中包含80的文档
    { $set: {"grades.$": 82}} //将查询到的文档中,第一个为80的数组元素更新为80
    )

在查询语序中,位置操作符$符号,找到了第一个符合查询条件的数组元素。

更新数组中的文档

在UPDATE中使用位置操作符$,也可以更新包含文档类型的数组。使用$操作符更新文档数组时,需要使用点操作符。

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

在students集合中添加下面的数据文档

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

构造数据更新语句,更新符合grade等于85的第一个数组元素,将std值更新为6

db.students.updateOne(
    {_id: 4, "grades.grade": 85}, //此处必须包含grades数组作为过滤条件
    { $set: {"grades.$.std": 6}}
    )

使用符合查询条件更新内嵌数组文档

使用位置操作符$,可以更新符合复合查询条件数组中的第一个元素。

向students集合中,插入数据。

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

在下面的更新语句中,使用$elemMatch构建了一个符合查询条件,过滤数组grades中的文档。

db.students.updateOne({
    _id: 5,
    grades: { $elemMatch: {grade: {$lte:90}, mean: {$gt:80}}}
}, {$set: {"grades.$.std": 6}})

过滤grades小于等于80, mean大于80的数据,即数组中第二个和第三个元素。使用$更新第一个找到的元素。只是将std:5更新到了6.

多数组匹配更新

在对多个数组字段进行过滤时,位置$ update操作符的行为是有歧义的。

构建集合students_deans_list. 其中包括三个数组字段, activity_ids, grades, 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字段进行过滤。将数值2021更新成2022.

db.students_deans_list.updateOne(
    { activity_ids: 1, grades:  95, deans_list:2021},
    { $set: {"deans_list.$": 2022}}
    )

当执行更新语句时,尽管使用过滤条件,查询出了_id为8的数据 ,但位置操作符$更新了数值2020的数据。

{
	"_id" : 8,
	"activity_ids" : [ 1, 2 ],
	"grades" : [ 90, 95 ],
	"deans_list" : [ 2021, 2022 ]
}

为了避免这个问题,Mongodb给出了位置操作符的另外一种使用方法。$[<identifier>].我们下一篇文章就详细描述这个方法。

最近更新

  1. TCP协议是安全的吗?

    2024-06-08 20:36:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-08 20:36:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-08 20:36:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-08 20:36:03       20 阅读

热门阅读

  1. C++linux下使用clog和重定向实现写日志

    2024-06-08 20:36:03       10 阅读
  2. 使用安装包安装飞桨寒武纪版本@启智(未通过)

    2024-06-08 20:36:03       14 阅读
  3. 《青少年编程与数学》课程方案:4、课程策略

    2024-06-08 20:36:03       9 阅读
  4. 速盾:DDoS高防IP上设置转发规则

    2024-06-08 20:36:03       7 阅读
  5. 在Pycharm中的命令行窗口中实现清屏的命令

    2024-06-08 20:36:03       13 阅读
  6. reset database to incarnation rman 恢复最早的全备方法

    2024-06-08 20:36:03       8 阅读
  7. C++的内存管理

    2024-06-08 20:36:03       12 阅读
  8. Vue进阶(八十八)前端测试工具介绍

    2024-06-08 20:36:03       10 阅读
  9. 一个可以自动生成随机区组试验的excel VBA小程序2

    2024-06-08 20:36:03       10 阅读
  10. 使用 Python 的 Tkinter 来创建 GUI 应用程序

    2024-06-08 20:36:03       11 阅读