Mongodb使用$<identifier>过滤更新数组元素

学习mongodb,体会mongodb的每一个使用细节,欢迎阅读威赞的文章。这是威赞发布的第65篇mongodb技术文章,欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题,欢迎在文章下面点个赞,或者关注威赞。谢谢。

前面两篇文章我们研究了在UPDATE中使用更新数组中第一个匹配的元素值,使用[]更新数组中所有满足条件的元素值。本文中我们继续研究UPDATE语句中的操作符,使用<identifier>更新符合查询条件的数组数组元素。本文也是UPDATE 操作三部曲中的最后一篇。操作符,为mongodb数组元素操作提供了灵活的解决方案。很值得探讨和学习。

定义

带有过滤器的位置操作符$<identifier>定义数组中数据更新时,只符合identifier定义条件的元素才可以更新。其中<identifier>作为查询条件,需要在arrayFilters中定义。语法如下

复制代码
{<update operator>: { "<array>.$<identifier>": value}},
{ arrayFilters: [{<identifier>: <condition>}]}

搭配使用arrayFiters,更新数组元素中符合过滤条件的数组。

复制代码
db.collection.updateMany(
  { <query conditions>},
  { <update operator>: { "<array>.$[<identifier>]": value}},
  { arrayFilters: [{<identifier>: <condition>}]}
)

其中identifier,是一小写字母开头的字符串,只能包含小写字母和数字

行为

  • 自mongodb5.0开始,UPDATE操作按照字段名称的字典顺序更新字段。当字段中包含数字时,按照数字顺序依次更新字段。当然,对一个文档的多个字段操作,是原子性的。

  • 在arrayFilters中,不能包含文本索引操作符expr, text, $where

  • 当使用{upsert: true}并产生insert行为时,数据查询过滤条件必须包含一个针对操作数组字段的精确查询。这样才可以在update语句中,使用\[identifier\]操作符 。按照下面的更新语句,来定义查询和使用[identifier]操作符。

    db.collection.update(
    {myArray: [5,8]},//查询过滤条件中必须包含myArray数组
    {set: {"myArray.[element]": 10}},//更新字段名称为myArray的数组中所有元素
    {upsert: true, arrayFilters: [{element: 0}]}
    )

当没有文档符合查询条件时,该操作向数据库中插入一份新的文档。

复制代码
{ "_id": ObjectId(...), "myArray": [10, 10]}

当数据更新操作中没有包含该数组的精确查询时,若数据集中不包含符合查询条件的数据记录,更新插入操作会报错。下面的两个数据更新操作会报错。

复制代码
db.emptyCollection.updateOne(
  {},
  { $set: {"myArray.$[element]": 10}},
  { upsert: true, arrayFilters: [{element: 9}]}
)

WriteError({
	"index" : 0,
	"code" : 2,
	"errmsg" : "The path 'myArray' must exist in the document in order to apply array updates.",
	"op" : {
		"q" : {
			
		},
		"u" : {
			"$set" : {
				"myArray.$[]" : 10
			}
		},
		"multi" : false,
		"upsert" : true
	}
})
  • $[]操作符可以用在多层嵌套数组当中。

应用

更新符合arrayFilters条件的数组元素

向students表插入数据。其中grades字段是数字类型的数组。

复制代码
db.students.insertMany([
    { "_id" : 1, "grades" : [ 95, 92, 90 ] },
    { "_id" : 2, "grades" : [ 98, 100, 102 ] },
    { "_id" : 3, "grades" : [ 95, 110, 100 ] }
])

构建数据更新语句,将大于100分的数据更新为100.

复制代码
db.students.updateMany(
    {},
    { $set:  {"grades.$[element]": 100}},
    { arrayFilters: [{"element": {$gte: 100}}]}
)

更新数组中符合arrayFilter条件的文档

使用\[identifier\]操作符,能够更新数组内包含的文档字段值。[identifier]操作符与点操作符一起,指定数组内文档元素字段名称,实现字段值的更改。

复制代码
db.collection.updateOne(
  {<query selector>},
  { <update operator>: {"array.$[identifier].field":value}}
  { arrayFilters: [{<identifier>: <condition>}]}
)

构建students2 集合,其中grades是文档型数组。

