承接国内外服务器租用托管、定制开发、网站代运营、网站seo优化托管接单、网站代更新,新老站点皆可!!咨询QQ:3787320601

MongoDB中文档的更新操作示例详解

管理员 2023-06-20 11:40:13 互联网圈 10 ℃ 0 评论 15355字 收藏

前言

在MongoDB中,更新单个doc的操作是原子性的。默许情况下,如果一个update操作更新多个doc,那末对每一个doc的更新是原子性的,但是对全部update 操作而言,不是原子性的,可能存在前面的doc更新成功,而后面的doc更新失败的情况。由于更新单个doc的操作是原子性的,如果两个更新同时产生,那末一个更新操作会阻塞另外一个,doc的终究结果值是由时间靠后的更新操作决定的。

我们在前面的文章中提到过文档的基本的增删改查操作,MongoDB中提供的增删改查的语法非常丰富,不清楚的朋友们可以参考这篇文章:#

那末下面的本文我们主要来看看更新都有哪几种好玩的语法。

文档替换

假定我的集合中现在存了以下一段数据:

{
“_id” : ObjectId(“59f005402844ff254a1b68f6”),
“name” : “三国演义”,
“authorName” : “罗贯中”,
“authorGender” : “男”,
“authorAge” : 99.0
}

这是一本书,有书名和作者信息,但是作者是一个独立的实体,所以我想将之提取出来,变成下面这样:

{
“_id” : ObjectId(“59f005402844ff254a1b68f6”),
“name” : “三国演义”,
“author” : {
“name” : “罗贯中”,
“gender” : “男”,
“age” : 99.0
}
}

我可以采取以下操作:

另外一个问题是更新时,MongoDB只会匹配第一个更新的文档,假定我的MongoDB中有以下数据:

{ “_id” : ObjectId(“59f00d4a2844ff254a1b68f7”), “x” : 1 }
{ “_id” : ObjectId(“59f00d4a2844ff254a1b68f8”), “x” : 1 }
{ “_id” : ObjectId(“59f00d4a2844ff254a1b68f9”), “x” : 1 }
{ “_id” : ObjectId(“59f00d4a2844ff254a1b68fa”), “x” : 2 }

我想把所有x为1的数据改成99,我们很容易想到以下命令:

db.sang_collect.update({x:1},{x:99})

但我们发现履行结果却是这样:

{ “_id” : ObjectId(“59f00d4a2844ff254a1b68f7”), “x” : 99 }
{ “_id” : ObjectId(“59f00d4a2844ff254a1b68f8”), “x” : 1 }
{ “_id” : ObjectId(“59f00d4a2844ff254a1b68f9”), “x” : 1 }
{ “_id” : ObjectId(“59f00d4a2844ff254a1b68fa”), “x” : 2 }

即只有第一条匹配的结果被更新了,其他的都没有变化。这是MongoDB的更新规则,即只更新第一条匹配结果。如果我们想将所有x为1的更新为x为99,可以采取以下命令:

db.sang_collect.update({x:1},{$set:{x:99}},false,true)

首先我们将要修改的数据赋值给$set,$set是一个修改器,我们将在下文详细讲授,然后后面多了两个参数,第一个false表示如果不存在update记录,会不会将我们要更新的文档作为一个新文档插入,true表示插入,false表示不插入,默许为false,第二个true表示会不会更新全部查到的文档,false表示只更新第一条记录,true表示更新所有查到的文档。

使用修改器

很多时候我们修改文档,只是要修改文章的某一部份,而不是全部,但是现在我面临这样一个问题,假定我有以下一个文档:

{x:1,y:2,z:3}

我现在想把这个文档中x的值改成99,我可能使用以下操作:

db.sang_collect.update({x:1},{x:99})

但是更新结果却变成了这样:

{ “_id” : ObjectId(“59f02dce95769f660c09955b”), “x” : 99 }

