MongoDB入门指南:环境安装与基本操作

发布于:2025-06-12 ⋅ 阅读:(20) ⋅ 点赞:(0)

1、Ubuntu安装MongoDB

1、导入公钥。
从终端安装 gnupgcurl(如果尚未安装):

sudo apt install gnupg curl

导入MongoDB 公共 GPG 密钥:

curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \
   sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg \
   --dearmor

2、创建文件列表。

# ubuntu-22.04
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list

# ubuntu-24.04
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list

3、重新加载包数据库。

sudo apt update

4、安装MongoDB Community Server。

sudo apt install -y mongodb-org

目录:
如果通过软件包管理器安装,则在安装过程中会创建数据目录 /var/lib/mongodb 和日志目录 /var/log/mongodb
默认情况下,MongoDB 使用 mongodb 用户账户运行。如果更改运行 MongoDB 进程的用户,您还必须修改数据和日志目录以赋予该用户访问这些目录的权限。

配置文件:MongoDB 官方软件包有一份配置文件 (/etc/mongod.conf)。这些设置(如数据目录和日志目录规范)将在启动时生效。换言之,如果在 MongoDB 实例运行时更改该配置文件,则必须重新启动实例才能使更改生效。

初始化系统:要运行和管理 mongod 进程,需使用操作系统内置的初始化系统。较新版本的 Linux 倾向于使用 systemd(它会使用 systemctl 命令),而较旧版本的 Linux 倾向于使用 System V init(它使用 service 命令)。

启动MongoDB:

sudo systemctl start mongod

验证MongoDB是否启动成功:

sudo systemctl status mongod

停止MongoDB:

sudo systemctl stop mongod

重启MongoDB:

sudo systemctl restart mongod

开始使用MongoDB:

mongosh

使用以下命令查看mongod服务端程序:

sudo netstat -tnlp

在这里插入图片描述
可以看到该程序所占用的端口为27017。所以MongoDB也是基于CS的网络服务程序。

使用mongosh进入客户端后,会看到
在这里插入图片描述
可以看到服务端和客户端的版本,还有一些警告信息,这些警告信息可以不用管。


2、基本介绍

什么是MongoDB?
MongoDB是一个文档数据库,提供好的性能,领先的非关系型数据库。采用BSON存储文档数据。
BSON是一种类json的一种二进制形式的存储格式,全称:Binary JSON。相对于json多了date类型和二进制数组。

为什么用MongoDB?
1、数据结构简单,没有固定的数据结构。
2、没有复杂的连接,没有复杂的外键约束。
3、深度查询能力,MongoDB支持动态查询,查询api是基于v8引擎(javascript)。
4、容易调试。
5、容易扩展。
6、不需要转化/映射应用对象到数据库对象。
7、使用内部内存作为存储工作区,以便更快的存取数据。

MongoDB的应用场景?
1、大数据
2、内容管理系统
3、移动端App
4、数据管理
5、日志处理
6、游戏道具处理

MongoDB相对于RDBMS的优势?
以存储游戏道具为例,存储的信息类似以下格式:

在这里插入图片描述
如果使用MongoDB存储,就是类似上面的JSON格式的字符串,而使用MySQL就是下面表的结构,会造成一定的空间浪费。

  • 无固定结构 。面向文档,以 JSON 格式的文档保存数据,数据结构由键值(key=>value)对组成。MongoDB 的文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组,单个对象的结构是清晰的。
  • 没有复杂的表连接。不需要维护表与表之间的内在关联关系。
  • 查询功能强大,丰富的查询语言。MongoDB的查询功能几乎与SQL一样强大,使用基于文档的查询语言,可以对文档进行动态查询,提供丰富的查询操作和索引支持,也有事务操作,可以更快地更稳定的访问数据。(mongoDB4.0以后才真正支持所谓的多文档事务操作)
  • 易于调优和扩展。具备高性能、高可用性及可伸缩性等特性
  • 应用程序对象与数据库对象天然对应。
  • 可分片,对数据存储友好,可以基于内存存储或者硬盘文件存储。
  • 任何属性都可以建立索引,而且提供丰富多样的索引。

术语对比:
在这里插入图片描述
主键在MongoDB中表示的是对象ID,这是基于时间生成的一个随机数。


3、通用操作

3.1、查看帮助文档

输入help可以查看帮助文档:

help

在这里插入图片描述

  • show profile:查看当前系统的环境配置
  • show users:查看当前数据库中所有的管理员用户
  • show roles:查看当前数据库中所有的管理员角色
  • show log <type>:查看指定日志信息,如果type没设置,默认为global
  • show logs:查看全部日志
  • sleep:睡眠函数,指定睡眠毫秒
  • load:加载指定js文件的js代码到当前mongo shell交互终端下
  • print:打印输出一个终端对象

比较重要的就是下面的:

  • db 查看当前用户所在数据库的数据库对象
  • use <db>:切换操作的数据库
  • show databases / show dbs:查看当前系统所有的数据库
  • show collections / show tables:查看当前数据库所有的数据集合
  • it:查看更多的查询结果,相当于下一页
  • exit / quit:退出终端
  • cls:清屏

3.2、当前服务器状态

db.serverStatus()

可以查看MongoDB版本,客户端连接数相关、全局锁相关、操作计数器相关信息等。


3.3、查看当前数据库的连接机器地址

db.getMongo()

在这里插入图片描述


3.4、数据备份与恢复

MongdoDB一共提供了4个命令行工具给我们对数据进行备份与恢复以及导入与导出数据。
备份与恢复操作的数据文件格式是bson格式,二进制文件,不需要进入mongo终端。
导入与导出数据的数据文件格式是json格式,文本文档格式,不需要进入mongo终端。

3.4.1、准备测试数据

mongosh
use test

// 声明了一个函数,生成一个指定随机整数,不足10,前面补0
formatnumber = (start, end)=>{
num = Math.round(Math.random() * (end-start)) + start
	if(num<10){
		return "0"+num;
	}else{
		return num;
	}
}

// 声明了一个函数,生成具有随机标题
rand_title = ()=>{
num = Math.round( Math.random() * 10 );
num1 = Math.round( Math.random() * 10 );
	return [
	"赠送礼品-"+num,
	"购物狂欢-"+num,
	"随便买买-"+num,
	"愉快购物-"+num,
	"赠送礼物-"+num,
	"商品购买-"+num,
	"买多送多-"+num,
	"买年货-"+num,
	"买买买买-"+num,
	"充值会员-"+num
	][num1];
}

