MongoDB文档更新操作
我们在前面的文章中提到过文档的基本的增删改查操作,MongoDB中提供的增删改查的语法非常丰富,本文我们主要来看看更新都有哪些好玩的语法。
本文是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是一个修改器,我们将在下文详细讲解,然后后面多了两个参数,第一个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:-5}}})
注意$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:-1}}}})
$sort的取值为-1和1,-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
}
]
}
sort不能只和each。
$addToSet
我们可以在插入的时候使用$addToSet,表示要插入的值如果存在则不插入,否则插入,如下:
db.sang_collect.update({name:"三国演义"},{$addToSet:{comments:"好书"}})
上面的命令执行多次之后,发现只成功插入了一条数据。也可以将each结合起来使用,如下:
db.sang_collect.update({name:"三国演义"},{$addToSet:{comments:{$each:["111","222","333"]}}})
$pop
$pop可以用来删除数组中的数据,如下:
db.sang_collect.update({name:"三国演义"},{$pop:{comments:1}})
1表示从comments数组的末尾删除一条数据,-1表示从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-1"}})
查询条件查出来333的下标,符号就能将之修改。
save
save是shell中的一个函数,接收一个参数,这个参数就是文档,如果文档中有_id
参数save会执行更新操作,否则执行插入操作,使用save操作我们可以方便的完成一些更新操作。
类似于如下命令则表示一个插入操作(因为没有_id
):
db.sang_collect.save({x:111})
好了,MongoDB的更新操作我们就先介绍这么多,有问题欢迎留言讨论。
参考资料:
1.《MongoDB权威指南第2版》
- Spring思维导图,让Spring不再难懂(aop篇)
- MongoDB初探第二篇 (r4笔记第82天)
- Spring思维导图,让Spring不再难懂(cache篇)
- 曲折的10g,11g中EM的安装配置过程(r4笔记第98天)
- Linux 学习记录 一(安装、基本文件操作).
- 实用的位运算应用(r4笔记第97天)
- 关于date格式的两个案例(r4笔记第96天)
- 【新技术分享】C++17 最新进展
- Ant + Jenkies +Tomcat 自动构建部署Web项目
- 博客项目Tale思维导图,让项目不再难懂
- 关于权限管理的实用脚本(r4笔记第94天)
- Ant学习笔记
- 清理session的小插曲(r4笔记第95天)
- CVE-2017-16943 Exim UAF漏洞分析
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android编程之光线传感器用法详解
- Android Studio 3.0 新功能全面解析和旧项目适配问题
- Android开发中使用外部应用获取SD卡状态的方法
- Android编程使用光线传感器获取光线强弱的方法【LightSensorManager封装类】
- Android开发中的重力传感器用法实例详解
- 腾讯云TKE-Ingress案例: Nginx-Ingress 实现grpc转发
- 机器人软件中间层 yarp-Yet Another Robot Platforms
- 3分钟短文:Laravel写个命令行,你就是下一个Geek!
- Android开发多年每天Crud不清楚自己的技术?来刷刷大厂的高端技术面试题就知道了
- MySQL案例:count(*)效率优化
- MUI进行APP混合开发实现下拉刷新和上拉加载 原创
- Android 给控件添加边框阴影效果
- 详解Android Selinux 权限及问题
- Android图片采样缩放功能实例代码
- Android开发中使用Intent打开第三方应用及验证可用性的方法详解