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

news2024/9/23 13:19:04

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

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

  • 一、简介
  • 二、内容概述
  • 三、更新文档
    • 3.1 db.collection.update()
      • 注意点
    • 3.2 更新操作符
      • $set 更新或新增字段
      • $unset 删除字段
      • $rename 重命名字段
      • $inc 加减字段值
      • $mul 相乘字段值
      • $min 比较较小字段值
      • $max 比较较大字段值
    • 3.3 数组更新操作符
      • $addToset 添加元素
      • $pop 移除元素
      • $pull 选择性删除
      • $pullAll 选择性删除
      • $push 添加元素
    • 3.4 配置参数
      • multi 多篇更新
      • upsert 更新或创建
  • 四、小结

一、简介

本篇记录备份的是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>)
  • query: 代表更新文档时筛选文档的条件,简单的说就是要更新某个文档的前提是你得找到这个文档;
  • update: 代表更新文档时需要更新的内容;
  • option: 代表声明了一些更新操作的参数;

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

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

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

注意点

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

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

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

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

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

3.2 更新操作符

$set 更新或新增字段

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

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

具体示例如下

db.test.update(
{name: "jack"},
$set:{
	balance: 3000,
	info: {
  	dateOpened: new Date("2016-05-18T16:00:00z")
  	branch:"branch1"
  }
})

首先在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"),
})

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

db.test.update(
{name: "jack"},
$set:{
	"contact.0": 752746873,
})

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

$unset 删除字段

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

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

具体示例如下

db.test.update(
	{name: "jack"},
	$unset:{
		balance: "",
		"info.branch": ""
})

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

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

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

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

$rename 重命名字段

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

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

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

db.test.update(
	{name: "jack"},
	$rename:{
		name: "contact"
})

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

db.test.update(
	{name: "karen"},
	$rename:{
		"info.branch": "branch",
  	"balance":"info.balance"
})

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

db.test.update(
	{name: "karen"},
	$rename:{
		"contact.3.primaryEmail": "primaryEmail"
})

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

$inc 加减字段值

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

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

看个例子

db.test.update(
	{name: "karen"},
	$inc:{
		"balance": 0.5
})

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

db.test.update(
	{name: "karen"},
	$inc:{
		"balance": -0.5
})

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

$mul 相乘字段值

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

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

看个例子

db.test.update(
	{name: "karen"},
	$mul:{
		"balance": 2
})

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

db.test.update(
	{name: "karen"},
	$mul:{
		"balance": 0.5
})

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

$min 比较较小字段值

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

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

看个例子

db.test.update(
	{name: "karen"},
	$min:{
		"info.balance": 5000
})

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

db.test.update(
	{name: "karen"},
	$min:{
		"info.balance": null
})

最终,文档的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>} })

看个例子

db.test.update(
	{name: "karen"},
	$max:{
		"info.balance": 5000
})

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

db.test.update(
	{name: "karen"},
	$min:{
		"info.balance": null
})

最终,文档的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>} })

具体示例如下:

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

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

$pop 移除元素

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

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

具体示例如下:

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

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

$pull 选择性删除

基本使用方式如下

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

具体示例如下:

db.test.update(
	{name: "jack"},
	{$pull:{contact: {$regex:/hi/}}}
)

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

db.test.update(
	{name: "jack"},
	{$pull:{contact: "222222"}}
)

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

$pullAll 选择性删除

基本使用方式如下

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

具体示例如下:

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

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

$push 添加元素

基本使用方式如下

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

具体示例如下:

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

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

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

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

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

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

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

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

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

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

3.4 配置参数

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

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

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

multi 多篇更新

{multi:<boolean>}

具体示例

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

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

upsert 更新或创建

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

{upsert:<boolean>}

具体示例

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

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

四、小结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/60978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“剧情+综艺” 助推国潮文化破圈

一舞千年&#xff0c;重现大唐辉煌&#xff1b;一曲流光&#xff0c;雕琢岁月模样&#xff1b;一纸云烟&#xff0c;漫卷诗书山河&#xff1b;跨历史长河&#xff0c;览盛世华章。自从河南卫视开启“剧情综艺”的晚会形式&#xff0c;晚会便多了一种呈现方式。 从2021年《唐宫夜…

本周XR新闻:吴德周成立AR硬件公司,SideQuest支持PICO、Magic Leap

本周AR/VR大新闻&#xff0c;AR方面&#xff1a;吴德周成立AR硬件公司“致敬未知科技”&#xff1b;彭博称苹果AR操作系统或命名“xrOS”&#xff1b;AR眼镜开源方案OpenAR亮相&#xff1b;Epic 3D扫描工具RealityScan上线&#xff1b;Qoncept推出基于AI的实时姿态追踪系统。 …

Docker_实用篇_Docker-Compose_微服务部署

Docker_实用篇_Docker-Compose_微服务部署 文章目录Docker_实用篇_Docker-Compose_微服务部署4.1Docker-Compose4.2.初识DockerCompose4.3.部署微服务集群4.3.1.打包前文件汇总4.3.2.修改微服务配置4.3.3.打包4.3.4.拷贝jar包到部署目录4.3.5.部署4.1Docker-Compose Docker Co…

