mongodb数组更新运算符($、$[]、$[<identifier>])

系列文章:

mongodb数组更新运算符($、$[]、$[<identifier>])
Spring Data mongodb中数组更新运算符(、[]、$[<identifier>])

  • $可以在不显示指定数组元素位置的情况下标识要更新的数组中的元素,$只是单个占位符。

  • $[]要更新数组中的所有元素。

  • $[<identifier>]会使用一个或多个数组作为过滤条件进行匹配。

1. $运算符

$运算符可在不显式指定数组中元素位置的情况下标识要更新的数组中的元素。

语法:

{ "<array>.$" : value }

当我们使用更新运算符,例如:db.collection.update() 或者db.collection.findAndModify(),时

存在两个限制条件:

  • $只会更新数组中第一个匹配的值;
  • $的数组必须是查询条件的一部分;
db.collection.update(
  {<array>:value},  //查询条件中必须含有array
  {<update operator>:{"<array>.$":value}}  //只会更新第一个匹配的文档的值。
)

注意:更新操作分为两步(1)匹配(2)更换。使用$等位置描述符的目的就是在不确定内置数组需要更换元素位置的前提下,进行占位处理。

1.1 $的使用案例

1.1.1 更新内置数组的值

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

需求:将id为1的文档grades数组中80修改为90。

失败的解决方案.png

可以显示的指定具体的下标{"grades.1":82}来进行修改。

修改下标为1的数组元素.png

现在的问题是不知道数组中更换元素的具体位置。那么使用$进行占位:

并且占的位是query document中进行声明的grades:80。也就是说必须在query document包含数组字段

image.png
db.students.updateOne(
//定位到查询的文档。grades:80的含义是数组中只要存在80,便可匹配。
{"_id":1,grades:80},  
//修改操作。
{$set:{"grades.$":82}}
);
错误案例.png

1.1.2 更新内置数组的文档

语法:

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

$是数组文档的占位符。有助于更新包含嵌入式文档的数组。

db.student1.insert({
   "_id" : 4,
   "grades" : [
      { "grade" : 80, "mean" : 75, "std" : 8 },
      { "grade" : 85, "mean" : 90, "std" : 6 },
      { "grade" : 85, "mean" : 85, "std" : 8 }
   ]
   })
   db.student1.update(
        {"_id":4,"grades.grade":85},  //查询条件
        {$set:{"grades.$.std":5}},  //更新后的值
        {multi:true}  //全部更新
   )

执行结果:

/* 1 */
{
  "_id" : 4,
  "grades" : [{
      "grade" : 80,
      "mean" : 75,
      "std" : 8
    }, {
      "grade" : 85,
      "mean" : 90,
      "std" : 5
    }, {
      "grade" : 85,
      "mean" : 85,
      "std" : 8
    }]
}

$只会更新数组中第一个匹配的值。

案例三:使用多个字段个字段更新嵌入式文档

   db.student2.insert(
   {
     _id: 5,
     grades: [
     { grade: 80, mean: 75, std: 8 },
     { grade: 85, mean: 90, std: 5 },
     { grade: 90, mean: 85, std: 3 }
     ]
    })
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 }
  ]
}

2. $[]占位符

注:3.6版本后的新功能:

功能:在查询条件匹配的情况下,$[]会修改指定数组字段中的所有元素。

因为$[]是修改整个数组。所以无需在查询条件中指定数组。

语法:

db.collection.updateMany(
   { <query conditions> },
   { <update operator>: { "<array>.$[]" : value } }
)

案例

案例1:替换数组全部内容:

    db.testupdateinner2.insert([
        {name:"数组1",myArray:[5,8]},
        {name:"数组2",myArray:[8,5]},
        {name:"数组3",myArray:[5]}
    ]);
只是修改了一条结果.png

最终结果:将符合条件的数组中的内容全部进行了替换。

{
  "_id" : ObjectId("5e6d8b661ec58cc4b94b7b3c"),
  "name" : "数组1",
  "myArray" : [10, 10]
}

/* 2 */
{
  "_id" : ObjectId("5e6d8b661ec58cc4b94b7b3d"),
  "name" : "数组2",
  "myArray" : [8, 5]
}

/* 3 */
{
  "_id" : ObjectId("5e6d8b661ec58cc4b94b7b3e"),
  "name" : "数组3",
  "myArray" : [5]
}

案例二:若使用$的情况

执行删除命令,重新插入数据testupdateinner2文档

db.testupdateinner2.remove({})
使用$符会产生什么情况.png

案例三: 查询条件的值不是数组的情况

执行结果.png

案例四:显式指定更新多个文档

显式更新多个条件下,会更新多个文档.png

案例五:在查询条件中不指定数组

在查询条件中不指定数组.png

执行结果

/* 1 */
{
  "_id" : ObjectId("5e6d90101ec58cc4b94b7b48"),
  "name" : "数组1",
  "myArray" : [10, 10]
}

/* 2 */
{
  "_id" : ObjectId("5e6d90101ec58cc4b94b7b49"),
  "name" : "数组2",
  "myArray" : [8, 5]
}

/* 3 */
{
  "_id" : ObjectId("5e6d90101ec58cc4b94b7b4a"),
  "name" : "数组3",
  "myArray" : [5]
}

案例:更新数组文档

语法:

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

案例:

    db.student2.insert([
    {
     "_id" : 1,
     "grades" : [
      { "grade" : 80, "mean" : 75, "std" : 8 },
      { "grade" : 85, "mean" : 90, "std" : 6 },
      { "grade" : 85, "mean" : 85, "std" : 8 }
     ]
     },
    {
     "_id" : 2,
    "grades" : [
      { "grade" : 90, "mean" : 75, "std" : 8 },
      { "grade" : 87, "mean" : 90, "std" : 5 },
      { "grade" : 85, "mean" : 85, "std" : 6 }
     ]
    }
   ])

