博客已经搬家https://www.tianmingxing.com
MongoDB是一个使用C++编写的、开源的、面向文档的NoSQL(Not Only SQL)数据库,也是当前最热门的NoSql数据库之一。
NoSQL简介
NoSQL的意思是“不仅仅是SQL”,是目前流行的“非关系型数据库”的统称。常见的NoSQL数据库如:Redis、CouchDB、MongoDB、HBase、Cassandra等。
背景
出现NoSQL的原因:为解决在Web2.0时代出现的三高要求:
- 对数据库高并发读写的需求
- 对海量数据的高效率存储和访问的需求
- 对数据库的高可扩展性和高可用性的需求
- 而RDB里面的一些特性,在web2.0里面往往变得不那么重要,比如:
- 数据库事务一致性
- 数据库的实时读写
- 复杂的SQL查询,特别是多表关联查询
从第4点可以看出MongoDB的使用场景,但具体的我在后面重点介绍。
CAP定理
又被称作布鲁尔定理(Eric Brewer)它指出对于一个分布式计算系统来说,不可能同时满足以下三点:
- 强一致性(Consistency):系统在执行某项操作后数据状态仍然处于一致,例如在分布式系统中,更新操作执行成功后所有的用户都应该读取到最新的值,这样的系统被认为具有强一致性。
- 可用性(Availability):每一个操作总是能够在一定的时间内返回结果
- 分区容错性(Partition tolerance):单个节点故障不应导致整个系统崩溃,也就是说尽管网络在节点之间丢弃(或延迟)任意数量的消息,但是系统继续操作。
根据CAP原理将数据库分成了满足CA原则、满足CP原则和满足AP原则三大类
- CA:单点集群,满足一致性,可用性,通常在可扩展性上不太强大,比如RDB
- CP:满足一致性和分区容错性,通常性能不是特别高,如分布式数据库
- AP:满足可用性和分区容错性,通常可能对一致性要求低一些,如大多数的NoSQL
BASE(Basically Available,Soft-state,Eventual consistency)
eBay的架构师Dan Pritchett源于对大规模分布式系统的实践总结,在ACM上发表文章提出BASE理论,BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)。
- 基本可用(Basically Available):系统能够基本运行并一直提供服务
- 软状态(Soft-state):系统不要求一直保持强一致状态
- 最终一致性(Eventual consistency):系统需要在某一时刻后达到一致性要求
NoSQL的特点
优点
- 扩展简单方便,尤其是水平横向扩展(纵向扩展是指用更强的机器;横向扩展是指把数据分散到多个机器)
- 读写快速高效,多数都会映射到内存操作
- 成本低廉,用普通机器,分布式集群即可
- 数据模型灵活,没有固定的数据模型
缺点
- 不提供对SQL的支持
- 对事务操作的支持较弱
MongoDB使用场景
相信有很多人了解之后都会觉得,nosql好是好但我该在哪些情况下使用,又如何与实际项目结合呢?接下来我举几个例子,大家自己体会体会。
- 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
- 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
- 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
- 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
- 视频直播,使用 MongoDB 存储用户信息、礼物信息等
并没有某个业务场景必须要使用 MongoDB才能解决,但使用 MongoDB 通常能让你以更低的成本解决问题(包括学习、开发、运维等成本),下面是 MongoDB 的主要特性,大家可以对照自己的业务需求看看,匹配的越多,用 MongoDB 就越合适。
MongoDB 特性 | 优势 |
---|---|
事务支持 | MongoDB 目前只支持单文档事务,需要复杂事务支持的场景暂时不适合 |
灵活的文档模型 | JSON 格式存储最接近真实对象模型,对开发者友好,方便快速开发迭代 |
高可用复制集 | 满足数据高可靠、服务高可用的需求,运维简单,故障自动切换 |
可扩展分片集群 | 海量数据存储,服务能力水平扩展 |
高性能 | mmapv1、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支持满足各种场景需求 |
强大的索引支持 | 地理位置索引可用于构建 各种 O2O 应用、文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求 |
Gridfs | 解决文件存储的需求 |
aggregation & mapreduce | 解决数据分析场景需求,用户可以自己写查询语句或脚本,将请求都分发到 MongoDB 上完成 |
安装
mongodb的安装方式比较简单,下面我演示在CentOS7上用源码和yum两种方式安装。
使用yum方式安装
在实际生产环境中服务器上的软件通常都由运维人员安装,我们通过此种傻瓜式安装是为了方便现在学习。
-
整个mongodb(社区版)包含如下软件
软件名称 描述 mongodb-org-server 包含mongod守护程序和关联的配置和init脚本 mongodb-org-mongos 包含mongos守护程序 mongodb-org-shell 包含mongo shell,它是一个连接mongodb的命令行客户端,允许用户直接输入nosql语法管理数据库。 mongodb-org-tools 包含以下工具的MongoDB:数据导入、导出、备份、恢复等等 创建yum源文件
vim /etc/yum.repos.d/mongodb-org-3.4.repo
- 把下面的内容复制到上面的文件中
[mongodb-org-3.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc
- 启动yum命令开始安装
yum install -y mongodb-org
- 如果使用SELinux,则必须配置SELinux,以允许在基于Red Hat Linux的系统(Red Hat Enterprise Linux或CentOS Linux)上启动MongoDB。
vim /etc/selinux/config
#在打开的文件中将值设置为disabled
SELINUX=disabled
- 启动mongodb
#centos6用这种方式
service mongod start
#centos7用这种方式
systemctl start mongod
检查进程是否成功启动
可以通过查看mongodb的日志文件来判断,打开/var/log/mongodb/mongod.log
查看里面有无内容。我们可以将mongodb服务设置为开机就启动chkconfig mongod on
。其它控制命令
#停止mongodb服务
service mongod stop
#重启mongodb
service mongod restart
命令行客户端连接到MongoDB服务
./bin/mongo
#上面的命令输入后直接回车,你应该可以看到和我相同的提示
root@bogon mongodb-linux-x86_64-rhel70-3.4.1]# ./bin/mongo
MongoDB shell version v3.4.1
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.1
Welcome to the MongoDB shell.
#我们可以输入一个命令测试一下,现在教你一个最简单的命令,查看当前数据库有哪些
> show dbs
admin 0.000GB
local 0.000GB
#可以看到有两个默认的数据库,如果要退出客户端可以输入
> exit
bye
到这里第一种方式的安装就结束了
使用源码安装
运维人员都比较喜欢源码安装,他们觉得可控性会比较强,但现在公司项目中都使用云服务,所以作为开发者直接连接上就可以用了。我这里使用的源码包实际上是二进制包,也就是说已经被官方编译过了,所以下载二进制包的时候一定要确定你用的操作系统,否则包是不可用的。
- 下载二进制安装包并解压
cd /usr/local/src
wget -c https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-3.4.1.tgz
gunzip mongodb-linux-x86_64-rhel70-3.4.1.tgz
tar -xvf mongodb-linux-x86_64-rhel70-3.4.1.tar
#你现在应该可以看到和我相同的文件目录
.
├── bin
│ ├── bsondump
│ ├── mongo
│ ├── mongod
│ ├── mongodump
│ ├── mongoexport
│ ├── mongofiles
│ ├── mongoimport
│ ├── mongooplog
│ ├── mongoperf
│ ├── mongoreplay
│ ├── mongorestore
│ ├── mongos
│ ├── mongostat
│ └── mongotop
├── GNU-AGPL-3.0
├── MPL-2
├── README
└── THIRD-PARTY-NOTICES
- 创建数据存储目录,默认是在
/data/db
,如果这个目录不存在则启动时报错。你可以*创建一个目录,在启动时指定即可。
mkdir -p data/db1
#开始启动服务
./bin/mongod --dbpath=data/db1
#你应该可以看到和我相同的结果
017-01-08T13:54:55.378+0800 I CONTROL [initandlisten]
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten]
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten]
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten]
2017-01-08T13:54:55.378+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-01-08T13:54:55.379+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2017-01-08T13:54:55.379+0800 I CONTROL [initandlisten]
2017-01-08T13:54:55.446+0800 I FTDC [initandlisten] Initializing full-time diagnostic data capture with directory 'data/db1/diagnostic.data'
2017-01-08T13:54:55.447+0800 I NETWORK [thread1] waiting for connections on port 27017
- 细心的你应该发现一个问题,当前服务是前台启动的,如果你想做后续操作那必须再开启一个终端。这样肯定麻烦,那我们也可以采用后台启动的,在启动时必须指定一个日志文件。
mkdir logs
touch logs/db1.log
./bin/mongod --dbpath=data/db1 --fork --logpath=logs/db1.log
到这里源码安装并启动mongodb就结束了,下面对启动时的可配置参数作一个介绍。
- 基本配置
--quiet # 安静输出
--port arg # 指定服务端口号,默认端口27017
--bind_ip arg # 绑定服务IP,若绑定127.0.0.1,则只能本机访问,不指定默认本地所有IP
--logpath arg # 指定MongoDB日志文件,注意是指定文件不是目录
--logappend # 使用追加的方式写日志
--pidfilepath arg # PID File 的完整路径,如果没有设置,则没有PID文件
--keyFile arg # 集群的私钥的完整路径,只对于Replica Set 架构有效
--unixSocketPrefix arg # UNIX域套接字替代目录,(默认为 /tmp)
--fork # 以守护进程的方式运行MongoDB,创建服务器进程
--auth # 启用验证
--cpu # 定期显示CPU的CPU利用率和iowait
--dbpath arg # 指定数据库路径
--diaglog arg # diaglog选项 0=off 1=W 2=R 3=both 7=W+some reads
--directoryperdb # 设置每个数据库将被保存在一个单独的目录
--journal # 启用日志选项,MongoDB的数据操作将会写入到journal文件夹的文件里
--journalOptions arg # 启用日志诊断选项
--ipv6 # 启用IPv6选项
--jsonp # 允许JSONP形式通过HTTP访问(有安全影响)
--maxConns arg # 最大同时连接数 默认2000
--noauth # 不启用验证
--nohttpinterface # 关闭http接口,默认关闭27018端口访问
--noprealloc # 禁用数据文件预分配(往往影响性能)
--noscripting # 禁用脚本引擎
--notablescan # 不允许表扫描
--nounixsocket # 禁用Unix套接字监听
--nssize arg (=16) # 设置信数据库.ns文件大小(MB)
--objcheck # 在收到客户数据,检查的有效性,
--profile arg # 档案参数 0=off 1=slow, 2=all
--quota # 限制每个数据库的文件数,设置默认为8
--quotaFiles arg # number of files allower per db, requires --quota
--rest # 开启简单的rest API
--repair # 修复所有数据库run repair on all dbs
--repairpath arg # 修复库生成的文件的目录,默认为目录名称dbpath
--slowms arg (=100) # value of slow for profile and console log
--smallfiles # 使用较小的默认文件
--syncdelay arg (=60) # 数据写入磁盘的时间秒数(0=never,不推荐)
--sysinfo # 打印一些诊断系统信息
--upgrade # 如果需要升级数据库
- Replicaton 参数
--fastsync # 从一个dbpath里启用从库复制服务,该dbpath的数据库是主库的快照,可用于快速启用同步
--autoresync # 如果从库与主库同步数据差得多,自动重新同步,
--oplogSize arg # 设置oplog的大小(MB)
- 主、从参数
--master # 主库模式
--slave # 从库模式
--source arg # 从库 端口号
--only arg # 指定单一的数据库复制
--slavedelay arg # 设置从库同步主库的延迟时间
- 副本集
--replSet arg # 设置副本集名称
- 分片
--configsvr # 声明这是一个集群的config服务,默认端口27019,默认目录
/data/configdb
--shardsvr # 声明这是一个集群的分片,默认端口27018
--noMoveParanoia # 关闭偏执为moveChunk数据保存
卸载
如果你想卸载上面安装mongodb,那么可以参考下面的步骤。当然这个需求在实际部署时不会有,但是作为我们练习也是有用的。
要从系统中完全删除MongoDB,你必须删除MongoDB应用程序本身,配置文件和任何包含数据和日志的目录。此过程不可逆,因此请确保在继续之前备份所有配置和数据。
yum erase $(rpm -qa | grep mongodb-org)
rm -r /var/log/mongodb
rm -r /var/lib/mongo
如果是采用源码(二进制)包安装,删除解压出来的目录即可(因为我们上面把数据和日志都放进去了),如果你指定在其它位置,那找到相应位置删除即可。
MongoDB基本使用
基本概念
-
数据库
MongoDB的一个实例可以拥有一个或多个相互独立的数据库,每个数据库都有自己的集合(表)。 -
集合
集合可以看作是拥有动态模式的表 -
方档
文档是MongoDB中基本的数据单元,类似于RDB的行。文档是键值对的一个有序集合。在JS中,文档被表示成对象。 -
_id
每个文档都有个特殊的_id
,在文档所属集合中是唯一的,默认由mongodb自己维护,当然你也可以自己指定。 -
JavaScript shell
MongoDB自带了一个功能强大的JavaScript Shell,可以用于管理或操作MongoDB -
MongoDB和RDB的概念对比
- 都有数据库的概念
- 集合 –>RDB的表
- 文档 –>RDB表中的一条记录
- 文档对象里面的 key –> RDB表中的字段
- 文档对象里面的 value–> RDB表中字段的值
- MongoDB中没有外键的概念
数据库名称定义规则
1:不能是空串
2:不得含有/、\、?、$、空格、空字符等等,基本只能使用ASCII中的字母和数字
3:区分大小写,建议全部小写
4:最多为64字节
5:不得使用保留的数据库名,比如:admin,local,config
注意:数据库最终会成为文件,数据库名就是文件的名称
集合名称定义规则
1:不能是空串
2:不能包含\0字符(空字符),这个字符表示集合名的结束,也不能包含”$”
3:不能以”system.”开头,这是为系统集合保留的前缀
文档的键的定义规则
1:不能包含\0字符(空字符),这个字符表示键的结束
2:“.”和“$”是被保留的,只能在特定环境下用
3:区分类型,同时也区分大小写
4:键不能重复
注意:文档的键值对是有顺序的,相同的键值对如果有不同顺序的话,也是不同的文档
MongoDB基本的数据类型
数据类型 | 描述 | 举例 |
---|---|---|
null | 表示空值或者未定义的对象 | {“x”:null} |
布尔值 | 真或者假:true或者false | {“x”:true} |
32位整数 | shell不支持该类型,默认会转换成64位浮点数,也可以使用NumberInt类 | {“x”:NumberInt(“3”)} |
64位整数 | shell不支持该类型,默认会转换成64位浮点数,也可以使用NumberLong类 | {“x”:NumberLong(“3”)} |
64位浮点数 | shell中的数字就是这一种类型 | {“x”:3.14,”y”:3} |
字符串 | UTF-8字符串 | {“foo”:”bar”} |
符号 | shell不支持,shell会将数据库中的符号类型的数据自动转换成字符串 | |
对象id | 文档的12字节的唯一id | {“id”: ObjectId()} |
日期 | 从标准纪元开始的毫秒数 | {“date”:new Date()} |
正则表达式 | 文档中可以包含正则表达式,遵循JavaScript的语法 | {“foo”:/foobar/i} |
代码 | 文档中可以包含JavaScript代码 | {“x”:function() {}} |
未定义 | undefined | {“x”:undefined} |
数组 | 值的集合或者列表 | {“arr”: [“a”,”b”]} |
内嵌文档 | 文档可以作为文档中某个key的value | {“x”:{“foo”:”bar”}} |
增删改(CUD)操作
接下来我在命令行客户端中操作数据,这里假设你已经进入命令行。如果你没有与我保持同步,请再看一看上面的教程。
# 运行shell,命令:mongo ip:port,在这里我使用的默认的端口所以就没有指定
[root@bogon mongodb-linux-x86_64-rhel70-3.4.1]# ./bin/mongo
# 显示现有的数据库,命令:show dbs;
> show dbs
admin 0.000GB
local 0.000GB
# 显示当前使用的数据库,命令:db
> db
test
# 切换当前使用的数据库,命令:use 数据库名称
> use local
switched to db local
# 创建数据库:MongoDB没有专门创建数据库的语句,可以使用“use” 来使用某个数据库,如果要使用的数据库不存在,那么将会创建一个,会在真正向该库加入文档后,保存成为文件。
> use mall
switched to db mall
# 删除数据库,命令:db.dropDatabase(),在使用这个命令前要记得切换到将被删除的数据,可以用上面学到的命令确认一下。
> db
mall
> db.dropDatabase()
{ "dropped" : "mall", "ok" : 1 }
# 显示现有的集合,命令:show collections,我们切换到一个新的数据库,到里面插入一个集合并保存一条数据,这样做只是为了造些语句便于演示这个命令。
> use mall
switched to db mall
> db.user.insert({name:'张三'})
WriteResult({ "nInserted" : 1 })
> show collections
user
# 创建集合:在MongoDB中不用创建集合,因为没有固定的结构,直接使用db.集合名称.命令 来操作就可以了。如果非要显示创建集合的话,用:db.createCollecion('集合名称');
> db.createCollection('system')
{ "ok" : 1 }
# 插入并保存文档
# insert方法,可以单独插入一个文档,也可以插入多个,用“[ ]”即可。注意:
# 1:MongoDB会为每个没有“_id”字段的文档自动添加一个”_id”字段
# 2:每个Doc必须小于16MB
# 上面已经有演示,这里就不再赘述
# 删除文档,命令:remove , 可以按条件来删除只是删除文档,集合还在,如果使用 drop命令,会连带集合和索引都删掉
# 查看集合中所有的文档,命令:db.集合名称.find();
> db.user.find()
{ "_id" : ObjectId("5871e5f99423674edcea4eec"), "name" : "张三" }
> db.user.remove({})
WriteResult({ "nRemoved" : 1 })
> db.user.find()
>
# 查看集合的状态信息:db.集合名.stats();
> db.user.stats()
{
"ns" : "mall.user",
"size" : 0,
"count" : 0,
"storageSize" : 20480,
"capped" : false,
"wiredTiger" : {...},
...
}
# 查看集合中第一个文档,命令:db.集合名称.findOne({条件对象}),造一些数据方便测试
> db.user.insert([{name:'李四'},{name:'王五'}])
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
> db.user.findOne()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
# 文档替换,命令: db.集合名称. update(条件,新的文档);
> db.user.update({name:'王五'}, {name:'王五',sex:'男'})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
# 上面的操作说明:先找到名称是王五的这一条记录,然后用新文档替换数据原来的所有值,是一个整体替换的操作。
# save方法
# 如果文档存在就更新,不存在就新建,主要根据”_id”来判断。可以看到我们插入了一个名称相同的文档,结果是成功的。
> db.user.save({name:'李四'})
WriteResult({ "nInserted" : 1 })
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }
# upsert
# 找到了符合条件的文档就更新,否则会以这个条件和更新文档来创建一个新文档。指定update方法的第三个参数为true,可表示是upsert
> db.user.update({name:"张三"}, {name:"张三",sex:'女'})
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }
# 上面的修改没有成功,原因就在于根据条件查询不到张三这个文档,那下面我们给他换上第三个参数。我们希望找不到就增加,找到就修改。
> db.user.update({name:"张三"}, {name:"张三",sex:'女'}, true)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("5871ecba7f264dfcaeec2489")
})
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "女" }
#大家请看上面的操作,从查询出来的结果中我们看到,要修改的记录没有找到就变成新增操作了。
# 查询更新了多少个文档
# 使用命令:getLastError ,返回最后一次操作的相关信息,里面的n就是更新的文档的数量。
> db.runCommand({"getLastError":1})
{
"connectionId" : 3,
"n" : 0,
"syncMillis" : 0,
"writtenTo" : null,
"err" : null,
"ok" : 1
}
更新修改器,用来做复杂的更新操作
细心的人可能已经发现,上面介绍的修改操作是整体替换的,如果想局部修改怎么办呢?这节就告诉你具体的方法。
# 我准备了测试数据如下
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "女" }
# 1:$set :指定一个字段的值,如果字段不存在,会创建一个
> db.user.update({name:'张三'}, {$set:{sex:'保密'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密" }
# 如果指定修改的字段不存在则新增加
> db.user.update({name:'张三'}, {$set:{nick:'小三'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "nick" : "小三" }
# 2:$unset :删掉某个字段
> db.user.update({name:'张三'}, {$unset:{nick:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密" }
# 3:$inc :用来增加已有键的值,如果字段不存在,会创建一个。只能用于整型、长整型、或双精度浮点型的值。
> db.user.update({name:'张三'}, {$inc:{age:12}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12 }
# 4:$push:向已有数组的末尾加入一个元素,要是没有就新建一个数组
> db.user.update({name:'张三'}, {$set:{group:[4,2]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 4, 2 ] }
> db.user.update({name:'张三'}, {$push:{group:5}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 4, 2, 5 ] }
# 5:$each:通过一次$push来操作多个值,同时push多个值
> db.user.update({name:'张三'}, {$push:{group:{$each:[4,2,6,5]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 4, 2, 5, 4, 2, 6, 5 ] }
# 6:$slice:限制数组只包含最后加入的n个元素,其值必须是负整数
> db.user.update({name:'张三'}, {$push:{group:{$each:[4,2,6,5],$slice:-3}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 2, 6, 5 ] }
# 7:$sort:对数组中的元素,按照指定的字段来对数据进行排序(1为升序,-1为降序),然后再按照slice删除。注意:不能只将$slice或者$sort与$push配合使用,且必须使用$each
> db.user.update({name:'张三'}, {$push:{group:{$each:[4,2,6,5],$slice:-3, $sort:1}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6 ] }
# 8:$ne:判断一个值是否在数组中,如果不在则添加进去
> db.user.update({name:'张三', group:{$ne:7}}, {$push:{group:7}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6, 7 ] }
> db.user.update({name:'张三', group:{$ne:7}}, {$push:{group:7}})
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6, 7 ] }
# 9:$addToSet:将数组作为数据集使用,以保证数组内的元素不会重复
> db.user.update({name:'张三'}, {$addToSet:{group:7}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
# 10:$pop :从数组一端删除元素,{$pop:{key:1}},从末尾删掉一个,-1则从头部删除
> db.user.update({name:'张三'}, {$pop:{group:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
# 11:$pull:按照条件来删除所有匹配的元素
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6, 7, 7 ] }
> db.user.update({name:'张三'}, {$pull:{group:7}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6 ] }
# 12:$:用来修改第一个匹配的元素
> db.user.update({name:'张三'}, {$set:{'group.2':8}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 8 ] }
# 像上面这种就是根据数组的索引来替换值
> db.user.update({name:'张三', 'group.1':6}, {$set:{'group.$':4}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'张三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "张三", "sex" : "保密", "age" : 12, "group" : [ 5, 4, 8 ] }
以上的命令一定要自己都跟着练习几次,如此这些东西才能算是你学到的。学了和学到是两个完全不同的状态。我后面还会进一步介绍其它高级内容,以及与实际项目的结合,欢迎关注。