Vue(第十六课)JSON-SERVE和POSTMAN技术中对数据的增删改查

今天来聊聊axios技术 同样将官网地址放在博客里: 邮递员API平台|免费注册 (postman.com) json-server - npm (npmjs.com) 起步 | A jsxios 中文文档 | Axios 中文网 (axios-http.cn) 了解一下概念: 1 Axios Axios 是一个基于 promise 网络请求库&#xff0c;作用于node.js …

五、伊森商城 前端基础-Vue p24

目录 1、v-on 2、事件修饰符 3、按键修饰符 3.1、组合按钮 4、v-for 5、v-if和v-show 6、v-else 和 v-else-if 6.1、v-if结合v-for来时用 1、v-on v-on 指令用于给页面元素绑定事件。 语法&#xff1a; v-on:事件名"js 片段或函数名"事件绑定可以简写&#xff…

[附源码]计算机毕业设计基于JEE平台springboot技术的订餐系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Dcoker入门,小白也学得懂!

目录 一、Dcoker简介 二、Centos7安装Docker 三、Docker 容器运行原理解析 四、阿里云镜像仓库配置 一、Dcoker简介 1.1简单介绍 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从Apache2.0协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个…

【Java难点攻克】「海量数据计算系列」如何使用BitMap在海量数据中对相应的进行去重、查找和排序实战

BitMap&#xff08;位图&#xff09;的介绍 BitMap从字面的意思&#xff0c;很多人认为是位图&#xff0c;其实准确的来说&#xff0c;翻译成基于位的映射&#xff0c;其中数据库中有一种索引就叫做位图索引。 在具有性能优化的数据结构中&#xff0c;大家使用最多的就是hash…

LiteOS-M内核

简介 OpenHarmony LiteOS-M内核是面向IoT领域构建的轻量级物联网操作系统内核&#xff0c;具有小体积、低功耗、高性能的特点&#xff0c;其代码结构简单&#xff0c;主要包括内核最小功能集、内核抽象层、可选组件以及工程目录等&#xff0c;分为硬件相关层以及硬件无关层&…

[附源码]计算机毕业设计校园快递柜存取件系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

多线程复习——进程线程(上)

目录 一、进程是啥?跑起来的程序 二、进程是怎么管理的?描述组织 三、进程的PCB里有啥? 四、进程的调度是咋进行?时间管理大师 五、进程的独立性是咋回事? 六、进程之间如何通信? 一、进程是啥?跑起来的程序 进程(process) 也叫做 任务(task).对于操作系统来说 一个…

牛客网刷题(BC72、BC18、BC83、BC84、BC41、BC31、BC17、BC6)

目录 一、BC72 平均身高 二、BC18 计算带余除法​编辑 三、BC83 被5整除问题​编辑 四、BC84计算y的值 五、BC41 你是天才吗&#xff1f; 六、BC31 发布信息 七、BC17 计算表达式的值 八、BC6 小飞机 **太简单的题就没有文字叙述了~ 一、BC72 平均身高 #define _CRT_S…

[附源码]Python计算机毕业设计Django人体健康管理app

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

力扣(LeetCode)134. 加油站(C++)

贪心 考虑暴力&#xff0c;枚举所有点作为起点&#xff0c;每个点先加油&#xff0c;再尝试前往下一个点&#xff0c;剩余油量大于等于 000 &#xff0c;就可以走下去。枚举所有点的所有路径&#xff0c;时间复杂度 O(n2)O(n^2)O(n2) &#xff0c;对于本题规模 TLETLETLE 。 …

Redis常见错误

目录 一、(error) NOAUTH Authentication required. 二、(error) ERR unknown command keys* 三、(error) ERR invalid password 一、(error) NOAUTH Authentication required. 错误原因&#xff1a;没有使用密码登录认证 使用命令&#xff1a; auth 你自己的密码 如果密码…

【人工智能】确定型推理

确定性推理&#xff08;Deterministic Reasoning&#xff09;目录一、推理基本概念二、推理逻辑基础1. 命题逻辑&#xff08;Propositional Logic&#xff09;2. 谓词逻辑&#xff08;Predicate Logic&#xff09;3. 推理的控制策略(i) 推理方向(a) 正向推理&#xff1a;已知事…

[附源码]Python计算机毕业设计SSM教务一点通管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

制作一个简单HTML个人网页网页——人物介绍梵高(HTML+CSS)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

c++中的cin和getline()函数

cin cin 输入接触到第一个非空格字符时开始阅读&#xff0c; 当遇到下一个空格字符&#xff08;空格、制表符、换行符&#xff09;时就会停止读取&#xff0c; #include<iostream> using namespace std;int main() {string s;printf("输入:\n");cin >>…

[论文阅读] 颜色迁移-颜色空间的选择

[论文阅读] 颜色迁移-颜色空间的选择 论文: [Colour Spaces for Colour Transfer] 本文将颜色迁移方法分为了2大类: 一是直接3D颜色迁移方法二是转换为3个1D颜色迁移方法 对于第二种方法, 颜色空间的选择对颜色迁移结果有很大的影响, 如下所示为 RGB 和 Lab 颜色空间的结果…