以下图:

MongoDB帮我把全部文档更新了!要解决这个问题,我们可使用修改器。

$set修改器

$set可以用来修改一个字段的值,如果这个字段不存在,则创建它。以下:

如果该字段不存在,则创建,以下:

也能够利用$unset删除一个字段,以下:

$set也能够用来修改内嵌文档,还以刚才的书为例,以下:

{
“_id” : ObjectId(“59f042cfcafd355da9486008”),
“name” : “三国演义”,
“author” : {
“name” : “罗贯中”,
“gender” : “男”
}
}

想要修改作者的名字,操作以下:

db.sang_collect.update({name:”三国演义”},{$set:{“author.name”:”明朝罗贯中”}})

修改结果以下:

{
“_id” : ObjectId(“59f042cfcafd355da9486008”),
“name” : “三国演义”,
“author” : {
“name” : “明朝罗贯中”,
“gender” : “男”
}
}

$inc修改器

$inc用来增加已有键的值,如果该键不存在就新创建一个。比如我想给上文的罗贯中增加一个年龄为99,方式以下:

db.sang_collect.update({name:”三国演义”},{$inc:{“author.age”:99}})

履行结果以下:

{
“_id” : ObjectId(“59f042cfcafd355da9486008”),
“name” : “三国演义”,
“author” : {
“name” : “明朝罗贯中”,
“gender” : “男”,
“age” : 99.0
}
}

加入我想给罗贯中增加1岁,履行以下命令:

db.sang_collect.update({name:”三国演义”},{$inc:{“author.age”:1}})

这是会在现有值上加1,结果以下:

{
“_id” : ObjectId(“59f042cfcafd355da9486008”),
“name” : “三国演义”,
“author” : {
“name” : “明朝罗贯中”,
“gender” : “男”,
“age” : 100.0
}
}

注意$inc只能用来操作数字,不能用来操作null、布尔等。

数组修改器

数组修改器有好几种,我们分别来看。

$push可以向已有数组末尾追加元素,要是不存在就创建一个数组,或者以我们的上面的book为例,假定book有一个字段为comments,是一个数组,表示对这个book的评论,我们可使用以下命令添加一条评论:

db.sang_collect.update({name:”三国演义”},{$push:{comments:”好书666″}})

此时不存在comments字段,系统会自动帮我们创建该字段,结果以下:

{
“_id” : ObjectId(“59f042cfcafd355da9486008”),
“name” : “三国演义”,
“author” : {
“name” : “明朝罗贯中”,
“gender” : “男”,
“age” : 100.0
},
“comments” : [
“好书666”
]
}

此时我们可以追加评论,以下:

db.sang_collect.update({name:”三国演义”},{$push:{comments:”好书666啦啦啦啦”}})

结果以下:

{
“_id” : ObjectId(“59f042cfcafd355da9486008”),
“name” : “三国演义”,
“author” : {
“name” : “明朝罗贯中”,
“gender” : “男”,
“age” : 100.0
},
“comments” : [
“好书666”,
“好书666啦啦啦啦”
]
}

如果想一次添加3条评论,可以结合$each一起来使用,以下:

db.sang_collect.update({name:”三国演义”},{$push:{comments:{$each:[“111″,”222″,”333”]}}})

结果以下:

{
“_id” : ObjectId(“59f042cfcafd355da9486008”),
“name” : “三国演义”,
“author” : {
“name” : “明朝罗贯中”,
“gender” : “男”,
“age” : 100.0
},
“comments” : [
“好书666”,
“好书666啦啦啦啦”,
“111”,
“222”,
“333”
]
}

我们可使用$slice来固定数组的长度,假定我固定数组的长度为5,如果数组中的元素不足5个,则全部保存,如果数组中的元素超过5个,则只会保存最新的5个,以下:

db.sang_collect.update({name:”三国演义”},{$push:{comments:{$each:[“444″,”555”],$slice:⑸}}})