// 创建一个函数,循环生成指定的数据
function rand_data(size=200000){
	for(var i=0; i<size; i++){
		// 往当前数据库的orders集合(相当于mysql的orders数据表) 添加1条数据
		db.orders.insertOne({
			"order_number": ( "0000000000000000" + i ).substr( String(i).length ),
		"date": "20"+formatnumber(0,21)+"-"+formatnumber(1,12)+"-"+formatnumber(1,31),
		"title": rand_title(i),
		"user_id": parseInt(i/200)+1,
		"items" :[{
				"goods_id" : parseInt(i/200)+1,
				"goods_number" : formatnumber(2, 10),
				"price" : formatnumber(50, 1000)
			},{
			"goods_id" : parseInt(i/200)+2,
			"goods_number" :formatnumber(2, 10),
			"price" : formatnumber(50, 1000)
			}]
		})
		// 判断循环过程中,i每逢1000则打印一次数据
		if(i%10000==0){
		print("已经添加了"+Math.ceil(i/10000)+"万条数据!");
		}
	}
}

// 调用上面生成测试数据的函数
rand_data()

// 查看上面生成的数据
db.orders.find()

// 每次显示的数据,mongoDB交互终端默认只会显示20条,所以如果要查看更多,则根据提示输入it可以查看下一页数据。

3.4.2、数据备份

mongodump -h dbhost -d dbname -o dbdirectory

在这里插入图片描述
例如上面备份test数据库:

mongodump -h 127.0.0.1:27017 -d test -o /home/zzy/MongoDB/

3.4.3、数据恢复

mongorestore -h dbhost -d dbname --dir dbdirectory --drop

在这里插入图片描述
如果说恢复的数据库test中有内容,我们可以在后面带上--drop。下面演示恢复之前备份的数据库:
我们先使用以下命令删除数据库:这是删除当前所在数据库,所以需要先use。

db.dropDatabase()

然后进行恢复:

mongorestore -h 127.0.0.1:27017 -d test --dir /home/zzy/MongoDB/test/ --drop

接着我们进入MongoDB客户端,进入数据库test,使用以下命令查看orders表的文档总数:

db.orders.countDocumets()

在这里插入图片描述
可以看到成功恢复了。


3.4.4、数据导出

mongoexport -h dbhost -d dbname -c collectionname -o file --type json/csv -f field

在这里插入图片描述
例如导出test数据库里面的orders集合:

mongoexport -h 127.0.0.1:27017 -d test -c orders -o /home/zzy/MongoDB/orders.json --type json

在这里插入图片描述
可以直接查看对应的orders.json文件,可以看到而十万条记录以json格式保存在文件中。


3.4.5、数据导入

mongoimport -h dbhost -d dbname -c collectionname --file filename --type json/csv --headerline -f field

在这里插入图片描述
将之前导出的orders.json导入demo数据库中的orders集合中:

mongoimport -h 127.0.0.1:27017 -d demo -c orders --file /home/zzy/MongoDB/orders.json --type json

4、用户管理

4.1、创建用户

db.createUser(user, pwd, writeConcern)

创建一个数据库新用户用db.createUser()方法,如果用户存在则返回一个用户重复错误。

db.createUser({
	user: "<用户名>",
	pwd: "<密码>",
	customData: { <any information> }, // 任意内容,主要是为了表示用户身份的相关介绍
	roles: [ // 角色和权限分配,一个用户可以绑定多个角色,也就是具备多个角色的权限
			{ role: "<角色名>", db: "<数据库名>" }, // 也可以直接填写由mongoDB内置的角色,例如: "<role>"
			...
	]
})

注意:
与之前学习的redis或MySQL不同,mongoDB的用户是以数据库为单位来分别建立和管理用户访问控制权限的,每个数据库有属于自己的一个或多个管理员。管理员可以管理自己的数据库,但是不能直接管理其他数据库,要先在内置数据库admin认证后才可以。管理员的权限设置包含了2块,分别是角色和权限,由创建用户时通过roles属性进行设置。

4.1.1、内置角色

也叫内建角色,是MongoDB安装以后默认创建提供给我们使用的。有以下这些角色:

  • 数据库用户角色:read、readWrite
  • 数据库管理角色:dbAdmin、dbOwner、userAdmin
  • 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager
  • 备份恢复角色:backup、restore
  • 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
  • 超级用户角色:root

超级用户角色 提供了系统超级用户的访问权限有几个角色间接或直接提供了系统超级用户的访问权限(dbAdmin、dbOwner 、userAdmin、userAdminAnyDatabase、dbAdminAnyDatabase)

4.1.2、内置权限

  • Read:允许用户读取指定数据库。
  • readWrite:允许用户读写指定数据库。
  • dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile。
  • userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户。
  • clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
  • readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限。
  • readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限。
  • userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限。
  • dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。
  • root:只在admin数据库中可用。超级账号,拥有超级权限,对所有数据具有超级权限。

给Admin数据库创建账户管理员:相当于分配了一个HR。

db.createUser({
	user: "admin",
	pwd: "123456",
	roles: [
		{role: "userAdminAnyDatabase",db:"admin"},
	]
});

创建超级管理员:

db.createUser({
	user: "root",
	pwd: "123456",
	roles: [
		{role:"root", db:"admin"}, // 也可以这样简写:"root",
	]
})

创建用户自己的数据库角色:帐号是跟着数据库绑定的,所以是什么数据库的用户,就必须先到指定库里授权和验证。一般开发中,往往一个项目就分配一个数据库,并给这个数据库分配一个管理员。

use yingmingapp

db.createUser({
	user: "yingmingapp",
	pwd: "123456",
	roles: [
		{ role: "dbOwner", db: "yingmingapp"}
	]
})

show users

4.2、用户信息

查看当前数据库的用户:需要先切换到对应的数据库。

show users

查看系统中所有的用户,需要切换到admin中使用账号管理员的权限进行操作:

use admin
// db.auth("root","123456") // 如果开启了mongoDB的访问控制,就需要下用户认证密码才能继续往下操作
db.system.users.find() // 只能在admin数据库中使用。
// db.system.users.find().pretty() // 在mongoDB5.0以前的版本中,pretty表示在返回多个查询结果时,以结构化格式显示数据

4.3、删除用户

db.system.users.remove(json条件)

下面演示删除yingmingapp用户:

use admin
db.system.users.deleteOne({user:"yingmingapp"});