复制代码
db.students2.insertMany([
    {
      "_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大于等于85的mean字段.

复制代码
db.students2.updateMany(
    {},
    { $set: {"grades.$[elem].mean": -2}},
    { arrayFilters: [{"elem.grade": {$gte: 85}}]}
)

更新符合多个查询条件的数组元素

向students3集合插入数据

复制代码
db.students3.insertMany([
    {
      "_id" : 1,
      "grades" : [
         { "grade" : 80, "mean" : 75, "std" : 6 },
         { "grade" : 85, "mean" : 100, "std" : 4 },
         { "grade" : 85, "mean" : 100, "std" : 6 }
      ]
   },
   {
      "_id" : 2,
      "grades" : [
         { "grade" : 90, "mean" : 100, "std" : 6 },
         { "grade" : 87, "mean" : 100, "std" : 3 },
         { "grade" : 85, "mean" : 100, "std" : 4 }
      ]
   }
    ])

构建数据更新语句,要求将grades数组中,grade大于等于30同时std大于等于5的元素std值减少1

复制代码
db.students3.updateMany(
    {},
    {$inc: {"grades.$[elem].std": -1}},
    {arrayFilters: [{"elem.grade": {$gte: 80}, "elem.std": {$gte: 5}}]}
    )

使用反向操作符查询更新数组中的文档元素

向集合alumni插入数据

复制代码
db.alumni.insertMany([{
      "_id": 1,
      "name": "Christine Franklin",
      "degrees": [
         { "level": "Master" },
         { "level": "Bachelor" }
      ],
  },
   {
      "_id": 2,
      "name": "Reyansh Sengupta",
      "degrees": [ { "level": "Bachelor" } ],
   }
    ])

构建数据更新语句,要求将degrees数组中level=Bachelor以外的元素,添加字段gradcampaign,并将字段值设置为1.

复制代码
db.alumni.updateMany(
    {},
    {$set: {"degrees.$[degree].gradcampaign": 1}},
    { arrayFilters: [ {"degree.level": {$ne: "Bachelor"}}]}
    )

与$[]集合使用更新数组内嵌套数组元素值

向集合students4插入数据。其中grades是文档数组。文档的questions字段是数字数组类型。

复制代码
db.students4.insertOne(
   { "_id" : 1,
      "grades" : [
        { type: "quiz", questions: [ 10, 8, 5 ] },
        { type: "quiz", questions: [ 8, 9, 6 ] },
        { type: "hw", questions: [ 5, 4, 3 ] },
        { type: "exam", questions: [ 25, 10, 23, 0 ] },
      ]
   }
)

构建数据更新语句,要求更新grades数组中,type字段值为quiz的文档,将questions数组中大于等于8的元素值增加2.

复制代码
db.students4.updateMany(
    {},
    { $inc: {"grades.$[t].questions.$[score]": 2}},
    {arrayFilters: [{"t.type": "quiz"}, {"score": {$gte: 8}}]}
    )

其中,\[t\]和[score]中括号当中,不需要添加空格,否则mongodb会报错 。

复制代码
WriteError({
	"index" : 0,
	"code" : 2,
	"errmsg" : "No array filter found for identifier ' t ' in path 'grades.$[ t ].questions.$[score]'",
	"op" : {
		"q" : {
			
		},
		"u" : {
			"$inc" : {
				"grades.$[ t ].questions.$[score]" : 2
			}
		},
		"multi" : true,
		"upsert" : false,
		"arrayFilters" : [
			{
				"t.type" : "quiz"
			},
			{
				"score" : {
					"$gte" : 8
				}
			}
		]
	}
})

构建数据更新语句,要求更新grades数组中的所有文档,将questions数组中大于等于8的元素值增加2.

复制代码
{
  "acknowledged": true,
  "matchedCount": 1,
  "modifiedCount": 1
}
相关推荐
广州智造3 小时前
OptiStruct实例:3D实体转子分析
数据库·人工智能·算法·机器学习·数学建模·3d·性能优化
技术宝哥6 小时前
Redis(2):Redis + Lua为什么可以实现原子性
数据库·redis·lua
学地理的小胖砸7 小时前
【Python 操作 MySQL 数据库】
数据库·python·mysql
dddaidai1237 小时前
Redis解析
数据库·redis·缓存
数据库幼崽7 小时前
MySQL 8.0 OCP 1Z0-908 121-130题
数据库·mysql·ocp
Amctwd8 小时前
【SQL】如何在 SQL 中统计结构化字符串的特征频率
数据库·sql
betazhou8 小时前
基于Linux环境实现Oracle goldengate远程抽取MySQL同步数据到MySQL
linux·数据库·mysql·oracle·ogg
lyrhhhhhhhh9 小时前
Spring 框架 JDBC 模板技术详解
java·数据库·spring
喝醉的小喵10 小时前
【mysql】并发 Insert 的死锁问题 第二弹
数据库·后端·mysql·死锁
付出不多11 小时前
Linux——mysql主从复制与读写分离
数据库·mysql