MongoDB
MongoDB相关概念
MongoDB简介
MongoDB是一个开源、高性能、无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL数据库产品中的一种。是最像关系型数据库(MySQL)的非关系型数据库。
它支持的数据结构非常松散,是一种类似于 JSON 的 格式叫BSON,所以它既可以存储比较复杂的数据类型,又相当的灵活。
MongoDB中的记录是一个文档,它是一个由字段和值对(field:value)组成的数据结构。MongoDB文档类似于JSON对象,即一个文档认为就是一个对象。字段的数据类型是字符型,它的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组。
使用场景:
具体的应用场景如:
1)社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。
2)游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、高效率存储和访问。
3)物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
4)物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析。
5)视频直播,使用 MongoDB 存储用户信息、点赞互动信息等。
这些应用场景中,数据操作方面的共同特点是:
(1)数据量大
(2)写入操作频繁(读写都很频繁)
(3)价值较低的数据,对事务性要求不高
对于这样的数据,我们更适合使用MongoDB来实现数据的存储。相对于Mysql,MongoDB可以以更低的成本解决问题(包括学习、开发、运维等成本)
体系结构
MySQL和MongoDB对比
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
colunm | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB不支持 | |
嵌入文档 | MongoDB通过嵌入式文档来替代多表连接 | |
primary key | primary key | 主键,MongoDB自动将 _id 设置为主键 |
数据模型
MongoDB的最小存储单位就是文档(document)对象。文档(document)对象对应于关系型数据库的行,数据在MongoDB中以BSON(Binary-JSON)文档的格式存储在磁盘上
BSON(Binary Serialized Document Format)是一种类json的一种二进制形式的存储格式,简称Binay JSON。BSON和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Data和BinData类型。
BSON采用了类似于 C 语言结构体的名称、对表示方法,支持内嵌的文档对象和数组对象,具有轻量性、可遍历性、高效性的三个特点,可以有效描述非结构化数据和结构化数据。这种格式的优点是灵活性高,但它的缺点是空间利用率不是很理想。
Bson中,除了基本的JSON类型:string,integer,boolean,double,null,array和object,mongo还使用了特殊的数据类型。这些类型包括date,object id,binary data,regular expression 和code。每一个驱动都以特定语言的方式实现了这些类型,查看你的驱动的文档来获取详细信息。
BSON数据类型参考列表:
数据类型 | 描述 | 举例 |
---|---|---|
字符串 | UTF-8字符串都可表示为字符串类型的数据 | {“x” : “foobar”} |
对象id | 对象id是文档的12字节的唯一ID | {“X” : ObjectId()} |
布尔值 | 真或者假:true 或者 false | {“x”:true}+ |
数组 | 值的集合或者列表可以表示成数组 | {“x” : [“a”, “b”, “c”]} |
32位整数 | 类型不可用。JavaScript仅支持64位浮点数,所以32位整数会被自动转换 | shell是不支持该类型的,shell中默认会转换成64位浮点数 |
64位整数 | 不支持这个类型。shell会使用一个特殊的内嵌文档来显示64位整数 | shell是不支持该类型的,shell中默认会转换成64位浮点数 |
64为浮点数 | shell中的数字就是这一种类型 | {“x”:3.14159,”y”:3} |
null | 表示空值或者未定义的对象 | {“x”:null} |
undefined | 文档中也可以使用未定义类型 | {“x”:undefined} |
符号 | shell不支持,shell会将数据库中的符号类型的数据自动转换成字符串 | |
正则表达式 | 文档中可以包含正则表达式,采用JavaScript的正则表达式语法 | {“x” : /foobar/i} |
代码 | 文档中还可以包含JavaScript代码 | {“x” : function() { /* …… */ }} |
二进制数据 | 二进制数据可以由任意字节的串组成,不过shell中无法使用 | |
最大值/最小值 | BSON包括一个特殊类型,表示可能的最大值。shell中没有这个类型 |
提示:
shell默认使用64位浮点型数值。{“x”:3.14}或{“x”:3}。对于整型值,可以使用NumberInt(4字节符号整数)或NumberLong(8字节符号整数),{“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}
特点
高性能:
- MongoDB提供高性能的数据持久性。特别是:
- 对嵌入式数据模型的支持减少了数据库系统上的 I/O 活动
- 索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。(文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求、地理位置索引可用于构建各种 O2O 应用)
- mmapv1 、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支持满足各种场景需求
- Gridfs解决文件存储的需求
高可用性:
- MongoDB的复制工具称为副本集(replica set),它可提供自动故障转移和数据冗余
高可扩展性:
- MongoDB提供了水平可扩展性作为其核心功能的一部分。
- 分片将数据分部在一组集群的机器上。(海量数据存储,服务能力水平扩展)
- 从3.4开始,MongoDB支持基于片键创建数据区域。在一个平衡的集群中,MongoDB将一个区域所覆盖的读写只定向到该区域内的那些片
丰富的查询支持:
MongoDB支持丰富的查询语言,支持读和写操作(CRUD),比如数据聚合、本地搜索和地理空间查询等
其他特点: 如无模式(动态模式)、灵活的文档模型
部署
Windows系统中的安装启动
下载安装
可以在官网上下载。Windows系统用zip,Linux系统用tar
解压安装启动
将压缩包解压到一个目录中。
在解压目录中,手动建立一个目录用于存放数据文件,如 /data/db
方式1 :命令行参数方式启动服务
在
/bin
目录中打开命令行提示符,输入如下命令:mongod --dbpath = ../data/db
我们在启动信息中可以看到,mongoDB的默认端口是27017,如果我们想改变默认的启动端口,可以通过–port来指定端口。
为了方便我们每次启动,可以将安装目录的 bin 目录设置到环境变量的 path 中,
bin
目录下是一些常用命令,比如mongod
是启动服务用的,mongo
是客户端连接服务用的方式2 :配置文件方式启动服务
在解压目录中新建
config
文件夹,该文件夹中新建配置文件mongod.conf
,内容参考如下:storage: #The directory where the mongod instance stores its data.Default Value is "\data\db" on Windows. dbPath: D:\02_Server\DBServer\mongodb-win32-x86_64-2008plus-ssl-4.0.1\data
详细内容参考文档 :https://docs.mongodb.com/manual/reference/configuration-options/
注意:
配置文件中如果使用双引号,比如路径地址,自动会将双引号的内容转义。如果不转义,则会报错:
error-parsing-yaml-config-file-yaml-cpp-error-at-line-3-column-15-unknown-escape-character-d
解决:
- 对
\
换成/
或者\\
- 如果路径中没有控制,则无需加引号
- 对
配置文件中不能以 Tab 分割字段
解决:
将其转换成空格
启动方式:
mongodb -f ../config/mongod.conf 或 mongodb --config ../config/mongod.conf
更多参数配置:
systemLog:
destination: file
#The path of the log file to which mongod or mongos should send all diagnostic logging information
path: "D:/02_Server/DBServer/mongodb-win32-x86_64-2008plus-ssl-4.0.1/log/mongod.log"
logAppend: true
storage:
journal:
enabled: true
#The directory where the mongod instance stores its data.Default Value is "/data/db".
dbPath: "D:/02_Server/DBServer/mongodb-win32-x86_64-2008plus-ssl-4.0.1/data"
net:
#bindIp: 127.0.0.1
port: 27017
setParameter:
enableLocalhostAuthBypass: false
Shell连接(mongo命令)
在命令提示符输入一下 shell 命令即可完成登录
mongo
或
mongo --host=127.0.0.1 --port=27017
查看已有的数据库
> show databases
退出mongodb
exit
更多参数可以通过 help 查看
mongo --help
提示:
MongoDB javaScript shell 是一个基于 javaScript 的解释器,所以是支持 js 程序和语法的
Compass - 图形化界面客户端
在MongoDB官网下载 MongoDB Compass
如果是下载安装版,则按照步骤安装;如果是下载加压缩版,直接解压,执行里面的 MongoDBCompassCommunity.exe 文件即可。
在打开的界面中,输入主机地址、端口等相关信息,点击连接:
在Linux系统中的安装启动
目标:在Linux中部署一个单机的 MongoDB,作为生产环境下使用
提示:和 Windows 下操作差不多
下载安装连接
官网下载,可以通过xftp传到Linux中
上传到Linux后,解压到当前目录:
tar -zxvf mongodb-linux-x86_64-4.0.10.tgz
移动解压后的文件夹到指定的目录中:
mv mongodb-linux-x86_64-4.0.10 /usr/local/mongodb
新建几个目录,分别用来存储数据和日志:
#数据存储目录 mkdir -p /mongodb/single/data/db #日志存储目录 mkdir -p /mongodb/single/log
新建并修改配置文件
vim /mongodb/single/mongod.conf
配置文件的内容如下:
systemLog: #MongoDB发送所有日志输出的目标指定为文件 # #The path of the log file to which mongod or mongos should send all diagnostic logging information destination: file #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径 path: "/mongodb/single/data/log/mongod.log" #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。 logAppend: true storage: #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。 ##The directory where the mongod instance stores its data.Default Value is "/data/db". dbPath: "/mongodb/single/data/db" # journal: #启用或禁用持久性日志以确保数据文件保持有效和可恢复。 # enabled: true processManagement: #启用在后台运行mongos或mongod进程的守护进程模式。 fork: true net: #服务实例绑定的IP,默认是localhost bindIp: 192.168.200.133 #bindIp #绑定的端口,默认是27017 port: 27017
启动 MongoDB 服务
/usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf
启动成功的页面会显示:
about to fork child process, waiting until server is ready for connections. forked process: 90384 child process started successfully, parent exiting
注意:
如果启动后不是
successfully
,则是启动失败了。原因基本上就是配置文件有问题可以通过进程来查看服务是否启动了:
[root@byxlCentos-1 ~]# ps -ef |grep mongod root 2717 1 0 19:45 ? 00:00:02 /usr/local/mongodb/bin/mongod -f /mongodb/single/mongodb.conf root 3057 2907 0 19:52 pts/0 00:00:00 grep --color=auto mongod
分别使用mongo命令和compass 工具来连接测试
mongo 命令
注意:
--host=自己Linux的ip地址
,port默认就是 27017这个端口compass工具
注意要改成自己Linux的ip地址
提示:如果远程连接不上,需要配置防火墙放行,或直接关闭Linux防火墙
#查看防火墙状态 systemctl status firewalld #临时关闭防火墙 systemctl stop firewalld #开机禁止启动防火墙 systemctl disable firewalld
停止关闭服务
停止服务的方式有两种:快速关闭和标准关闭:
快速关闭方法(快速、简单,数据可能会出错)
目标:通过系统的kill命令直接杀死进程:
杀完要检查一下,避免有的没有杀掉。
#通过进程编号关闭节点 kill -2 54410
补充: 如果一旦是因为数据损坏,则需要进行如下操作(了解):
删除 lock 文件:
rm -rf /mongodb/single/data/db/*.lock
修复数据
/usr/local/mongdb/bin/mongod --repair --dbpath=/mongodb/single/data/db
标准的关闭方法(数据不容易出错,但麻烦):
目标:通过mongo客户端中的 shutdownServer 命令来关闭服务
//客户端登录服务,注意,这里通过localhost登录,如果需要远程登录,必须先登录认证才行。 mongo --port 27017 //#切换到admin库 use admin //关闭服务 db.shutdownServer()
基本常用命令
案例需求
存放文章品论的数据存放到MongoDB中,数据结构参考如下:
数据库:articledb
专栏文章评论 | comment | ||
---|---|---|---|
字段名称 | 字段含义 | 字段类型 | 备注 |
_id | ID | ObjectId 或 String | Mongo的主键的字段 |
article | 文章ID | String | |
content | 评论内容 | String | |
userid | 评论人ID | String | |
nickname | 评论人昵称 | String | |
createdatetime | 评论的日期时间 | Date | |
likenum | 点赞数 | Int32 | |
replynum | 回复数 | Int32 | |
state | 状态 | String | 0 :不可见;1 :可见 |
parentid | 上级ID | String | 如果为0表示文章的顶级评论 |
数据库操作
选择和创建数据库
选择和创建数据库的语法格式:
use 数据库名称
如果数据库不存在则自动创建,例如,以下语句创建 spitdb
数据库:
use articledb
查看有权限查看的所有的数据库命令
show dbs
或
show databases
注意:在 MongoDB 中,集合只有在内容插入后才会创建!就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建
查看当前正在使用的数据库命令
db
MongoDB 中默认的数据库为 test,如果你没有选择数据库,集合将存放在 test 数据库中。
另外:
数据库名可以是满足以下条件的任意 UTF-8 字符串
- 不能是空字符串(””)
- 不得含有 ‘ ‘ (空格)、. 、 $ 、 / 、 \ 和 \0 (空字符)
- 应全部小写
- 最多有64字节
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库
- admin : 从权限的角度来看,这是 “root” 数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器
- local : 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
- config : 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片和相关信息
数据库的删除
MongoDB 删除数据库的语法格式如下:
db.dropDatabase()
提示:主要用来删除已经持久化的数据库
集合操作
集合,类似关系型数据库中的表
可以显示的创建,也可以隐式的创建
集合的显示创建(了解)
基本语法格式:
db.createCollection(name)
参数说明:
- name :要创建的集合名称
例如:创建一个名为 mycollection
的普通集合
db.createCollection(mycollection)
查看当前库中的表:show tables
show collections
或
show tables
集合的命名规范:
- 集合名不能是空字符串 “ “
- 集合名不能含有 \0 字符(空字符),这个字符表示集合名的结尾
- 集合名不能以 “system.” 开头,这是为系统集合保留的前缀
- 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$
集合的隐式创建(常用)
当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合(collection)。
db.collection.insert({})
集合的删除
集合删除语法格式如下:
db.collection.drop()
或
db.集合.drop()
返回值
如果成功删除选定集合,则 drop() 方法返回 true ,否则返回false
例如:要删除 mycollection 集合
db.mycollection.drop()
文档基本CRUD
文档(document)的数据结构和 JSON 基本一样
所有存储在集合中的数据都是 BSON 格式
文档的插入
单个文档的插入
使用 insert() 或 save() 方法向集合中插入文档,语法如下:
db.collection.insert( <document or array of documents>, { writeConcern: <document>, ordered: <boolean> } )
参数:
Parameter Type Description document document or array 要插入到集合中的文档或文档数组(json格式) writeConcern document Optional. A document expressing the write concern. Omit to use the default write concern.See Write Concern.Do not explicitly set the write concern for the operation if run in a ordered boolean 可选。如果为真,则按顺序插入数组中的文档,如果其中一个文档出现错误,MongoDB将返回而不处理数组中的其余文档。如果为假,则执行无序插入,如果其中一个文档出现错误,则继续处理数组中的主文档。在版本2.6+中默认为true 示例:
向comment集合(表)中插入一条测试数据:
db.comment.insert({ "articleid":"100000", "content":"今天天气真好,阳光明媚", "userid":"1001", "nickname":"Rose", "createdatetime":new Date(), "likenum":NumberInt(10), "state":null })
提示:
- comment集合如果不存在,则会隐式创建
- mongo中的数字,默认情况下是 doouble 类型,如果要存整型,必须使用函数 NumberInt(整型数字),否则取出来就有问题了
- 插入当前日期使用
new Date()
- 插入的数据没有指定
_id
,会自动生成主键值 - 如果某字段没值,可以赋值为 null ,或不写该字段
执行后,如下,说明插入一个数据成功了。
writeResult({"nInserted" : 1})
注意:
- 文档中的键/值对是有序的
- 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)
- MongoDB 区分类型和大小写
- MongoDB 的文档不能有重复的键
- 文档的键是字符串,除了少数例外情况,键可以使用任意 UTF-8 字符
文档键命名规范:
- 键不能含有 \0 (空字符),这个字符用来表示键的结尾
- . 和 $ 有特别的意义,只有在特定环境下才能使用
- 以下划线 “_” 开头的键是保留的(不是严格要求的)
批量插入
语法:
db.collection.insertMany( [ <document 1>, <document 3>, ...], { writeConcern: <document>, ordered: <boolean> })
参数:
Parameter Type Description document document 要插入到集合中的文档或文档数组。(json格式) writeConcern document Optional. A document expressing the write concern. Omit to use the default write concern.Do not explicitly set the write concern for the operation if run in a transaction. To use write concern with transactions, see Transactions and Write Concern. ordered boolean 可选。一个布尔值,指定Mongod实例应执行有序插入还是无序插入。默认为true。 示例:
批量插入多条文章评论:
db.comment.insertMany([ { "_id": "1", "articleid": "100001", "content": "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid": "1002", "nickname": "相忘于江湖", "createdatetime": new Date("2019-08-05T22:08:15.522Z"), "likenum": NumberInt(1000), "state": "1" }, { "_id": "2", "articleid": "100001", "content": "我夏天空腹喝凉开水,冬天喝温开水", "userid": "1005", "nickname": "伊人憔悴", "createdatetime": new Date("2019-08-05T23:58:51.485Z"), "likenum": NumberInt(888), "state": "1" }, { "_id": "3", "articleid": "100001", "content": "我一直喝凉开水,冬天夏天都喝。", "userid": "1004", "nickname": "杰克船长", "createdatetime": new Date("2019-08-06T01:05:06.321Z"), "likenum": NumberInt(666), "state": "1" }, { "_id": "4", "articleid": "100001", "content": "专家说不能空腹吃饭,影响健康。", "userid": "1003", "nickname": "凯撒", "createdatetime": new Date("2019-08-06T08:18:35.288Z"), "likenum": NumberInt(2000), "state": "1" }, { "_id": "5", "articleid": "100001", "content": "研究表明,刚烧开的水千万不能喝,因为烫嘴。", "userid": "1003", "nickname": "凯撒", "createdatetime": new Date("2019-08-06T11:01:02.521Z"), "likenum": NumberInt(3000), "state": "1" } ]);
提示:
插入时指定了
_id
,则主键就是该值。如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉。
因为批量插入由于数据较多容易出现失败,因此使用 try catch 进行异常捕捉处理,测试的时候可以不处理,如:(了解)
try{ db.comment.insertMany([ { "_id": "1", "articleid": "100001", "content": "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid": "1002", "nickname": "相忘于江湖", "createdatetime": new Date("2019-08-05T22:08:15.522Z"), "likenum": NumberInt(1000), "state": "1" }, { "_id": "2", "articleid": "100001", "content": "我夏天空腹喝凉开水,冬天喝温开水", "userid": "1005", "nickname": "伊人憔悴", "createdatetime": new Date("2019-08-05T23:58:51.485Z"), "likenum": NumberInt(888), "state": "1" }, { "_id": "3", "articleid": "100001", "content": "我一直喝凉开水,冬天夏天都喝。", "userid": "1004", "nickname": "杰克船长", "createdatetime": new Date("2019-08-06T01:05:06.321Z"), "likenum": NumberInt(666), "state": "1" }, { "_id": "4", "articleid": "100001", "content": "专家说不能空腹吃饭,影响健康。", "userid": "1003", "nickname": "凯撒", "createdatetime": new Date("2019-08-06T08:18:35.288Z"), "likenum": NumberInt(2000), "state": "1" }, { "_id": "5", "articleid": "100001", "content": "研究表明,刚烧开的水千万不能喝,因为烫嘴。", "userid": "1003", "nickname": "凯撒", "createdatetime": new Date("2019-08-06T11:01:02.521Z"), "likenum": NumberInt(3000), "state": "1" } ]); } catch (e){ print(e); }
文档的基本查询
查询数据的语法格式如下:
db.collection.find(<query>, [projection])
参数:
Parameter | Type | Description |
---|---|---|
query | document | 可选。使用查询运算符指定选择筛选器,若要返回集合中的所有文档,请省略此参数或传递空文档 ({}) |
projection | document | 可选。指定要在与查询筛选器匹配的文档中返回的字段(投影)。若要返回匹配文档中的所有字段,请省略此参数 |
示例:
查询所有
如果我们要查询 spit 集合的所有文档,输入以下命令:
db.comment.find() 或 db.comment.find({})
这里你会发现每条文档会有一个叫 _id 的字段,这个相当于我们原来关系数据库中表的主键,当在插入文档记录时没有指定该字段,MongoDB 会自动创建,其类型时 ObjectID 类型。
如果在插入文档记录时指定该字段也可以,其类型可以时 ObjectID类型,也可以是MongoDB支持的任意类型。
如果想按一定条件来查询,例如我想查询 userid 为 1003 的记录,需要在find()中添加参数,参数也是json格式,如下:
db.comment.find({userid : "1003"})
如果只需要返回符合条件的第一条数据,我们可以使用 findOne 命令,语法和find 一样。
db.comment.find({userid : "1003"}) //只会返回第一条数据记录
投影查询(Projection Query):
如果要查询结果返回部分字段,则需要使用投影查询(不显示所有字段,只显示指定的字段)
例如:查询结果只显示
_id 、userid 、nickname
>db.comment.find({userid:"1002"},{userid:1, nickname:1})
默认
_id
会显示。例如:查询结果只显示
userid 、nickname
,不显示_id
>db.comment.find({userid:"1003"},{userid:1, nickname:1, id:0}) { "userid" : "1003", "nickname" : "凯撒" } { "userid" : "1003", "nickname" : "凯撒" }
例如:查询所有数据,但只显示
_id、userid、nickname
>db.comment.find({}, {userid:1, nickname:1})
文档的更新
更新文档的语法:
db.collection.update(query, update, options)
//或
db.collection.update(
<query>,
<update>,
{
upset: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument>, ...],
hint: <ducument|string>
}
)
参数:
提示:
主要关注前四个参数即可。
示例:
覆盖的修改
如果我们想修改 _id 为1的记录,点赞量为 1001 ,输入一下语句:
db.comment.update({_id:"1"}, {likenum:NumberInt(1002)})
执行后,我们会发现,这条文档除了 likenum 字段,其他字段都不见了
局部修改
为了解决这个问题,我们需要使用修改器 $set 来实现,命令如下:
我们想修改为 _id 为2的记录,浏览器为889,输入以下语句:
db.comment.update({_id:"2"},{$set:{likenum:NumberId(889)}})
批量的修改
更新所有用户为 1003 的用户的昵称为
张三
。//默认只修改第一条数据 db.comment.update({userid:"1003"}, {$set:{nickname:"张三"}}) //修改所有符合条件的数据 db.comment.update({userid:"1003"}, {$set:{nickname:"李四"}}, {multi:true})
提示:如果不加后面的参数
{multi:true}
,则只更新符合条件的第一条记录列值增长的修改
如果我们想实现对某列值在原有值的基础上进行增加或减少,可以使用
inc
运算符来实现。需求:对3号数据的点赞数,每次递增1
db.comment.update({_id:"3"}, {$inc: {likenum:NumberInt(1)}})
删除文档
删除文档的语法结构:
db.集合名称.remove(条件)
以下语句可以将数据全部删除,请慎用
db.comment.remove({})
如果删除 _id = 1 的记录,输入以下语句:
db.comment.remove({id:"1"})
文档分页查询
统计查询
统计查询使用 count( ) 方法,语法如下:
db.collection.count(query, options)
参数:
Parameter | Type | Description |
---|---|---|
query | document | 查询选择条件 |
options | document | 可选。用于修改技术的额外选项 |
提示:可选项暂时不使用
示例:
统计所有记录数:
统计comment集合的所有的记录数:
db.comment.count()
按条件统计记录数:
统计userid为1003的记录条数:
db.comment.count({userid:"1002"})
提示:默认情况下 count()
方法返回符合条件的全部记录条数
分页列表查询
可以使用 limit() 方法来读取指定数量的数据,使用 skip() 方法来跳过指定数量的数据。
基本语法如下:
db.collection.find().limit(number).skip(number)
如果想返回指定条数的记录,可以在find方法后调用limit方法来返回结果,默认值20,例如:
db.comment.find().limit(3) //表示只显示最前面的三条数据
skip 方法接收一个数字n作为跳过的记录条数(即前n个数据不要),默认值为0
db.comment.find().skip(3) //表示跳过前三条数据输出后面的数据
分页查询:需求:每页2个,第二页开始:跳过前两条数据,接着显示3、4条数据
//第一页
db.comment.find().skip(0).limit(2)
//第二页
db.comment.find().skip(2).limit(2)
//第三页
db.comment.find().skip(4).limit(2)
排序查询
sort( ) 方法对数据进行排序,sort( ) 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,-1 为降序排列
语法格式如下:
db.collection.find().sort({key:1})
//或
db.collection.find().sort(排序方式)
示例:
先对 userid 降序排列,再对其中的如果有重复则对点赞数进行升序排列
db.comment.find().sort({userid: -1, likenum: 1})
注意:
skip( ),limit( ),sort( ) 三个放在一起执行的时候,执行的顺序是先 sort( ),然后是 skip( ),最后是显示的 limit( ),和命令编写顺序无关。
文档的更多查询
正则的复杂条件查询
MongoDB的模糊查询是通过 正则表达式 的方式实现的。格式为:
db.collection.find({field:/正则表达式/})
提示:正则表达式是 js 的语法,直接量的写法。
示例: 查询评论内容包含 “开水” 的所有文档:
db.comment.find({content: /开水/})
查询评论内容中以 “专家” 开头的所有文档:
db.comment.find({content: /^专家/})
比较查询
< , <= , > , >= 这个操作符很常用,格式如下:
db.集合名称.find({ "field" : { $gt: value }}) // 大于: field > value
db.集合名称.find({ "field" : { $lt: value }}) // 小于: field < value
db.集合名称.find({ "field" : { $gte: value }}) // 大于等于: field >= value
db.集合名称.find({ "field" : { $lte: value }}) // 小于等于: field <= value
db.集合名称.find({ "field" : { $ne: value }}) // 不等于: field != value
示例: 查询评论点赞数量大于 700 的记录
db.comment.find({likenum:{$gt: NumberInt(700)}})
包含查询
包含使用 $in 操作符。
示例: 查询评论的集合中 userid 字段包含 1003 或 1004 的文档
db.comment.find({userid: {$in: ["1003", "1004"]}})
不包含使用 $nin 操作符。
示例: 查询评论集合中 userid 字段不包含 1003 和 1004 的文档
db.comment.find({userid: {$nin: ["1003", "1004"]}})
条件连接查询
如果需要查询同时满足两个以上条件,需要使用 $and 操作符将条件进行关联。(相当于 SQL 的and),格式为:
db.collection.find($and: [{field:{}}, {field:{}}, {field:{}}])
示例:查询评论集合中 likenum 大于等于 700并且小于 2000的文档:
db.comment.find({$and: [
{likenum: {$gte: NumberInt(700)}},
{likenum: {$lt:NumberInt(2000)}}
]})
如果两个以上条件之间是或者的关系,可以使用操作符进行关联,与前面的 and 的使用方式相同,格式为:
db.collection.find({$or: [{field:{}}, {field:{}}, {field:{}}]})
示例:查询评论集合中 userid 为 1003,或者点赞数小于 1000 的文档记录:
db.comment.find({$or: [
{userid: "1003"},
{likenum: {$lt: 1000}}
]})
常用命令小结
选择切换数据库:
use articledb
插入数据:
db.comment.insert({ userid:"1001", nickname:"rose" })
查询所有数据:
db.comment.find()
条件查询数据:
db.comment.find({ userid: "1001" })
查询符合条件的第一条记录:
db.comment.findOne({ userid: "1001" })
查询符合条件的前几条记录:
db.comment.find({ userid: "1001" }).limit(2)
查询符合条件的跳过的记录:
db.comment.find({ userid: "1001" }).skip(2)
修改数据:
db.comment.update({条件}, {修改后的数据}) 或 db.comment.update({userid}, {$set: {要修改部分的字段: 数据}})
修改数据并自增某字段
db.comment.update( {userid: "1001"}, {$inc: {likenum: NumberInt(1)}} )
删除数据
db.comment.remove({userid: "1001"})
统计查询:
db.comment.count({userid: "1001"})
模糊查询:
db.comment.find({content: /专家/})
条件比较运算
db.comment.find({likenum: {$gt: 100}})
包含查询:
db.comment.find({ userid: {$in:["1001", "1002"]} }) 或: db.comment.find({ userid: {$nin: ["1003", "1004"]} })
条件连接查询:
db.comment.find({ $and: [{userid: "1001"}, {nickname: "Rose"}] }) 或: db.comment.find({ $or: [{userid: "1001"}, {nickname: "Rose"}] })
索引-Index
概述
匹配的文档。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
如果查询存在适当的索引,MongoDB可以使用该索引限制必须检查的文档数。
索引是特殊的数据结构,它以易于遍历的形式存储集合数据集的一小部分。索引存储特定字段或一组字段的值,按字段值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。此外,MongoDB还可以使用索引中的排序返回排序结果。
官网文档:https://docs.mongodb.com/manual/indexes/
了解:
MongoDB索引使用B树数据结构(确切的说是B-Tree,MySQL是B+Tree)
索引的类型
单字段索引
MonogDB支持在文档的单个字段上创建用户定义的升序/降序索引,称为单字段索引(Single Field Index)
对于单个字段索引和排序操作,索引键的排序顺序(即升序或降序)并不重要,因为MongoDB可以在任何方向上遍历索引。
复合索引
MongoDB还支持多个字段的用户定义索引,即复合索引(Compound Index)。
复合索引中列出的字段顺序具有重要意义。例如,如果符合索引由 {userid: 1, score: -1}
组成,则索引首先按 userid 正序排序,然后在每个 userid 值内,再在按 score 倒序排序。
其他索引
地理空间索引(Geospatial Index)、文本索引(Text Indexes)、哈希索引(Hashed Indexes)。
地理空间索引(Geospatial Index):
为了支持对地理空间坐标数据的有效查询,MongoDB提供了两种特殊的索引:返回结果时使用平面集合的二维索引和返回结果时使用球面集合的二维球面索引。
文本索引(Text Indexes):
MongoDB提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停止词(例如 “the”、“a”、“or”),而将集合中的词作为词干,只存储根词。
哈希索引(Hashed Indexes):
为了支持基于散列的分片,MongoDB提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分部更加随机,但只支持相等匹配,不支持基于范围的查询。
索引的管理操作
索引的查看
说明:
返回一个集合中的索引的数组
语法:
db.collection.getIndexes()
提示: 该语法命令运行要求时 MongoDB 3.0+
示例:查看 comment 集合中所有的索引情况
db.comment.getIndexes()
结果为:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "articledb.comment"
}
]
结果中显示的是默认 _id 索引。
默认 _id 索引:
MonogDB在创建集合的过程中,在 _id 字段上创建一个唯一的索引,默认名字为 _id ,该索引可防止客户端插入两个具有相同值的文档,您不能在 _id 字段上删除此索引。
注意: 该索引是唯一索引,因此值不能重复,即 _id 值不能重复的。在分片集群中,通常使用 _id 作为片键。
索引的创建
说明:
在集合上创建索引:
语法:
db.collection.createIndex(keys, options)
参数:
Parameter | Type | Description |
---|---|---|
keys | document | 包含字段和值对的文档,其中字段是索引键,值描述该字段的索引类型。对于字段上的升序索引,请指定值1;对于降序索引,请指定值-1。比如: {字段:1或-1} ,其中1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。另外,MongoDB 支持几种不同的索引 |
options | document | 可选。包含一组控制索引创建的选项的文档。有关详细信息,请参见选项详情列表 |
options(更多选项)列表:
Parameter | Type | Description |
---|---|---|
background | Boolean | 建索引过程会阻塞其他数据库操作,background 可指定以后方式创建索引,即增加 “background” 可选参数。”background” 默认值为 false |
unique | Boolean | 建立的索引是否唯一。指定为 true创建唯一索引。默认值为 false |
name | String | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称 |
dropDups | Boolean | **3.0+**版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false. |
sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为 true 的话,在索引字段中不会查询出不包含对应字段的文档。默认值为 false |
expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL 设定,设定集合的生存时间 |
v | index version | 索引的版本号。默认的索引版本取决于 mongod 创建索引时运行的版本 |
weights | document | 索引权重值,数值在 1 到 99999 之间,表示该索引相对于其他索引字段的得分权重 |
default_language | String | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。默认为英语 |
language_override | string | 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的 language ,默认值为 language |
注意:
在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex()
,之后的版本使用了 db.collection.createIndex()
方法,ensureIndex()
还能用,但只是 createIndex()
的别名。
示例:
单字段索引示例:对 userid 字段建立索引:
db.comment.createIndex({userid: 1})
参数1:按升序创建索引,索引名字为 userid
复合索引:对
userid
和nickname
同时建立复合(Compound)索引:db.comment.createIndex({userid: 1, nickname: -1})
查看索引:
索引的移除
说明:可以移出指定的索引,或移出所有索引
一、指定索引的移除
语法:
db.collection.dropIndex(index)
参数:
Parameter | Type | Description |
---|---|---|
index | String or document | 指定要删除的索引。可以通过索引名称或索引规范文档指定索引。若要删除文本索引,请指定索引名称 |
示例: 删除 comment 集合中 userid 字段上的上升索引:
db.comment.dropIndex({userid: 1})
查看已经删除了
二、所有索引的移除
语法:
db.collection.dropIndexes()
示例: 删除 spit 集合中所有的索引:
db.comment.dropIndexes()
注意: _id 的字段是无法删除的,只能删除非 _id 字段的索引。
索引的使用
执行计划
分析查询性能(Analyze Query Performance)通常使用执行计划(解释计划、Explain Plan)来查看查询的情况,如查询耗费的时间、是否基于索引查询等。
那么,通常,我们想知道,建立的索引是否有效,效果如何,都需要通过执行计划查看。
语法:
db.collection.find(query, options).explain(options)
示例: 查看根据 userid 查询数据的情况:
db.comment.find({userid: "1003"}).explain()
关键看:"stage": "COLLSCAN"
, 表示全集合扫描
"stage": "INSCAN"
, 表示索引扫描
涵盖的查询
Covered Queries
当查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存。这些覆盖的查询可以非常有效。
更多规则信息查看:https://www.mongodb.com/docs/manual/core/query-optimization/#read-operations-covered-query
示例:
db.comment.find({userid: "1003"}, {userid: 1, _id: 0})
查出的是:
db.comment.find({userid: "1003"}, {userid: 1, _id: 0}).explain()
类型题
进入 my_test 数据库
use my_test
向数据库的user集合中插入一个文档
db.user.insert({ username:"sunwukong" })
查询user集合中的文档
db.user.find()
向数据库的user集合中插入一个文档
db.users.insert({ username:"zhubajie" })
查询数据库user集合中的文档
db.user.find()
统计数据库user集合中的文档数量
db.user.find().count()
查询数据库user集合中的username为sunwukong的文档
db.user.find({ username:"sunwukong" })
向数据库user集合中的username为sunwukong的文档,添加一个 address 属性,属性值为 huaguoshan
db.user.update({username:"sunwukong"}, {$set:{address:"huaguoshan"}})
使用 {username: “tangseng”} 替换 username 为zhubajie 的文档
db.user.relaceOne({username:"zhubajie"}, {username:"tangseng"}})
删除username为sunwukong的文档的address属性
db.user.update({username:"sunwukong"}, {$unset:{address:1}})
向username为sunwukong的文档中,添加一个 hobby:
cities:["bwijing", "shanghai", "shenzhen"]
MongoDB的文档属性也可以是一个文档,当一个文档的属性值是一个文档时,我们称这个文档为 “内嵌文档”
db.user.update( {username:"sunwukong"}, {$set: {hobby: {cities:["beijing","shanghai","shenzhen"]}}} )
向username为tangseng的文档中,添加一个hobby:
movies:["A Chinese Odyssey", "King of comedy"]
db.user.update( {username:"tangseng"}, {$set: {hobby: {movies: ["A Chinese Odyssey", "King of comedy"]}}} )
查询喜欢电影 hero 的文档
MongoDB支持直接通过内嵌文档的属性进行查询,如果要查询内嵌文档则可以通过 . 的形式来匹配
如果要通过内嵌文档来对文档进行查询,此时属性名必须使用引号 即:
"hobby.movies": "hero"
db.user.find({ "hobby.movies": "hero" })
向 tangseng 中添加一个新的电影 Interstellar
$push
用于向数组中添加一个新的元素$addToTest
用于向数组中添加一个新元素,如果数组中已经存在了该元素,则不会添加db.user.update( {user:"tangseng"}, {$push: {"hobby.movies": "Interstellar"}} )
删除喜欢 beijing 的用户
db.user.remove({"hobby.cities": "beijing"})
删除user集合
db.user.drop()
或
db.user.remove({})
向 numbers 中插入 20000条数据
//效率低 for(var i = 1; i <= 20000; i++){ db.numbers.insert({num: i}); }
//效率高 var arr=[]; for(var i = 1; i <= 20000; i++){ arr.push({num: i}); } db.numbers.insert(arr);
查询 numbers 中 num 为500的文档
db.numbers.find({ num: 500 })
查询 numbers 中 num 大于 5000 的文档
db.numbers.find({ num: {$gt:500} })
查询 numbers 中 num 小于 30 的文档
db.numbers.find({ num: {$lt: 30} })
查询 numbers 中 num 大于 40 小于 50 的文档
db.numbers.find({ num: {$gt: 40, $lt: 50} })
查询 numbers 中 num 大于 19996 的文档
db.numbers.find({ num: {$gt: 19996} })
查看 numbers 集合中的前 10 条数据
db.numbers.find({ num: {$lte: 10} })
或者
db.numbers.find({}).limit(10)
查看 numbers 集合中的第 11 条到 20 条数据
db.numebers.find({}).limit(10).skip(10)
查看 numbers 集合中的第 21 条到 30 条数据
db.numbers.find({}).limit(10).skip(20)
将 dept 和 emp 集合导入到数据库中
点import然后导入
查询工资小于 2000 的员工
db.emp.find({ sal: {$lt: 2000, $gt: 1000} })
查询工资在 1000 ~ 2000 之间的员工
db.emp.find({ sal: {$lt: 2000, $gt: 1000} })
查询工资小于1000或大于2500的员工
db.emp.find({$or: [{sal: {$lt: 1000}}, {sal: {$gt: 2000}}]})
查询财务部的所有员工
var depno = db.dept.findOne({dname: "财务部"}).deptno; db.emp.find({depno: depno})
查询销售部的所有员工
var depno = db.dept.findOne({dname: "销售部"}).deptno; db.emp.find({depno: depno})
查询所有 mgr 为 7698 的所有员工
db.emp.find({ mgr: 7698 })
查询所有 mgr 为 1000 的员工增加工资 400 元
db.emp.updateMany({sal: {$lte: 1000}}, {$inc: {sal: 400}})