4.4、修改密码

针对账户管理员或者超级管理员,需要在admin下修改,而其他数据库管理员则必须到对应数据库下才能修改。

db.changeUserPassword("账户名", "新密码");

比如将admin账户密码改为123:

db.changeUserPassword("admin", "123");

4.5、开启账户认证

开启账户认证功能,必须要修改配置文件,然后重启mongoDB才能生效。

sudo vim /etc/mongod.conf

在这里插入图片描述
找到security并去掉注释添加如图所示的内容,然后保存退出,重启mongod服务。

sudo systemctl restart mongod

此时重新进入mongodb的客户端,然后进入admin数据库中,再次查看用户信息,就会出现以下这种情况:
在这里插入图片描述
要求我们进行认证,我们使用db.auth进行认证:
在这里插入图片描述
密码输入错误就会认证失败,认证成功后就可以查看所有用户了。


5、数据库管理

显示所有数据库:空数据库不会显示。

show databases
show dbs

切换数据库:如果数据库不存在就创建。

use dbname

查看当前数据库名:

db // 等价于db.getName()

删除数据库:需要先进入对应的数据库中,也就是先use,然后再使用以下命令删除。

db.dropDatabase()

查看当前数据库统计信息:

db.stats()

在mongoDB中,最重要的核心是文档,如果一个库或者一个库下的集合中的文档全部被删除了,则这个库和这个集合就意味着不存在,会被mongoDB回收删除。


6、集合管理

6.1、创建固定集

MongoDB中的集合有两种:固定集和动态集。
一般工作中使用的是动态集,但是在mongoDB优化时,可以对部分数据转换成使用固定集来保存,性能更好,查询速度更快。
在mongodb中如果使用的动态集,其实不需要专门创建集合,直接添加文档,mongodb也会自动生成动态集合的。而固定集需要设置存储的文档上限数量,所以需要提前创建集合。

db.createCollection(
	<集合名称>, 
	{
		capped : <boolean>, 
		size : <bytes_size>, 
		max : <collection_size> 
	}
);

capped:当前创建的集合是否是固定集,固定集指限制固定数据大小的集合,当数据达到最大值会自动覆盖最早添加的文档内容。
size:如果capped值为True,则表示创建固定集,需要指定固定集合存储的最大字节数,单位:字节数。
max:如果capped值为True,则表示创建固定集,需要指定固定集合中包含文档的最大数量,单位:字节数。

db.createCollection不填写第二个参数则表示创建的是动态集,添加文档到不存在的集合中,mongodb会自动创建动态集合。

6.2、固定集使用操作

在test数据库中创建一个固定集,然后插入数据。

db.createCollection( "queue", { capped: true, size: 100000, max: 5 })

我们指定了queue这个集合最多5条文档,我们插入六条数据试试看:

db.queue.insert({name:"张三"})
db.queue.insert({name:"李四"})
db.queue.insert({name:"王五"})
db.queue.insert({name:"赵六"})
db.queue.insert({name:"田七"})

然后查看这个集合的所有数据:
在这里插入图片描述
我们再次插入一条,再次查看集合,发现最初插入的第一条数据被丢弃了。

固定集一般用于日志,历史记录中。固定集,会因为设置了max或size上限而出现文档被驱逐/出列的情况。后面新增超出部分的数据依旧被添加到集合中,但是最早添加的同等数据数据会被删除掉。

6.3、查看集合列表

show tables
show collections

6.4、删除集合

db.集合.drop()

6.5、查看集合

获取集合对象:

db.集合名称
db.getCollection("集合名称")

查看集合统计信息:

db.集合名称.stats()

7、文档管理

mongodb中,文档也叫 object/document。对应的就是存储的bson数据记录,对应的就是python中的字典或者列表。mongodb中,允许文档中有各种的自定义字段,每一个字段对应的值也存在不同数据格式,根据不同的格式产生不同的数据类型。

7.1、数据类型

  • ObjectID:用于存储文档的ID,相当于主键,区分文档的唯一字段,mongoDB中就是一个ObjectID对象的返回值。一共由3部分组成:4个字节的时间戳、5个字节的客户端进程生成的随机数、3个字节的增量计数器,一共12字节长度的十六进制数,以此保证了每一个文档的唯一性。
  • String:字符串是最常用的数据类型,MongoDB中的字符串必须是UTF-8编码。
  • Integer:整数类型用于存储数值。整数可以是32位,也可以是64位,这取决于你的服务器。
  • Double:双精度类型,用于存储浮点值,mongodb中没有float浮点数这个说法。
  • Boolean:布尔类型用于存储布尔值(true/ false),注意:是小写的!!!
  • Arrays:将数组、列表或多个值存储到一个键,[]。
  • Timestamp:时间戳,用于记录文档何时被修改或创建。Date(),Timestamp(),ISODate() ,默认是ISODate()
  • Date:用于以UNIX时间格式存储当前日期或时间。
  • Object:用于嵌入文档,相当于子属性是另一个json文档而已,这种方式就可以实现嵌套。{}
  • Null:空值。
  • Symbol:与字符串用法相同,常用于某些使用特殊符号的语言,可以理解为一种二进制格式字符串。
  • Binary data:二进制数据,常用于保存文件的内容,往往是图片、音频、视频等数据本身。
  • Regular expression:正则表达式。
  • Code:用于将JavaScript代码存储到文档中。

虽然,MongoDB中提供了数据类型,但是mongoDB中对于文档的添加,是不需要预先约束字段值类型的,而是一种自动推断类型。因此,上面的这些类型,我们知道即可。

7.2、添加文档

mongodb中,文档的数据结构和 JSON 基本一样。所有存储在集合中的数据在内部存储的格式都是 BSON 格式。BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。

// 添加文档
// 方式1
db.<集合名称>.insertOne( // 如果文档存在_id主键为更新数据,否则就添加数据。
	<document>
)
// 方式2:
// 一次性添加多个文档, 多次给同一个集合建议使用insertMany比insertOne效率更好
db.<集合名称>.insertMany([
	<document>,
	<document>,
	...
])

方式一:insertOne插入一条文档。

db.users.insertOne({
	"name": "laoli", // string
	"age": 33, // integer
	"sex": true, // boolean
	"child": { // Object
		"name":"xiaohuihui", // string
		"age":6 // integer
	}
});

mangoDB原则上使用了js解释引擎,所以支持js的语法。
在这里插入图片描述

方式二:insertMany插入多条文档。