处理语句:

    db.student2.update(
        {"grades.grade":85},
        {$set:{"grades.$[].std":0}}
    )

执行结果:

/* 1 */
{
  "_id" : 1,
  "grades" : [{
      "grade" : 80,
      "mean" : 75,
      "std" : 0
    }, {
      "grade" : 85,
      "mean" : 90,
      "std" : 0
    }, {
      "grade" : 85,
      "mean" : 85,
      "std" : 0
    }]
}

/* 2 */
{
  "_id" : 2,
  "grades" : [{
      "grade" : 90,
      "mean" : 75,
      "std" : 8
    }, {
      "grade" : 87,
      "mean" : 90,
      "std" : 5
    }, {
      "grade" : 85,
      "mean" : 85,
      "std" : 6
    }]
}

可以看到"_id":1的内嵌数组文档全部被更新。

对比:

    db.student2.update(
        {"grades.grade":85},
        {$set:{"grades.$.std":0}},
        {multi:true}
    )

执行结果:

/* 1 */
{
  "_id" : 1,
  "grades" : [{
      "grade" : 80,
      "mean" : 75,
      "std" : 8
    }, {
      "grade" : 85,
      "mean" : 90,
      "std" : 0
    }, {
      "grade" : 85,
      "mean" : 85,
      "std" : 8
    }]
}

/* 2 */
{
  "_id" : 2,
  "grades" : [{
      "grade" : 90,
      "mean" : 75,
      "std" : 8
    }, {
      "grade" : 87,
      "mean" : 90,
      "std" : 5
    }, {
      "grade" : 85,
      "mean" : 85,
      "std" : 0
    }]
}

每个元素中的内嵌数组中第一个匹配文档进行了更新。

3. $[<identifier>]

在monodb3.6版本后才可使用。

过滤后的位置运算符$[<identifier>]标识与arrayFilters条件匹配的数组元素来进行更新操作。

语法:

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

注意:在<identifier>必须以小写字母开头,并且只包含字母数字字符。

案例:

db.student3.insert([
{ "_id" : 1, "grades" : [ 95, 92, 90 ] },
{ "_id" : 2, "grades" : [ 98, 100, 102 ] },
{ "_id" : 3, "grades" : [ 95, 110, 100 ] }
])

命令:

db.student3.update(
{},
{$set:{"grades.$[elem]":100}},
{multi:true,
    arrayFilters:[{"elem":{$gte:100}}]}
)

结果:

/* 1 */
{
  "_id" : 1,
  "grades" : [95, 92, 90]
}

/* 2 */
{
  "_id" : 2,
  "grades" : [98, 100, 100]
}

/* 3 */
{
  "_id" : 3,
  "grades" : [95, 100, 100]
}

位置$[identifier]运算符充当数组字段中arrayFilters中指定条件匹配的所有元素的占位符。

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

案例二:

db.student4.insert([
{
   "_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元素的std属性为0.

db.student4.update(
    {},
    {$set:{"grades.$[elem].std":0}},
    {
        multi:true,
        arrayFilters:[{"elem.grade":85}]
    }
);

执行结果:

/* 1 */
{
  "_id" : 1,
  "grades" : [{
      "grade" : 80,
      "mean" : 75,
      "std" : 6
    }, {
      "grade" : 85,
      "mean" : 90,
      "std" : 0
    }, {
      "grade" : 85,
      "mean" : 85,
      "std" : 0
    }]
}

/* 2 */
{
  "_id" : 2,
  "grades" : [{
      "grade" : 90,
      "mean" : 75,
      "std" : 6
    }, {
      "grade" : 87,
      "mean" : 90,
      "std" : 3
    }, {
      "grade" : 85,
      "mean" : 85,
      "std" : 0
    }]
}

案例二:嵌套数组

db.student5.insert(
   { "_id" : 1,
      "grades" : [
        { type: "quiz", questions: [ 10, 8, 5 ] },
        { type: "quiz", questions: [ 8, 9, 6 ] },
        { type: "hw", questions: [ 5, 8, 3 ] },
        { type: "exam", questions: [ 25, 10, 23, 0 ] },

      ]
   }
)

修改内嵌数组中的值$[t]代指的是grades数组的元素。$[score]代指的是questions数组元素。

db.student5.update(
{},
{$set:{"grades.$[t].questions.$[score]":0}},
{arrayFilters:[{"t.type":"quiz"},{"score":8}]}
);

修改结果:

{
  "_id" : 1,
  "grades" : [{
      "type" : "quiz",
      "questions" : [10, 0, 5]
    }, {
      "type" : "quiz",
      "questions" : [0, 9, 6]
    }, {
      "type" : "hw",
      "questions" : [5, 4, 3]
    }, {
      "type" : "exam",
      "questions" : [25, 10, 23, 0]
    }]
}

案例二:

执行代码:

db.student5.update(
{},
{$set:{"grades.$[].questions.$[score]":0}},
{arrayFilters:[{"score":8}]}
);

得到案例:

{
  "_id" : 1,
  "grades" : [{
      "type" : "quiz",
      "questions" : [10, 0, 5]
    }, {
      "type" : "quiz",
      "questions" : [0, 9, 6]
    }, {
      "type" : "hw",
      "questions" : [5, 0, 3]
    }, {
      "type" : "exam",
      "questions" : [25, 10, 23, 0]
    }]
}

推荐阅读

Mongodb官网

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352