• 《MongoDB》Mongo Shell中的基本操作-更新操作一览


    前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言~博主看到后会去代替大家踩坑的~
    主页: oliver尹的主页
    格言: 跌倒了爬起来就好~
    来个关注吧,点个赞吧,谢谢

    一、简介

    本篇记录备份的是Mongo DB的一些基础知识,包括文档长什么样子,Mongo Shell中的CRUD这四种基本操作,什么是CRUD?C(Creadt、创建)R(Read、读取)U(Update、更新)D(Delete、删除),人话就是增、删、改、查等等;
    注意,本文中的示例命令都是基于Mongo Shell的,并不是直接运行在类似于node代码中的~

    二、内容概述

    本文主要记录分享了MongoDB中的更新操作以及更新操作时的操作符
    在这里插入图片描述

    三、更新文档

    当通过insert()方法将文档在MongoDB中创建之后,肯定是需要对文档进行更新的,比如用户点击了修改密码,这些操作不是新增,是对现有数据进行修改;

    3.1 db.collection.update()

    更新文档,具体模版如下:

    db.<collection>update(<query>,<update>,<option>)
    
    • 1
    • query: 代表更新文档时筛选文档的条件,简单的说就是要更新某个文档的前提是你得找到这个文档;
    • update: 代表更新文档时需要更新的内容;
    • option: 代表声明了一些更新操作的参数;

    在不使用 更新操作符 的情况下,使用db.collection.update()将会 直接替换集合中的文档;更新操作符等会再看;
    以上方的这个模版为例,且不使用更新操作符看一个具体的示例:

    db.test.update({name:"oliver"},{name:"oliver",age:20})
    
    • 1

    简单说明:在test这个集合中找到了name值是oliver的数据,并且将这条数据 替换 成了{name:“oliver”,age:20},假如这条数据 本来还有其他的字段这些字段都将会消失,因为是替换操作

    注意点

    在更新操作中有一个需要注意的问题

    • 文档主键也就是_id是不可以更改的,在上例中其实也已经看到了,在update中并没有包含_id字段,如果一定要写_id,比如
    db.test.update({name:"oliver"},{_id:"demotest",name:"oliver",age:20})
    
    • 1

    那么 _id这个值必须和原来的这个文档的_id值保持一致,如果不一致,那么就会报错,更新失败;

    • 当update中查询到多篇文档时,只有 第一篇 符合查询条件的文档会被更新,比如
    db.test.update({balance:{$gt:20,$lt:80}},{name:"bill",balance:50,gender:"M"})
    
    • 1

    在test集合中查询所有balance大于20,小于80的文档,将其更新成name等于bill,balance等于50,gender等于M
    在这里插入图片描述
    从结果看,只有第一篇被更新了,第二篇并没有被更新,因此说明update的整篇文档更新只能作用在单一文档上,这是一个局限~

    3.2 更新操作符

    $set 更新或新增字段

    使用$set更新某个字段或者在某个文档中创建字段,基本模版如下:

    db.<collection>.update({ $set : {<field1>:<value1>,...,<fieldn>:<valuen>} })
    
    • 1

    具体示例如下

    db.test.update(
    {name: "jack"},
    $set:{
    	balance: 3000,
    	info: {
      	dateOpened: new Date("2016-05-18T16:00:00z")
      	branch:"branch1"
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    首先在update的第一个参数name:jack,意思是找到name等于jack的数据,之后对这条数据进行更新,更新的字段是balance和info,将balance的值更新为3000,将info的值更新成一个对象,这个对象里有一个branch,且其值是branch1,如果jack这条数据不存在balance或者info,那么将为这条数据添加这两个字段并且加上对应的值;
    在这里插入图片描述
    如果更新的字段是 文档中的内嵌字段,比如上例中info里面的dateOpened,info本身就是一个对象,那么更新方式应该如下:

    db.test.update(
    {name: "jack"},
    $set:{
    	"info.dateOpened": new Date("2022-09-18T17:00:00z"),
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果更新的字段是 数组内的字段,那么更新方式应该如下:

    db.test.update(
    {name: "jack"},
    $set:{
    	"contact.0": 752746873,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    contact是一个数组,.0代表的就是数组内的下标,意思就是将contact下的第0位更新成752746873,在数组内新增也是同样的,为某个新位置添加对应的值即可,值得注意的是,如果下标的位置超出了原本的长度,比如contact的长度是3,但现在我们在第5位添加了数据,那么第4位就会自动补全为null;
    在这里插入图片描述

    $unset 删除字段

    使用$unset删除文档中的某个字段,基本模版如下:

    db.<collection>.update({ $unset : {<field1>:"",...,<fieldn>:""} })
    
    • 1

    具体示例如下

    db.test.update(
    	{name: "jack"},
    	$unset:{
    		balance: "",
    		"info.branch": ""
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在test集合中找到name等于jack的文档,删掉balance这个字段,同时也删掉info这个对象下的branch这个字段;
    在这里插入图片描述

    如果,删除的字段在文档中本身就不存在,那么文档本身将不会收到任何影响~
    如果要 删除数组内的某个字段,那么命令应该这么写:

    db.test.update(
    	{name: "jack"},
    	$unset:{"contact.0": ""})
    
    • 1
    • 2
    • 3

    值得注意的是,删除内容并不会改变数组的长度,只是会将对应位置的值变成null
    在这里插入图片描述

    $rename 重命名字段

    使用$rename重命名文档中的某个字段,基本模版如下:

    db.<collection>.update({ $rename : {<field1>:<newName1>,...,<fieldn>:<newNamen>} })
    
    • 1

    首先要注意的是,如果重命名的字段在文档中并不存在,那么文档将不会有任何改变如果重命名的字段在文档中已经存在了,那么原来存在的字段会被抹去,这个要非常注意;举个例子吧

    db.test.update(
    	{name: "jack"},
    	$rename:{
    		name: "contact"
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    contact这个字段在文档中本身就已经存在了,我们知道这个字段原来是一个数组,那么如果我们通过rename将name改成contact,那么contact这个字段原来值会被抹去
    在这里插入图片描述
    所以使用时得非常注意与小心,实际上,如果我们使用rename已经存在的字段,它在内部会先做一个unset删除,在做一个set新增;
    如果重命名的字段是 内嵌文档中的字段,那么我们改已以下方式重命名:

    db.test.update(
    	{name: "karen"},
    	$rename:{
    		"info.branch": "branch",
      	"balance":"info.balance"
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    什么意思呢?简单的说就是将info.branch路径的字段重命名成branch,将balance重命名到info下的balance,实际造成的结果就有点类似于将字段移动
    在这里插入图片描述
    那么 数组中的字段重命名 呢?

    db.test.update(
    	{name: "karen"},
    	$rename:{
    		"contact.3.primaryEmail": "primaryEmail"
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    答案是 不行,MongoDB会报错,它会不允许,同理,反向放入数组也不行,也会报错
    在这里插入图片描述

    $inc 加减字段值

    使用$inc可以更新的某个 数字类型的字段值,简单的说就是用来进行数字运算,基本模版如下:

    db.<collection>.update({ $inc : {<field1>:<newName1>,...,<fieldn>:<newNamen>} })
    
    • 1

    看个例子

    db.test.update(
    	{name: "karen"},
    	$inc:{
    		"balance": 0.5
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    得到的结果就是给name等于karen的文档下的balance值 加上0.5,同理,如果给的是一个负数,如下例

    db.test.update(
    	{name: "karen"},
    	$inc:{
    		"balance": -0.5
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    得到的结果就是给name等于karen的文档下的balance值 减去0.5
    注意的是,如果操作的字段并 不存在于文档上那么字段与字段值会被作为初始数据添加到文档上,比如"balance"这个字段不存在文档上,那么该文档会被添加一个"balance": -0.5的字段;

    $mul 相乘字段值

    使用$mul可以更新的某个 数字类型的字段值,简单的说就是用来进行数字运算,基本模版如下:

    db.<collection>.update({ $mul : {<field1>:<newName1>,...,<fieldn>:<newNamen>} })
    
    • 1

    看个例子

    db.test.update(
    	{name: "karen"},
    	$mul:{
    		"balance": 2
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    得到的结果就是给name等于karen的文档下的balance值 乘以2,同理,如果给的是一个负数,如下例

    db.test.update(
    	{name: "karen"},
    	$mul:{
    		"balance": 0.5
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    得到的结果就是给name等于karen的文档下的balance值 乘以0.5
    注意的是,如果操作的字段并 不存在于文档上那么字段与字段值会被作为初始数据添加到文档上且值和$inc不同,它的值是0,比如"balance"这个字段不存在文档上,那么该文档会被添加一个"balance": 0的字段;

    $min 比较较小字段值

    使用$min可以更新的某个字段值,简单的说就是用来进行字段比较,小的那一个会被最终留下,基本模版如下:

    db.<collection>.update({ $min : {<field1>:<newName1>,...,<fieldn>:<newNamen>} })
    
    • 1

    看个例子

    db.test.update(
    	{name: "karen"},
    	$min:{
    		"info.balance": 5000
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    意思是,先找到name值等于karen的文档,然后对info.balance的值进行比较,如果原文档中的Info.balance值比较小,那么会保留原来的值,如果5000比较小,那么会将info.balance的值更新成5000;
    除了数字类型外,min操作符还可以用在其他类型上,比如时间,可以比较时间哪个小,小的那个会被留下, 另外,如果更新的字段不存在,那么$min操作符会将值初始化进文档
    如果,两者的类型不一样,比如 info.balance的值是一个数字,我们将其更新成Null,

    db.test.update(
    	{name: "karen"},
    	$min:{
    		"info.balance": null
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    最终,文档的info.balance真的会被更新成null,因为在mongodb中它其实是有一个内部的大小排序的,排序如下
    Null < Number < Symbol, String < Object < Array < BinData < ObjectId < Boolean < Date < Timestamp < Regular Expression
    由于null比Number要小,因此上面例子中的更新会将null替换掉数字;

    $max 比较较大字段值

    使用$max可以更新的某个字段值,简单的说就是用来进行字段比较,大的那一个会被最终留下,基本模版如下:

    db.<collection>.update({ $max : {<field1>:<newName1>,...,<fieldn>:<newNamen>} })
    
    • 1

    看个例子

    db.test.update(
    	{name: "karen"},
    	$max:{
    		"info.balance": 5000
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    意思是,先找到name值等于karen的文档,然后对info.balance的值进行比较,如果原文档中的Info.balance值比较小,那么会保留5000,如果5000比较小,那么会将info.balance的值将不会变动,还是保留原来的值;
    除了数字类型外,min操作符还可以用在其他类型上,比如时间,可以比较时间哪个大,大的那个会被留下,另外,如果更新的字段不存在,那么$max操作符会将值初始化进文档
    如果,两者的类型不一样,比如 info.balance的值是一个数字,我们将其更新成Null,

    db.test.update(
    	{name: "karen"},
    	$min:{
    		"info.balance": null
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    最终,文档的info.balance会按规则进行排序,大的那个将会被留下,排序如下
    Null < Number < Symbol, String < Object < Array < BinData < ObjectId < Boolean < Date < Timestamp < Regular Expression
    由于null比Number要小,因此上面例子中的更新将不会被执行,Number依然会被保留下来;

    3.3 数组更新操作符

    $addToset 添加元素

    基本使用方式如下

    db.<collection>.update({ $addToset : {<field1>:<value1>} })
    
    • 1

    具体示例如下:

    db.test.update(
    {name: "jack"},
    {$addToset:{contact: "chind"}})
    
    • 1
    • 2
    • 3

    如果添加的值在待添加的数组中已存在,则会添加失败不会被添加进去,但注意的是,如果添加的值是复杂类型,那么判断会比较复杂,需要值完全相同且顺序也完全相同才会被判定是否已存在数组中

    $pop 移除元素

    $pop用来删除数组中的元素,但值得注意的是,只能删除第一个元素或者最后一个元素,基本使用方式如下

    db.<collection>.update({ $pop : {<field1>:<1|-1>} })
    
    • 1

    具体示例如下:

    db.test.update(
    {name: "jack"},
    {$pop:{contact: 1}})
    
    • 1
    • 2
    • 3

    代表删除contact这个数组中最后一个元素,1代表最后一个,-1代表第一个

    $pull 选择性删除

    基本使用方式如下

    db.<collection>.update({ $pull : {<field1>:<value|condition>} })
    
    • 1

    具体示例如下:

    db.test.update(
    	{name: "jack"},
    	{$pull:{contact: {$regex:/hi/}}}
    )
    
    • 1
    • 2
    • 3
    • 4

    找到所有name值是jack的文档,删除contact中所有包含hi的字段,这里的包含是通过正则进行匹配的,另外pull也可以删除特定的元素,示例如下:

    db.test.update(
    	{name: "jack"},
    	{$pull:{contact: "222222"}}
    )
    
    • 1
    • 2
    • 3
    • 4

    另外,通过pull去删除一个复杂类型的数据时,比如删除的不再是一个字符串,而是一个对象,通过pull删除时只需要提供对象中的一个键值对接口删除整个数据,或者提供全量的值但顺序可以不一样,这个和pullAll不一样,pullAll需要 全部提供且顺序也一样

    $pullAll 选择性删除

    基本使用方式如下

    db.<collection>.update({ $pullAll : {<field1>:[<value1>,<value2>]} })
    
    • 1

    具体示例如下:

    db.test.update(
    	{name: "jack"},
    	{$pullAll:{contact: ['222222','333333']}}
    )
    
    • 1
    • 2
    • 3
    • 4

    找到所有name值是jack的文档,删除contact中所有值是222222或者333333的值;
    另外,通过pullAll去删除一个复杂类型的数据时,比如删除的不再是一个字符串,而是一个对象,那么参数必须是顺序也是一摸一样的才会被删掉

    $push 添加元素

    基本使用方式如下

    db.<collection>.update({ $push : {<field1>:<value1>...} })
    
    • 1

    具体示例如下:

    db.<collection>.update({ $push : {newArray:[1,2,3]} })
    
    • 1

    这样[1,2,3]这个数组将会被添加进newArray里,如果说想要将1,2,3分别添加进行newArray而不是作为一个整体添加进去则需要通过$each操作符

    db.<collection>.update({ $push : {newArray:{$each:[1,2,3]}} })
    
    • 1

    另外push比addtoset更加强大的地方在于存储位置的调整,比如

    db.<collection>.update({ $push : {newArray:{$each:[1,2,3],$position:0}} })
    
    • 1

    这个的意思就是说,1,2,3会被存储到下标为第0位的这个位置;
    还可以进行排序,通过 sort 这个操作符进行操作,值得注意的是$sort这个操作符不可以单独使用,必须和 each 操作符一起使用

    db.<collection>.update({ $push : {newArray:{$each:[1,2,3],$sort:1}} })
    
    • 1

    从小到大进行排序,同理,-1则代表从大到小排序;
    $slice 操作符代表截取

    db.<collection>.update({ $push : {newArray:{$each:[1,2,3],$slice:-8}} })
    
    • 1

    代表截取到这数的8位进行保存,对于剩下的就不再进行保存了,直接丢弃,同理,如果值是正数,那么就代表正着数的8位进行保存
    如果position,sort,slice被一起使用时,那么顺序永远是,先通过$position向指定位置进行保存,在进行排序,最后进行截取

    3.4 配置参数

    在上面我们知道,update有三个参数

    db.<collection>update(<query>,<update>,<option>)
    
    • 1

    第三个参数中,有几个比较重要的配置项,首先是multi,是否允许更新多篇文档

    multi 多篇更新

    {multi:<boolean>}
    
    • 1

    具体示例

    db.test.update({name:"oliver"},{name:"oliver",age:20},{multi:true})
    
    • 1

    那么所有的数据都将会被改写,但值得注意的是,更新多个文档的操作虽然在单一线程中执行,但是线程在执行的过程中可能被挂起,以便其他线程也有机会对数据进行操作;因此,多个文档被操作因此不能保证原子性,也就代表结果不一定是对的,比如挂起阶段,某个文档被其他线程给删了,那等到挂起的线程继续操作时,文档已经改变了,或者干脆已经没有了;

    upsert 更新或创建

    update在没有匹配到文档时是否进行创建,默认时不会进行创建的;

    {upsert:<boolean>}
    
    • 1

    具体示例

    db.test.update({name:"oliver"},{name:"oliver",age:20},{upsert:true})
    
    • 1

    简单的说,假如在test中没有匹配到name为oliver的文档,此时会在test中创建一篇{name:“oliver”,age:20}的文档

    四、小结

    本文主要记录了在常规数据库操作中文档更新的一些用法~更新的命令非常简单,都是db.collection.update(),但查询时候的操作符非常复杂,需要经常使用与回顾~

  • 相关阅读:
    Spring Boot实践 --windows环境下 K8s 部署 Docker
    5个最好的乐高设计软件
    常用排序算法总结对比
    python代码实现论文〖文献引用顺序〗修改校对
    把项目打包成Maven Archetype(单模块项目脚手架)
    【力扣算法简单五十题】07.二进制求和
    【网安】网络安全防止个人信息泄露
    Grafana系列-统一展示-11-Logs Traces无缝跳转
    Python 学习 Day35
    12月2日:thinkphp中的链式操作
  • 原文地址:https://blog.csdn.net/zy21131437/article/details/128173046