db.users.insertMany([{"name":"xiaolan", "age":16}, {"name":"xiaoguang", "age":16}])

在这里插入图片描述


7.3、删除文档

// 方式1: 删除一条数据
db.<集合名称>.deleteOne(
	<filter>, // removed的条件表达式,一般写法:{"字段":{查询器函数:值}},如果不填写删除条件,删除所有文档
) 
// 方式2:删除多条数据
db.<集合名称>.deleteMany(
	<filter>, // removed的条件表达式,一般写法:{"属性":{条件:值}},如果不填写条件,删除所有文档
)

添加多条测试数据:

document1 = {"name":"xiaohei","age":16}
document2 = {"name":"xiaobai","age":15}
document3 = {"name":"xiaolan","age":18}
document4 = {"name":"xiaohui","age":11}
document5 = {"name":"xiaoming","age":13}
db.users.insertMany([document1,document2,document3,document4,document5]);

删除满足条件的第一条数据:

db.users.deleteOne({"age":{$eq:16}})
db.users.deleteOne({"age":16});

上面两种写法一致,因为等于可以省略不写。

删除满足条件的多条数据:

db.users.deleteMany({"age":{$eq:16}})

7.4、查询文档

7.4.1、基本操作

// 直接显示查询的所有,find和findOne的第二个参数,也是一个json对象,一般称之为字段投影,表示设置是否显示或隐藏指定数据字段。
// 获取一条
db.集合.findOne(
	<query>// 查询条件,删除、查询、修改都需要设置条件、条件写法基本一样的。
	{ 
		// 查询结果的字段投影,用于指定查询结果以后,显示的字段列
		<field>: 0, // 隐藏指定字段,例如:"_id":0,
		<field>: 1, // 显示指定字段,例如:"title":1,
		....
	}
) 

// 获取多条
db.集合.find(
	<query>, // 查询条件
	{
		<key>: 0, // 隐藏指定字段,例如:"_id":0,
		<key>: 1, // 显示指定字段,例如:"title":1,
		....
	}
) 
// 以易读的方式来格式化显示读取到的数据,只能在find方法后面使用。mongoDB6.0版本以后。默认以易读的方式来格式化显示
db.集合.find().pretty()

查询整个集合中所有数据:

db.users.find()
db.users.find({})

查询第一条数据:

db.users.findOne()
db.users.findOne({})

字段投影:find() 方法默认将返回文档的所有数据,但是可以通过设置 find() 的第二个参数projection,设置值查询部分数据。

// 设置字段投影,显示结果中字段列
db.users.find({}, {"child": 0}) // 获取集合中所有数据,并隐藏child属性的数据
db.users.find({}, {"_id": 0, "child": 0}) // 获取集合中所有数据,并隐藏_id 与 child属性的数据
db.users.find({}, {"name": 1}) // 获取集合中所有数据,并只显示name属性与_id主键的字段数据[注意:_id如果不明确隐藏,默认显示的]
db.users.find({}, {"name": 1, "_id": 0}) // 获取集合中所有数据,并只显示name属性的字段数据

7.4.2、条件运算符

条件运算符:mongoDB中,条件运算符也叫查询器(query selector)

一、基础比较操作符

操作 MongoDB 语法 MongoDB 示例 SQL 等效语句
等于 {字段: 值}{$eq} db.users.find({name: "小明"}) WHERE name = '小明'
小于 {字段: {$lt: 值}} db.users.find({age: {$lt: 18}}) WHERE age < 18
大于 {字段: {$gt: 值}} db.users.find({age: {$gt: 18}}) WHERE age > 18

二、范围操作符

操作 MongoDB 语法 MongoDB 示例 SQL 等效语句
小于等于 {字段: {$lte: 值}} db.users.find({age: {$lte: 18}}) WHERE age <= 18
大于等于 {字段: {$gte: 值}} db.users.find({age: {$gte: 18}}) WHERE age >= 18
不等于 {字段: {$ne: 值}} db.users.find({age: {$ne: 18}}) WHERE age != 18

三、集合操作符

操作 MongoDB 语法 MongoDB 示例 SQL 等效语句
包含 {字段: {$in: [值1,值2]}} db.users.find({age: {$in: [18,20]}}) WHERE age IN (18,20)
不包含 {字段: {$nin: [值1,值2]}} db.users.find({age: {$nin: [18,20]}}) WHERE age NOT IN (18,20)

四、逻辑操作符

操作 MongoDB 语法 MongoDB 示例 SQL 等效语句
{$and: [{条件1}, {条件2}]} db.users.find({$and: [{age:18}, {name:"小明"}]}) WHERE age=18 AND name='小明'
{$or: [{条件1}, {条件2}]} db.users.find({$or: [{age:18}, {age:20}]}) WHERE age=18 OR age=20
{$not: {条件}} db.users.find({age: {$not: {$lt: 18}}}) WHERE NOT age < 18

添加测试数据:

db.users.insertOne({"name": "laozhang", "age": 33, "sex": true, child: {name: "xiaozhang", "age": 4}})
db.users.insertOne({"name": "laowang", "age": 32, "sex": true, child: {name: "xiaowang", "age": 2}})
db.users.insertOne({"name": "laoli", "age": 33, "sex": true, child: {name: "xiaoli", "age": 1}})
db.users.insertOne({"name": "laosun", "age": 34, "sex": true, child: {name: "xiaosun", "age": 7}})
db.users.insertOne({"name": "laozhao", "age": 32, "sex": true, child: {name: "xiaozhao", "age": 6}})
db.users.insertOne({"name": "laoyang", "age": 35, "sex": true, child: {name: "xiaoyang", "age": 4}})
db.users.insertOne({"name": "laohuang", "age": 36, "sex": true, child: {name: "xiaohuang", "age": 3}})
db.users.insertOne({"name": "zhangsanfeng"})
// 查询年龄大于33岁的用户信息
db.users.find({"age":{$gt:33}})
// 查询年龄小于33岁的用户信息
db.users.find({"age":{$lt:33}})
// 查询年龄在32,33,34岁范围里面用户信息
db.users.find({"age":{$in:[32,33,34]}})

注意:没有当前字段的文档是不会被通过大于、小于或等于查询出来,因为默认条件不成立。
所以依靠不存在的字段,是无法使用比较运算符查询出上面添加zhangsanfeng的。

db.users.find({"age":{$nin:[33]}}) // 使用排除范围的方式,可以查询出来。
db.users.find({"age":{$ne: 33}}) // 使用不等于的方式,可以查询出来。