注意:$slice的值为负数,运行结果以下:

{
“_id” : ObjectId(“59f042cfcafd355da9486008”),
“name” : “三国演义”,
“author” : {
“name” : “明朝罗贯中”,
“gender” : “男”,
“age” : 100.0
},
“comments” : [
“111”,
“222”,
“333”,
“444”,
“555”
]
}

我们还可以在清算之前使用$sort对数据先进行排序,然后再清算比如我有一个class文档,以下:

{
“_id” : ObjectId(“59f07f3649fc5c9c2412a662”),
“class” : “三年级二班”
}

现在向这个文档中插入student,每一个student有姓名和成绩,然后依照成绩降序排列,只要前5条数据,以下:

db.sang_collect.update({class:”三年级二班”},{$push:{students:{$each:[{name:”张一百”,score:100},{name:”张九九”,score:99},{name:”张九八”,score:98}],$slice:5,$sort:{score:⑴}}}})

$sort的取值为⑴和1,⑴表示降序,1表示升序。

上面的命令履行两次以后(即插入两次),结果以下:

{
“_id” : ObjectId(“59f07f3649fc5c9c2412a662”),
“class” : “三年级二班”,
“students” : [
{
“name” : “张一百”,
“score” : 100.0
},
{
“name” : “张一百”,
“score” : 100.0
},
{
“name” : “张九九”,
“score” : 99.0
},
{
“name” : “张九九”,
“score” : 99.0
},
{
“name” : “张九八”,
“score” : 98.0
}
]
}

$slice和$sort不能只和$push一起使用,还要加上$each。

$addToSet

我们可以在插入的时候使用$addToSet,表示要插入的值如果存在则不插入,否则插入,以下:

db.sang_collect.update({name:”三国演义”},{$addToSet:{comments:”好书”}})

上面的命令履行屡次以后,发现只成功插入了一条数据。也能够将$addToSet和$each结合起来使用,以下:

db.sang_collect.update({name:”三国演义”},{$addToSet:{comments:{$each:[“111″,”222″,”333”]}}})

$pop

$pop可以用来删除数组中的数据,以下:

db.sang_collect.update({name:”三国演义”},{$pop:{comments:1}})

1表示从comments数组的末尾删除一条数据,⑴表示从comments数组的开头删除一条数据。

$pull

使用$pull我们可以按条件删除数组中的某个元素,以下:

db.sang_collect.update({name:”三国演义”},{$pull:{comments:”444″}})

表示删除数组中值为444的数据。

$

既然是数组,我们固然可以通过下标来访问,以下一行操作表示将下标为0的(第一个comments)comments修改成999:

db.sang_collect.update({name:”三国演义”},{$set:{“comments.0″:”999”}})

可是有的时候我其实不知道我要修改的数据处于数组中的甚么位置,这个时候可使用$符号来解决:

db.sang_collect.update({comments:”333″},{$set:{“comments.$”:”333⑴″}})

查询条件查出来333的下标,$符号就代码这个下标,然后通过$符号就可以将之修改。

save

save是shell中的一个函数,接收一个参数,这个参数就是文档,如果文档中有_id参数save会履行更新操作,否则履行插入操作,使用save操作我们可以方便的完成一些更新操作。

类似于以下命令则表示一个插入操作(由于没有_id):

db.sang_collect.save({x:111})

好了,MongoDB的更新操作我们就先介绍这么多,有问题欢迎留言讨论。

参考资料:

1.《MongoDB权威指南第2版》

总结

本篇文章到此结束,如果您有相关技术方面疑问可以联系我们技术人员远程解决,感谢大家支持本站!

文章来源:丸子建站

文章标题:MongoDB中文档的更新操作示例详解

https://www.wanzijz.com/view/57014.html

X

截屏,微信识别二维码

微信号:weimawl

(点击微信号复制,添加好友)

打开微信