逻辑运算符和其他共同使用:
$and:

SQL:
	(class=301 and sex =true) or (class=302 and sex=false)
mongo:
{
	$or: [
	{$and: [{class:301}, {sex:true}]},
	{$and: [{class:302}, {sex:false}]},
	]
}
// 查询age>34 并且 child.age<5
db.users.find({
	$and:[
		{"age":{$gt:34}},
		{"child.age":{$lt: 5}}
	]
})
// 简写;
db.users.find({"age": {$gt:34}, "child.age": {$lt:5}})


// 查询age=35,child.age=4
db.users.find({
	$and:[
		{"age":{$eq:35}},
		{"child.age":{$eq: 4}}
	]
})
// 简写
db.users.find({
	$and:[
		{"age": 35},
		{"child.age": 4}
	]
})
// 再次简写:
db.users.find({
	"age": 35,
	"child.age": 4,
})

$or、$and与$or:

// 查询age=33或者age=36
db.users.find({
	$or: [
		{"age":{$eq:33}},
		{"age":{$eq:36}}
	]
})
// 简写:
db.users.find({
	$or:[
		{"age":33},
		{"age":36}
	]
})

// 查询age=33,child.age==5 或者 age=35,child.age==4
db.users.find({
	$or: [
		{$and: [{age: {$eq: 33}}, {"child.age": {$eq: 5}}]},
		{$and: [{age: {$eq: 35}}, {"child.age": {$eq: 4}}]},
	]
})
// 简写:
db.users.find({
	$or: [
		{$and: [{age: 33}, {"child.age": 5}]},
		{$and: [{age: 35}, {"child.age": 4}]},
	]
})
// 再次简写:
db.users.find({
	$or: [
		{age: 33, "child.age": 5},
		{age: 35, "child.age": 4},
	]
})

$not:

// 查询年龄!=16的
db.users.find({"age":{$not:{$eq:33}}})
// 简写:
db.users.find({"age":{$ne: 33}})

五、MongoDB 高级查询操作符参考表

操作符 格式 语法示例 说明
$type { <key>: { $type: <datatype> }} db.集.find({ "name": { $type: "string" } }) 匹配指定键是指定数据类型的文档
常见类型:number 数值型,string 字符串,bool 布尔型,object 对象类型,array 数组类型
$exists { <key>: { $exists: <bool> }} db.集.find({ "title": { $exists: true } }) 匹配具有指定键的文档,判断字段是否存在
$regex { <key>: /正则/ }
{ <key>: { $regex: /正则/ }}
db.集.find({ "name": { $regex: /张$/ } }) 使用正则表达式进行字段匹配
$mod { <key>: { $mod: [除数, 余数] }} db.集.find({ "age": { $mod: [10, 0] } }) 取模匹配,例子表示 age % 10 == 0
db.users.insert({"name":"xiaoming","sex":0,"age":"18","mobile": "13313621234"});
db.users.insert({"name":"xiaoming","sex":1,"age":18,"mobile": "13441351234"});
db.users.insert({"name":"xiaoming","sex":1,"age":33,"mobile": "13851351234"});
db.users.insert({"name":"xiaoming","sex":0,"age":"33","mobile": "13881351234", child: {"age": 3, "sex": true}});
db.users.insert({"name":"xiaoming","sex":1,"age":33,"mobile": "10086"});

// $type的使用
db.users.find({"sex":{$type:"number"}});
db.users.find({"sex":{$type:"bool"}});
db.users.find({"age":{$type:"string"}});
// $exists
db.users.find({"child":{$exists:true}}); // 查询出存在child字段的数据
// $regex 正则匹配
db.users.find({"mobile":{$regex: /^133/ }});
// 不符合手机号码格式的
db.users.find({"mobile":{$not:{$regex: /1[3-9]\d{9}/ }}});
// $mod 取模,注意:仅针对数值类型,对于字符串是不识别的,所以$mod[3,0],对于 "18"来说,并非整除!
db.users.find({"age":{$mod: [3,0] }});

六、自定义条件查询函数

// 用法1,逻辑比较复杂的情况,可以使用更多的javascript进行运算处理:结果函数结果为true,则当前数据被查询出来。
db.<集合名称>.find({$where: function(){ // this代表的就是查询过程中,被循环的每一条文档数据
	return <this.字段> <运算符> <条件值>;
}});

// 用法2,相对没那么复杂的,取函数的返回值作为条件值:
db.集合.find({$where: "<this.字段> <运算符> <条件值>"});
// db.集合.find({$where:"this.name=='xiaoming'"});

操作:

db.users.find({$where: function(){
	return this.age>33 && this.child.age<4;
}});
// 把字符串作为代码条件执行,当结果为true,则返回当前符合的数据
db.users.find({$where: "this.age>33 && this.child.age<4"});

7.4.3、排序显示

db.集合.find().sort({<key>:1}) // 升序,默认为升序
db.集合.find().sort({<key>:-1}) // 倒序

操作:

db.users.find().sort({age:-1});
db.users.find().sort({age:-1, "child.age":-1});

7.4.4、限制与偏移

limit方法用于限制返回结果的数量。skip方法用于设置返回结果的开始位置。

db.集合.find(...).limit(结果数量).skip(开始下标)

操作:

db.users.find().sort({age:-1}).limit(3); // 年龄最大的三个人
db.users.find().sort({age:-1}).limit(3).skip(3) // 按年龄倒序排序,找到排名在4-6之间的用户
db.users.find({},{"_id":0,"name":1,"age":1}).sort({"age":1}).limit(5); // 年龄升序排序,第1-5名
db.users.find({},{"_id":0,"name":1,"age":1}).sort({"age":1}).limit(5).skip(0);// 年龄升序排序,第一页,第1-5名
db.users.find({},{"_id":0,"name":1,"age":1}).sort({"age":1}).limit(5).skip(5);// 年龄升序排序,第二页,第6-10名

7.5、更新文档

使用语法如下:

// 更新一条
db.集合.updateOne(
	<query>, //update的查询条件,一般写法:{"属性":{条件:值}}
	<update>, // update的更新数据,一般写法 { $set:{"属性":"值",....} } 或者 { $inc:{"属性":"值"} }
	{
		upsert: <boolean>, // 可选参数,如果文档不存在,是否插入objNew, true为插入,默认是false,不插入
		multi: <boolean>, // 可选参数,是否把满足条件的所有数据全部更新,设置更新1条还是多条
		writeConcern: <document> // 可选参数,抛出异常的级别。
	}
)
// 更新多条
db.集合.updateMany(
	<query>, // update的查询条件,一般写法:{"属性":{条件:值}}
	<update>, // update的对象,一般写法 { $set:{"属性":"值"} } 或者 { $inc:{"属性":"值"} }
	{
		upsert: <boolean>, // 可选参数,如果文档不存在,是否插入objNew, true为插入,默认是false,不插入
		multi: <boolean>, // 可选参数,是否把满足条件的所有数据全部更新
		writeConcern: <document> // 可选参数,抛出异常的级别。
	}
)

// 替换一条数据
db.collection.replaceOne(
	<filter>,
	<replacement>,
	{
		upsert: <boolean>, // 可选参数,如果文档不存在,是否插入objNew, true为插入,默认是false,不插入
		writeConcern: <document>, // 可选参数,抛出异常的级别。
		collation: <document>,
	}
)

update更新运算符[修改器]:

操作符 语法 说明
$inc db.collection.updateOne(filter, { $inc: { <key2>: <val2> } }) 对匹配的文档中 <key2> 字段执行增量操作,<val2> 可为正值(增)或负值(减)。
$set db.collection.updateOne(filter, { $set: { <key2>: <val2> } }) 在匹配文档中设置或更新 <key2><val2>;若字段不存在则会新增。
$unset db.collection.updateOne(filter, { $unset: { <key2>: "" } }) 移除匹配文档中的 <key2> 字段。
$push db.collection.updateOne(filter, { $push: { <key2>: <val2> } }) 向匹配文档的数组字段 <key2> 追加元素 <val2><key2> 必须是数组。
$addToSet + $each db.collection.updateOne(filter, { $addToSet: { <key2>: { $each: [<val2>, <val3>, …] } } }) 批量向数组字段 <key2> 添加多个元素;已有的元素不会重复添加。
$pull db.collection.updateOne(filter, { $pull: { <key2>: <val2> } }) 从匹配文档的数组字段 <key2> 中移除所有等于 <val2> 的元素。
$pullAll db.collection.updateOne(filter, { $pullAll: { <key2>: [<val2>, <val3>, …] } }) 从匹配文档的数组字段 <key2> 中一次性移除多个指定元素。
$pop db.collection.updateOne(filter, { $pop: { <key2>: <val> } }) 从匹配文档的数组字段 <key2> 中移除一个元素:传入 1 则移除最后一个,传入 -1 则移除第一个。
$rename db.collection.updateOne(filter, { $rename: { <key2>: <key3> } }) 将匹配文档中的字段 <key2> 重命名为 <key3>

操作如下:

// $inc
// 把符合条件的用户的年龄+2岁
db.users.updateOne({"name":"laoli"},{$inc:{"age": 2}}); // 更新一条
db.users.updateMany({"name":"laoli"},{$inc:{"age":2}}); // 更新多条
// 把符合条件的用户的孩子年龄+2岁
db.users.updateMany({"name":"laoli"},{$inc:{"child.age":2}}); // 更新多条

// $set
// 如果字段不存在,则新增字段的键值对,如果字段存在,则修改字段的值
// 更新laoli的手机号码
db.users.updateOne({"name":"laoli"},{$set:{"mobile":"18012312312"}}); // 更新一条
// 重新设置年龄
db.users.updateOne({"name":"laoli"},{$set:{"age":18}});

// $unset
// 移除laoli的性别键值对
db.users.updateOne({"name": "laoli"}, {$unset: {"sex":true}})

// $push 往数组字段的值追加成员
db.users.updateOne({"name": "laoli"}, {$push: {"lve": "TV"}}) // 不存在的字段,自动设置成新字段,并把TV作为数组成员。 lve:[ 'TV' ]
db.users.updateOne({"name": "laoli"}, {$push: {"lve":"game"}}) // 已存在的字段,自动追加成员到数组中。lve: [ 'TV','game' ]
db.users.updateOne({"name": "laoli"}, {$push: {"lve":["code"]}}) // 所有设置的值,都会作为成员被追加数组中作为成员,lve: ['TV', 'game', [ 'code' ] ]

// $addToSet + $each.  批量向数组字段添加多个元素;已有的元素不会重复添加。
db.users.updateOne({"name":"laoli"}, {$addToSet: {"lve": {$each: ['game', ['code']]}}})

// $pull 从数组属性中移除成员
db.users.updateOne({"name":"laoli"}, {$pull:{"lve": "TV"}})
// lve: [ 'game', [ 'code' ] ]
db.users.updateOne({"name":"laoli"}, {$pull:{"lve":["code"]}}) // lve: [ 'game' ]
// $addToSet 结合 $each 把多个成员添加到数组属性中
db.users.updateOne({"name": "laoli"}, {$addToSet:{"lve":{$each: ["shoping", "walk"]}}})

// $pullAll 移除多个成员
db.users.updateOne({"name": "laoli"}, {$pullAll: {"lve":["game", "walk"]}})
// 添加多个测试数据
db.users.updateOne({"name": "laoli"}, {$addToSet:{"lve":{$each: ["shoping", "walk", "TV", "game"]}}})
// $pop
db.users.updateOne({"name":"laoli"}, {$pop: {"lve": -1}}) //左边移除列表的第一个成员
db.users.updateOne({"name":"laoli"}, {$pop: {"lve": 1}}) //右边移除列表的最后一个成员
// $rename 字段名/属性名 重命名
db.users.updateOne({"name": "laoli"}, {$rename: {"lve":"love"}})
// replaceOne 替换数据
db.users.replaceOne({"name": "laoli"}, {"name":"xiaoli",
"age": 21, "sex": false}) // 除了主键,全部被替换,如果有声明主键,也能替换

8、索引操作

我们知道数据库里给数据构建索引通常能够极大的提高数据查询的效率,缩短查询耗时,如果没有索引,数据库在查询数据时必然会扫描数据表中的每个记录并提取那些符合查询条件的记录。同理,在MongoDB中构建索引也可以提高数据的查询效率和缩短查询耗时,没有索引的情况也是一样,MongoDB也会再查询数据时扫描集合中的每个文档并提取符合查询条件的文档。这种扫描全集合的查询效率是无疑是非常低下的,特别在处理大量的集合数据时,查询时间可能会达到几十秒甚至几分钟,这对用户体验来说是非常致命的。

8.1、准备数据

// 进入mongo交互终端
mongosh
use test 

// 声明了一个函数,生成一个指定随机整数,不足10,前面补0
formatnumber = (start, end)=>{
	num = Math.round(Math.random() * (end-start)) + start
	if(num<10){
	return "0"+num;
	}else{
	return num;
	}
} 

// 声明了一个函数,生成具有随机标题
rand_title = ()=>{
	num = Math.round( Math.random() * 10 );
	num1 = Math.round( Math.random() * 10 );
	return [
	"赠送礼品-"+num,
	"购物狂欢-"+num,
	"随便买买-"+num,
	"愉快购物-"+num,
	"赠送礼物-"+num,
	"商品购买-"+num,
	"买多送多-"+num,
	"买年货-"+num,
	"买买买买-"+num,
	"充值会员-"+num
	][num1];
} 
// 创建一个函数,循环生成指定的数据
function rand_data(size=200000){
	for(var i=0; i<size; i++){
		// 往当前数据库的orders集合(相当于mysql的orders数据表) 添加1条数据
		db.orders.insertOne({
		"order_number": ( "0000000000000000" + i).substr( String(i).length ),
		"date": "20"+formatnumber(0,21)+"-"+formatnumber(1,12)+"-"+formatnumber(1,31),
		"title": rand_title(i),
		"user_id": parseInt(i/200)+1,
		"items" :[{
				"goods_id" : parseInt(i/200)+1,
				"goods_number" : formatnumber(2, 10),
				"price" : formatnumber(50, 1000)
			},
			{
				"goods_id" : parseInt(i/200)+2,
				"goods_number" :formatnumber(2, 10),
				"price" : formatnumber(50, 1000)
			}]
		})
		// 判断循环过程中,i每逢1000则打印一次数据
		if(i%10000==0){
			print("已经添加了"+Math.ceil(i/10000)+"万条数据!");
		}
	}
} 
// 调用上面生成测试数据的函数
rand_data()
// 查看上面生成的数据
db.orders.countDocuments()
// 每次显示的数据,mongoDB交互终端默认只会显示20条,所以如果要查看更多,则根据提示输入it可以查看下一页数据。

8.2、注意事项

1、MongoDB的索引是存储在运行内存(RAM)中的,所以必须确保索引的大小不超过内存的限制。如果索引的大小超过了运行内存的限制,MongoDB会删除一些索引【这会涉及到mongoDB的驱逐机制,这将导致性能下降】。
2、MongoDB的索引在部分查询条件下是不会生效的。正则表达式及非操作符,如 $nin , $not , 等。算术运算符,如 $mod, 等。$where自定义查询函数。…
3、索引会在写入数据(添加、更新和删除)时重排,如果项目如果是写多读少,则建议少使用或者不要使用索引。
4、一个集合中索引数量不能超过64个。
5、索引名的长度不能超过128个字符。
6、一个复合索引最多可以有31个字段。
7、mongodb索引统一在system.indexes集合中管理。这个集合只能通过createIndex和dropIndexes来操作。

8.3、查看索引

// 获取当前集合中已经创建的所有索引信息
db.集合.getIndexes()
/*
[{
	"v" : 2, // 索引版本
	"key" : { // 索引的字段及排序方向(1表示升序,-1表示降序)
		"_id" : 1 // 根据_id字段升序索引
	},
	"name" : "_id" // 索引的名称
}]
*/
use admin
db.auth("root", "123456")
use test
// 获取当前集合中已经创建的索引总大小,以字节为单位返回结果
db.users.totalIndexSize()

MongoDB会为插入的文档默认生成 _id 字段(如果文档本身没有指定该字段),_id是文档唯一的主键,为了保证能根据文档id快速查询文档,MongoDB默认会为集合创建_id字段的主键索引。

8.4、查询分析

与SQL语句类似,MongoDB也提供了一个explain,供开发者进行查询分析,优化查询语句。explain的使用有3个参数,分别是:queryPlanner、executionStats、allPlansExecution,默认是queryPlanner,开发中常用的是executionStats。

db.orders.find({"title":"愉快购物-6"}).explain("executionStats")
{
  explainVersion: '1', 
  queryPlanner: {   # 被查询优化器选择出来的查询计划
    namespace: 'test.orders',  # 要查询的集合
    parsedQuery: { title: { '$eq': '愉快购物-6' } },
    indexFilterSet: false,   # 是否了使用索引
    queryHash: '79F2D0D9',  # 查询缓存的key值
    planCacheShapeHash: '79F2D0D9',
    planCacheKey: '3403A35D',
    optimizationTimeMillis: 0,
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,
    prunedSimilarIndexes: false,
    winningPlan: {  # 最佳执行计划
      isCached: false,
      stage: 'COLLSCAN',    # 扫描类型/扫描阶段
      filter: { title: { '$eq': '愉快购物-6' } },   # 过滤条件
      direction: 'forward'
    },
    rejectedPlans: []
  },
  executionStats: {
    executionSuccess: true,
    nReturned: 4001,
    executionTimeMillis: 178,
    totalKeysExamined: 0,
    totalDocsExamined: 400000,
    executionStages: {
      isCached: false,
      stage: 'COLLSCAN',
      filter: { title: { '$eq': '愉快购物-6' } },
      nReturned: 4001,
      executionTimeMillisEstimate: 163,
      works: 400001,
      advanced: 4001,
      needTime: 395999,
      needYield: 0,
      saveState: 9,
      restoreState: 9,
      isEOF: 1,
      direction: 'forward',
      docsExamined: 400000
    }
  },
  queryShapeHash: 'A54D40C16E18D0F6EC2C4226050C831EE858ADDA1CB22C0911416E98E1BE8338',
  command: { find: 'orders', filter: { title: '愉快购物-6' }, '$db': 'test' },
  serverInfo: {
    host: 'VM-24-8-ubuntu',
    port: 27017,
    version: '8.0.10',
    gitVersion: '9d03076bb2d5147d5b6fe381c7118b0b0478b682'
  },
  serverParameters: {
    internalQueryFacetBufferSizeBytes: 104857600,
    internalQueryFacetMaxOutputDocSizeBytes: 104857600,
    internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
    internalDocumentSourceGroupMaxMemoryBytes: 104857600,
    internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
    internalQueryProhibitBlockingMergeOnMongoS: 0,
    internalQueryMaxAddToSetBytes: 104857600,
    internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
    internalQueryFrameworkControl: 'trySbeRestricted',
    internalQueryPlannerIgnoreIndexWithCollationForRegex: 1
  },
  ok: 1
}

stage的扫描类型:
在这里插入图片描述

8.5、创建索引

MongoDB支持多种类型的索引,包括普通索引(也叫单列索引或单字段索引)、复合索引(也叫多字段索引)、多列索引(也叫数组索引)、全文索引、哈希索引、地理位置索引等,每种类型的索引有不同的使用场合。除此之外,还有一种特殊的ttl索引,ttl索引本质上就是普通索引,只是给索引添加一个过期时间而已。另外MongoDB的全文索引很弱智,如果真要用在开发中,还是建议使用elasticsearch或者Sphinx。

// 创建索引
db.集合.createIndex({
	// 单个字段,则为普通索引, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
	"字段名1": <sort|type>, // type的值可以是text,表示创建全文索引。db.集合.find({$text:{$search:"字符串"}})
	"字段名2": <sort|type>, // 多个字段,则为复合索引
	"字段名3": [<1>,<2>,...], // 多列索引
	....
	}, {
	background: <Boolean>, // 建索引过程会阻塞数据库的其它操作,background可指定以后台方式创建索引,默认为false
	unique: <Boolean>, // 是否建立唯一索引,默认值为false,也叫唯一索引
	name: <String>, // 索引的名称,不填写,则MongoDB会通过连接索引的字段名和排序顺序生成一个索引名称
	expireAfterSeconds: <integer>, // 设置索引的过期时间,类似redis的expire,也叫TTL索引
	sparse: <Boolean>, // 对文档中不存在的字段数据是否不启用索引,默认为False
});

// 单字段索引[普通索引]
db.集合.createIndex({
	"字段名": <sort>, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
	}, {
	....
})
// 普通索引创建: 
db.orders.createIndex({"order_number":1})
// 查询基本使用:
db.orders.find({"order_number":"0000000000030014"}).explain("executionStats");

// 多字段索引,也叫复合索引。[类似mysql里面的联合索引]
db.集合.createIndex({
	"字段名1": <sort>, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
	"字段名2": <sort>, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
	"字段名3": <sort>, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
	}, {
	....
})
// 复合索引的使用对单字段条件的查找是没有帮助的,必须多字段[必须包含复合索引的字段]条件使用
// 复合索引创建:
db.orders.createIndex({"date":1,"user_id":1});
// 查询基本使用:
db.orders.find({"date":"2002-09-19","user_id":751}).explain("executionStats"); // 全字段匹配,走索引
db.orders.find({"date":"2014-06-12","order_number":"0000000000030014"}).explain("executionStats"); // 靠左匹配原则,走索引
// 不走索引
db.orders.find({"user_id":751}).explain("executionStats");


// 全文索引
db.集合.createIndex({
	"字段名1": "text", // type的值只能是text,表示创建全文索引。db.集合.find({$text:{$search:"字符串"}})
	}, {
	....
})
// 全文索引创建: 
db.orders.createIndex({"title":"text"});
// 查询基本使用: 
db.orders.find({$text:{$search:"充值会员-1"}}).explain("executionStats")
// 多列索引[应用的地方是在数组属性]
db.集合.createIndex({
	"字段名3": [<1>,<2>,...],
	}, {
	....
});

下面进行实操:

// 创建测试数据
db.doc.drop()
db.doc.insert({"title":"标题1","tags":["python","django"]})
db.doc.insert({"title":"标题1","tags":["python","django"]})
db.doc.insert({"title":"标题1","tags":["python","django"]})
db.doc.insert({"title":"标题2","tags":["java","mvp"]})
db.doc.insert({"title":"标题3","tags":["java","mvp"]})
db.doc.insert({"title":"标题2","tags":["java","mvp"]})
db.doc.insert({"title":"标题3","tags":["python"]})
db.doc.insert({"title":"标题4","tags":["python"]})
db.doc.insert({"title":"标题2","tags":["python","flask"]})
db.doc.insert({"title":"标题3","tags":["java"]})
// 创建单列索引: 
db.doc.createIndex({"tags":1})
// 查询数据: 
db.doc.find({"tags":["python"]}).explain("executionStats")

// 唯一索引
db.集合.createIndex({
	"字段名1": <sort>,
	}, {
	unique: true, // 是否建立唯一索引,默认值为false,也叫唯一索引
})
// 创建唯一索引: 
db.orders.createIndex({"order_number":1},{unique:true, "name": "order_number_unique_1"});
// 查询数据:
db.orders.find({"order_number":"0000000000001019"}).explain("executionStats")

// ttl索引
// 使用ttl索引,索引关键字段的值类型必须是Date类型,如果该字段不是date类型或者文档中不存在该字段,则文档不会进行过期处理
// 数据过期的删除工作是在mongoDB中的独立线程内执行的,默认平均60s扫描一次有几率删除,不会立即删除。
// 例如:在文档创建10秒后删除文档
db.orders.createIndex({"date": 1},{expireAfterSeconds:10});
db.orders.insertOne({
	"date": new Date("2022-01-10 17:30:00"),
	"user_id": 2,
	"username": "xiaohong"
})
db.orders.insertOne({
	"date": new Date("2022-10-12 19:30:00"),
	"user_id": 3,
	"username": "xiaolv"
})

// 在文档创建后,由索引字段值指定的时间删除文档
// 创建索引:db.tasks.createIndex({"expire_time":1},
{expireAfterSeconds:0})
// 创建测试数据
db.tasks.insertOne( {
	"expire_time": new Date('2022-10-12 18:14:05'), 
	"user_id": 2,
	"username": "xiaoming",
});
db.tasks.insertOne( {
	"expire_time": new Date('2022-10-12 20:16:05'), 
	"user_id": 2,
	"username": "xiaoming"
});
db.tasks.insertOne( {
	"expire_time": new Date('2022-10-12 18:20:10'), 
	"user_id": 3,
	"username": "xiaoming"
});

// 重建索引[一般是在长期项目运行下来,索引创建时间太久了,性能下降的时候使用。]
// !!!!不能在高峰期时运行以下操作,会出现阻塞
db.集合.reIndex();

8.6、删除索引

MongoDB给文档主键 _id 默认创建单字段索引是无法删除的。

// 删除单个索引
db.集合.dropIndex("索引名称")
// db.orders.dropIndex("date_1")

// 删除所有索引(除了主键_id),慎用
db.集合.dropIndexes()