MongoDB
一、 MongoDB 简介
1 什么是 MongoDB
MongoDB 是一个基于分布式文件存储的数据库。由 C++语言编写。在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 json 的 bson 格式,因此可以存储比较复杂的数据类型。Mongo 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
2 什么是 NoSQL
NoSQL(NoSQL = Not Only SQL )
,意即“不仅仅是 SQL”,是一项全新的数据库革命性运动,早期就有人提出,发展至 2009 年趋势越发高涨。NoSQL 的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。
3 NoSQL 数据库的分类
3.1键值(Key-Value)存储数据库
这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value
模型对于 IT 系统来说的优势在于简单、易部署。但是如果 DBA 只对部分值进行查询或更新的时候,Key/value
就显得效率低下了。例如: Redis
3.2列存储数据库
这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。如:HBase。
3.3文档型数据库
文档型数据库的灵感是来自于 Lotus Notes 办公软件的,而且它同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如 JSON。文档型数据库可 以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高。如:CouchDB, MongoDB. 国内也有文档型数据库 SequoiaDB,已经开源。
3.4图形(Graph)数据库
图形结构的数据库同其他行列以及刚性结构的 SQL 数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSQL 数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多 NoSQL 数据库都有 REST 式的数据接口或者查询 API。
如:Neo4J, InfoGrid。
二、 MongoDB 与关系型数据库对比
1 与关系型数据库术语对比
2 存储数据对比
3 RDBMS 与 MongoDB 对应的术语
三、 MongoDB 的数据类型
四、 MongoDB 的下载与安装
1 下载 MongoDB
下载地址:https://www.mongodb.com/download-center/community
2 安装 MongoDB
2.1.1下载 ForLinux 平台的 MongoDB
2.2Linux 安装
在 Linux 平台的 MongoDB 为解压版。我们只要解压 tgz
文件就可以使用。
2.2.1将下载的 tgz 包上传到 Linux 环境中
我将 tgz
包上传到了自己创建的temp
目录中。该目录位于/root
目录中。
2.2.2解压 tgz 文件
通过 tar
命令对tgz
文件做解压处理。
2.2.3移动 MongoDB
我们将解压完的 MongoDB 目录移动到/usr/local
目录中并改名为 mongodb。
2.2.4创建数据库目录
MongoDB 的数据存储在 data 目录的 db 目录下,但是这个目录在安装过程不会自动创建,需要手动创建 data 目录,并在 data 目录中创建 db 目录。data 目录可以创建在任何位置。
这里,我们将 data 目录创建在 mongodb 的根目录下。
至此 Linux 平台中的 MongoDB 就已经安装完毕。
3 MongoDB 的启动与关闭
3.1启动 MongoDB
MongoDB 的启动方式分为两种
1)前置启动
2)后置启动
无论哪种启动方式都需要执行 bin
目录中的 mongod
命令。MongoDB 在启动时默认的查找数据库的路径为/data/db
。如果我们数据库路径有变化,需要在该命令中通过--dbpath
参数来指定db
目录的路径(该路径可以是绝对路径,也可是相对路径)。
3.1.1前置启动
MongoDB 的默认启动方式为前置启动。所谓前置启动就是 MongoDB 启动进程后会占用当前终端窗口。
进入到 MongoDB 的bin
目录。
执行 bin 目录中的mongod
命令。
由于我们的 db 目录放在 mongodb
的根下,所以在执行该命令时需要通过 --dbpath
参数指定 db 路径。
启动后会在终端中输出一些启动信息。此时终端窗口已被启动进程所占用。我们通过启动信息可以看到 MongoDB 默认的监听端口为 27017
按Ctrl+C
可结束启动进程关闭 MongoDB
3.1.2后置启动
所谓后置启动就是以守护进程的方式启动 MongoDB。我们需要在执行 mongod
命令中添加--fork
参数。需要注意的是,--fork
参数需要配合着--logpath
或者是--syslog
参数使用。 --logpath
与--syslog
参数是指定 MongoDB 的日志文件。MongoDB 的日志文件可以在系统中的任意位置,我们在 mongodb 目录下创建 log 目录,在该目录中创建一个名为 mongodb.log 的日志文件。
创建 log 目录
在 log 目录中创建 mongodb.log 日志文件
后置启动 MongoDB
3.1.3常见的启动参数
3.1.4通过配置文件加载启动参数
如果觉得在启动 MongoDB 时给定的参数项太多,那么我们也可以通过配置文件来配置启动参数,配置文件可以在任意目录中,配置文件的扩展名应为.conf
,配置文件中使用key=value
结构。在执行 MongoDB 时通过--config
参数来指定需要加载的配置文件。
我们在 mongodb 目录下创建一个 etc 目录,在该目录中创建一个名为 mongodb.conf 的配置文件。
创建 mongodb.conf 配置文件
编辑配置文件,在配置文件中添加配置项:
1)指定 db 路径
2)指定日志文件
3)配置端口
4)配置后端启动
在配置文件中配置启动参数时需要注意的是,在参数前不在加--
符号,直接以参数名作为 key 就可以。
通过加载配置文件启动 MongoDB
3.1.5配置环境变量
为了能够在任何目录中执行 bin 目录中的命令,我们可以将 bin 目录添加到环境变量中。修 改/etc/profile
文 件 , 添 加 export PATH=/usr/local/mongodb/bin:$PATH
。
/usr/local/monogdb/bin
为 MongoDB 的 bin 目录的绝对路径。可根据自己的情况来指定。
重新加载/etc/profile
文件
测试结果
3.2关闭 MongoDB
3.2.1使用 Ctrl+C 关闭
如果我们的启动方式是前置启动,那么直接使用快捷键 Ctrl+C
就可以关闭 MongoDB。这种关闭方式会等待当前进行中的的操作完成,所以依然是安全的关闭方式。
3.2.2使用 kill 命令关闭
我们可以通过 Linux 的 kill 命令结束 MongoDB 进程,然后删除data/db
目录中的 mongod.lock 文件,否则下次无法启动。但是此方法不建议使用,因为会造成数据损坏现象。
3.2.3使用 MongoDB 的函数关闭
在 MongoDB 中提供了两个关闭数据库的函数:
db.shutdownServer()
db.runCommand(“shutdown”)
如上两个方法都需要在 admin 库中执行,并且都是安全的关闭方式。
3.2.4使用 mongod 命令关闭 MongoDB
mongod --shutdown --dbpath<数据库路径>
mongod 命令的 shutdown 选项能安全的关闭 MongoDB 服务
五、 MongoDB 的用户与权限管理
Mongodb 作为时下最为热门的数据库,那么其安全验证也是必不可少的,否则一个没有验证的数据库暴露出去,任何人可随意操作,这将是非常危险的。我们可以通过创建用户的方式来降低风险。
1 Mongodb 用户权限列表
2 MongoDB 用户使用
2.1创建 DB 管理用户
mongodb 有一个用户管理机制,简单描述为,有一个管理用户组,这个组的用户是专门为管理普通用户而设的,暂且称之为管理员。
管理员通常没有数据库的读写权限,只有操作用户的权限, 因此我们只需要赋予管理员 userAdminAnyDatabase
角色即可。
另外管理员账户必须在 admin 数据库下创建。
2.1.1 切换到 Admin 库
管理员需要在 admin 数据库下创建,所以我们需要切换到 admin 数据库。
2.1.2查看 admin 中的用户
我们可以通过 db.system.users.find()
函数来查看 admin 库中的所有用户信息。
目前在 admin 库中没有用户,所以查无结果。
2.1.3db.createUser 函数
在 MongoDB 中可以使用 db.createUser({用户信息})
函数创建用户。
db.createUser({
user: "<name>",
pwd: "<cleartext password>",
customData: { <any information> },
roles: [
{ role: "<role>", db: "<database>" } | "<role>",
...
]
});
1)user:新建用户名。
2)pwd:新建用户密码。
3)customData:存放一些用户相关的自定义数据,该属性也可忽略。
4)roles:数组类型,配置用户的权限。
2.1.4创建管理员用户
我们现在需要在 admin 库中创建一个名为 bjsxt 的管理员用户,密码为 bjsxtpwd。
db.createUser({user:'bjsxt',pwd:'bjsxtpwd',roles:[{role:'userAdminAnyDatabase',db:'admin'}])
创建成功后会看到如下提示:
2.1.5重启 MongoDB
在管理员账户创建完成后,我们需要重新启动 MongoDB,并开启验证。
重新启动函数:db.shutdownServer()
。
2.1.6使用权限方式启动 MongoDB
在默认的情况下 MongoDB 是不开启用户认证的。如果我们添加用户,那么需要开启用户认证机制。通过修改 mongodb.conf
配置文件,在文件中添加 auth=true
即可。
修改完成后启动 MongoDB。
2.1.7用户认证
创建管理员后,需要认证方可使用该用户,否则会提示需要认证。
认证函数:db.auth(‘用户名’,’密码’)
如果结果返回 1,则表示认证成功,返回 0 则表示认证失败。
登录成功后可查询用户
2.2创建普通用户
普通用户由管理员创建。通常需要指定操作某个数据库。
2.2.1需求
我们创建一个 sxt
数据库,给这个数据库添加一个用户,用户名为 itsxt
,密码为 itsxtpwd
。
并授予该用户对 sxt 数据库进行读写操作的权限。
2.2.2使用管理员用户登录
普通用户需要由管理员创建并授权。所以,我们首先做的就是用管理员账户登录数据库。
2.2.3创建 sxt 数据库
use 命令切换数据库时如果该库不存在,那么则会创建该数据库。
2.2.4创建普通用户
2.2.5使用普通用户
打开一个新的客户端。
2.2.6 切换到 sxt 数据库
由于我们是在 sxt 数据库中创建的itsxt
用户,所以需要先切换到sxt
库。
2.2.7登录普通用户
如果我们不登录会发现无法对该数据库进行插入操作。因为缺少用户认证。
通过认证函数对用户进行登录认证。
认证成功后操作通过该用户操作 sxt 库。该用户对 sxt 库具备读写操作。
2.3更新用户角色
如果我们需要对已存在的用户的角色做修改,那么我们可以使用 db.updateUser()
函数来更新用户角色。注意,该函数需要当前用户具有 userAdminAnyDatabase
或者更高的权限。
2.3.1更新角色语法格式
db.updateUser("用户名", {"roles":[{"role":"角色名称"},{"更新项 2":"更新内容"}]})
2.3.2需求
目前 bjsxt 管理员用户只具备 userAdminAnyDatabase
用户管理角色,我们为该用户添加一个 dbAdminAnyDatabase
数据库管理角色。
2.3.3更新角色
db.updateUser("bjsxt",{roles : [{"role" : "userAdminAnyDatabase","db" : "admin"},{"role" : "dbAdminAnyDatabase","db" : "admin"}]})
如果没有提示任何信息则表示更新成功。退出当前客户端重新连接即可生效。
2.3.4查看用户信息
通过 show users
命令查看到 bjsxt 用户的角色已经发生改变。
2.4更新用户密码
更新用户密码有两种方式:
1)使用db.updateUser()
函数更新密码。
2)使用 db.changeUserPassword()
函数更新密码
2.4.1更新密码方式一
使用db.upateUser()
函数将 bjsxt 用户的密码修改为 sxt
2.4.1.1 语法格式
db.updateUser("用户名",{"pwd":"新密码"})
如果未提示任何信息则表示更新成功。退出当前客户端重新连接认证即可。
重新使用 bjsxt 用户登录
2.4.2更新密码方式二
使用 db.changeUserPassword()
函数将 bjsxt 用户的密码修改为 sxtpwd
。
2.4.2.1 语法格式
db.changeUserPassword("用户名","新密码")
如果未提示任何信息则表示更新成功。退出当前客户端重新连接认证即可。
重新使用 bjsxt 用户登录
2.5删除用户
通过 db.dropUser()
函数可删除指定用户。删除成功后会返回 true。在删除用户时需要切换到创建用户时所指定的数据库中才可以删除。注意:需要使用具有userAdminAnyDatabse
角色管理员用户才可以删除其他用户。
2.5.1需求
我们使用 db.dropUser()
函数将 itsxt 用户删除。
2.5.2切换数据库
itsxt
用户在 sxt 数据库中,所以需要先切换到 sxt 数据库。
2.5.3通过函数删除用户
我们可以看到,该函数返回了 true,表示删除成功。
六、 MongoDB 的数据库操作
1 创建数据库
在 MongoDB 中创建数据库的命令使用的是 use
命令。该命令有两层含义:
1)切换到指定数据库。
2)如果切换的数据库不存在,则创建该数据库。
我们使用 use 命令创建一个名为 sxttest 的数据库。
2 查看所有数据库
我们可以通过 show dbs
命令查看当前 MongoDB 中的所有数据库。
如果开启了用户认证,则需要先登录方可查看到结果,否则不显示任何信息。如果使用的是具备数据库管理员角色的用户,那么则可以看到 MongoDB 中的所有数据库,如果使用的普通用户登录的那么只能查询到该用户所拥有的数据库。
用户未登录查询数据库。
使用具有数据库管理员角色的用户登录查询数据库。
在查询结果中并未包含我们刚刚创建的 sxttest 数据库。因为,在 show dbs
命令中不显示未含有任何信息的数据的库。
使用普通用户登录查询数据库。
我们在 sxt 数据库中创建一个只具备读写权限的普通用户。
使用普通用户登录并查询数据库。
3 删除数据库
在 MongoDB 中使用 db.dropDatabase()
函数来删除数据库。在删除数据库之前,需要使用具备 dbAdminAnyDatabase
角色的管理员用户登录,然后切换到需要删除的数据库,执行db.dropDatabase()
函数即可。删除成功后会返回一个{ "ok" : 1 }
的 JSON 字符串。
我们现在将刚刚创建的 sxttest
删除。
七、 MongoDB 的集合操作
MongoDB 中的集合是一组文档的集,相当于关系型数据库中的表。
1 创建集合
MongoDB 使用 db.createCollection()函数来创建集合。
语法格式:db.createCollection(name, options)。
name: 要创建的集合名称。
options: 可选参数, 指定有关内存大小及索引的选项。
options 可以是如下参数。
在插入文档时,MongoDB 首先检查固定集合的 size
字段,然后检查 max
字段。
1.1使用默认集合
在 MongoDB 中,我们也可以不用创建集合,当我们插入一些数据时,会自动创建集合,并且会使用数据库的名字作为集合的名称。
创建一个新数据库,名为 develop
如果开启认证,需要为新数据库创建访问用户。新建用户名为itsxt
,密码为itsxtpwd
使用 itsxt 用户登录 develop 库
向 develop 库中插入一条数据
查询集合
1.2创建不带参数的集合
我们也可以根据自己的情况创建集合。在 develop 数据库中创建一个名为 dev 的集合,该集合创建时不指定任何参数。如果开启认证,则需要使用具有数据库管理员权限的用户来创建集合。
1.3创建带参数的集合
在 develop 数据库中创建一个名为 dev2 的固定集合,整个集合空间大小为 2000000kb,文档最大个数为 1000。
2 查看集合
如果要查看已有集合,可以使用show collections
或 show tables
命令。
2.1show collections
2.2show tables
3 删除集合
如果我们要删除集合,需要先切换到需要删除集合所在的数据库,使用 drop()
函数删除集合即可。
删除集合的语法格式为:db.集合名称.drop()
。
删除 dev2 集合
八、 MongoDB 的文档操作
在 MongoDB 中文档是指多个键及其关联的值有序地放置在一起就是文档,其实指的就是数据,也是我们平时操作最多的部分。MongoDB 中的文档的数据结构和 JSON 基本一样。所有存储在集合中的数据都是BSON 格式。
BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。
1 插入文档
1.1插入单个文档
1.1.1insert 函数
语法格式为:db.COLLECTION_NAME.insert(document)。
向 dev 集合中插入单个文档。
{title:'北京尚学堂',description:'程序员的摇篮',url:'www.bjsxt.com',tags:['java','大数据
','python']}
查看文档
1.1.2save 函数
向 dev 集合中插入单个文档。
{title:'百战程序员',description:'身经百战,高薪相伴',url:'www.itbaizhan.cn',tags:['javaWeb
实战','数据库实战','微服务实战']}
查看文档
1.1.3 insertOne 函数
在 MongoDB3.2 以后的版本中,提供了 insertOne()
函数用于插入文档。向 dev 集合中插入单个文档。
{title:' 尚 学 堂 大 数 据 ',description:' 培 养 大 数 据 人 才 的 摇 篮
',url:'www.bjsxt.com',tags:['hadoop','spark','Hbase']}
查看文档
1.2插入多个文档
向集合中批量插入多个文档时,需要使用数组来存放文档。
语法格式:db.COLLECTION_NAME.insert([{},{},{}.....])
。
1.2.1 insert 或者 save 函数
向 dev 集合中批量插入多个文档
[{title:'java',tags:['JavaSE','JavaEE','JavaME']},{title:'ORM',tags:['Mybatis','Hibernate']},{titl
e:'Spring',tags:['SpringMVC','SpringBoot','SpringCloud']}]
查看文档
1.2.2insertMany 函数
在 MongoDB3.2 以后的版本中,提供了 insertMany
函数用于插入文档。
语法格式:db.COLLECTION_NAME.insertMany([{},{},{},.....])
。
向 dev 集合中批量插入多个文档
[{title:'Web',tags:['JSP','Servlet']},{title:'RPC',tags:['RMI','Dubbo']},{title:'DataBase',tags:['Or
acle','MySQL']}]
查看文档
1.3通过变量插入文档
Mongo Shell 工具允许我们定义变量。所有的变量类型为 var 类型。也可忽略变量类型。变量中赋值符号后侧需要使用小括号来表示变量中的值。我们可以将变量作为任意插入文档的函数的参数。
语法格式:变量名=({变量值})
1.3.1通过变量插入单个文档
定义变量
document=({title:'SpringCloud',tags:['Spring Cloud Netflix','Spring Cloud Security','Spring
Cloud Consul']})
插入文档
查询文档
1.3.2通过变量插入多个文档
我们也可以在变量中定义多个文档。
语法结构:变量名=([{},{},{},....])
插入文档
我们现在将多个文档放入到了一个变量中,所以在插入数据时,可直接使用插入单个文档的函数。
查询文档
2 更新文档
MongoDB 通过 update
函数或者 save
函数来更新集合中的文档。
2.1update 函数
update()
函数用于更新已存在的文档。
语法格式:db.COLLECTION_NAME.update({查询条件},{更新内容},{更新参数(可选)})
将 Spring Data 修改为 SpringData
查看更新文档
2.2更新操作符
2.2.1$set 操作符
$set
操作符:用来指定一个键并更新键值,若键不存在并创建。
语法格式:db.COLLECTION_NAME.update({查询条件},{更新操作符:{更新内容}})
将 Spring Security 修改为 SpringSecurity。
查看结果
批量更新
在更新文档时,可以使用multi
参数实现批量更新。
添加测试数据
将 title 为 dev 的文档的 size 更新为 500
查看结果
2.2.2$inc 操作符
$inc
操作符:可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的
操作。
将 title 为 SpringData 的文档中的 num 值递增 1。
查看结果
2.2.3$unset 操作符
$unset
操作符:主要是用来删除键。
删除 title 为 SpringData 的文档中的 num 键。
查看结果
2.2.4$push 操作符
$push
操作符:向文档的某个数组类型的键添加一个数组元素,不过滤重复的数据。添加时键存在,要求键值类型必须是数组;键不存在,则创建数组类型的键。
向 title 为 SpringData 的文档中添加一个数组键为 tags 值为[“Spirng Data Redis”]
查看结果
2.2.5$pop 操作符
$pop
操作符:删除数据元素。
1 表示从数组的尾部删除
删除 title 为 Spring 的文档中 tags 数组中的 Spring Cloud
查看结果
-1 表示从数组的头部删除元素
删除 title 为 Spring 的文档中 tags 数组中的 SpringMVC
查看结果
2.2.6$pull 操作符
$pull
操作符:从数组中删除满足条件的元素
删除 title 为 Spring 的文档中 tags 数组中的 SpringBoot
查看结果
2.2.7$pullAll 操作符
$pullAll
操作符:从数组中删除满足条件的多个元素
删除 title 为 java 的文档中 tags 数组中的 JavaSE、JavaEE
查看结果
2.2.8$rename
$rename
操作符:对键进行重新命名。
将 title 为 Java 的文档中的 tags 键修改为 tag。
查看结果
2.3使用 save()函数更新文档
save()
方法通过传入的文档来替换已有文档。
语法格式:save({文档})
更新 title 为 SpringData 的文档,将 SpringData 修改为 Spring Data,并去掉 tags
查看结果
3 删除文档
3.1remove()函数
使用 remove()
函数可删除集合中的指定文档。
语法格式:remove({指定删除条件},删除参数(可选参数))
删除 title 为 Spring data 的文档,可使用该文档的 ObjectId 作为删除条件
如果使用的条件在集合中可以匹配多条数据,那么 remove()
函数会删除所有满足条件的数据。我们可以在 remove 函数中给定 justOne,表示只删除第一条,在 remove 函数中给定参数 1 即可。
向 dev 集合中插入三条拥有相同 title 的测试数据
查看结果
只删除第一条数据
查看结果
注意:remove()
方法 并不会真正释放空间。需要继续执行 db.repairDatabase()
来回收磁盘空间。
3.2deleteOne()函数
deleteOne()
函数是官方推荐删除文档的方法。该方法只删除满足条件的第一条文档。
查看结果
3.3deleteMany()函数
deleteMany
函数是官方推荐的删除方法。该方法删除满足条件的所有数据。
再次插入两条测试数据
查看结果
删除所有 title 为 dev 的文档
3.4删除集合中的所有文档
使用 remove 函数删除集合中的所有文档
语法格式:remove({})
使用 deleteMany 函数删除所有文档
语法格式:deleteMany({})
4 查询文档
4.1find()函数
在 MongoDB 中可以使用 find()函数查询文档。
语法格式为:find({查询条件(可选)},{指定投影的键(可选)})
如果未给定参数则表示查询所有数据。
pretty()
函数可以使用格式化的方式来显示所有文档。
查询 dev 集合中的所有数据并格式化显示。
查询 title 为 DataBase 的文档并格式化显示。
4.2findOne()函数
findOne()
函数只返回满足条件的第一条数据。如果未做投影操作该方法则自带格式化功
能。
语法格式:findOne({查询条件(可选)},{投影操作(可选)})
插入三条测试数据
使用 findOne 查询文档,条件为 title 的值为 dev 的文档。
4.3模糊查询
在 MongoDB 中可以通过//
与^ $
实现模糊查询,注意使用模糊查询时查询条件不能放到
双引号或单引号中。
查询文档中 title 的值含有 a 的内容。
使用^
表示起始位置。
查询文档中 title 的值以 S 开头的内容。
使用$
表示结尾位置。
查询文档中 title 的值以结尾的内容。
4.4投影操作
4.4.1find()函数投影操作
在 find 函数中我们可以指定投影键。
语法格式为:find({查询条件},{投影键名:1(显示该列)|0(不显示该列),投影键名:1|0,......})
_id
列默认为显示列。如果不显示_id
可在投影中通过 0 过滤。
4.4.2findOne 函数投影操作
在 findOne 函数中我们可以指定投影列。
语法格式为:findOne({查询条件},{投影键名:1(显示该列)|0(不显示该列)})
5 条件操作符
条件操作符用于比较两个表达式并从 mongoDB 集合中获取数据。
语法格式:find({键:{操作符:条件}})或者 findOne({键:{操作符:条件}})
5.1$gt
(>)
大于操作符
我们可以使用$gt
操作做大于的条件判断。该操作符可以数字或日期进行判断。
添加测试数据。
查询 size 大于 300 的文档。
5.2$lt
(<)
小于操作符
我们可以使用$lt
操作做小于的条件判断。该操作符可以数字或日期进行判断。
查询 size 小于 300 的文档。
5.3$gte
(>=)
大于或等于操作符
我们可以使用$gte
操作做大于或等于的条件判断。该操作符可以数字或日期进行判断。
查询 size 大于或等于 300 的文档。
5.4$lte
(<=)
小于或等于操作符
我们可以使用$lte
操作做小于或等于的条件判断。该操作符可以数字或日期进行判断。
查询 size 小于或等于 300 的文档。
5.5$eq
(==)
等于操作符
我们可以使用$eq
操作做相等的条件判断。
查询 size 等于 300 的文档。
5.6$ne
(!=)
不等操作符
我们可以使用$ne
操作做不等的条件判断。
查询 size 不等于 300 的文档。
5.7$and
我们可以使用$and
操作符来表示多条件间的并且关系。
语法格式为:find({$and:[{条件一},{,条件二},.......]})
插入测试数据
如果在查询中给定了多个查询条件,条件之间的关系默认为 and
关系。查询 size 大于 100 并且小于 300 的文档。
使用$and
指定多条件关系。
查询 size 大于 100 并且小于 300 的文档。
5.8$or
我们可以使用$or
操作符来表示多条件间的或者关系。
语法格式为:find({$or:[{条件一},{条件二},.....]})
查询 title 的值为 test2 或者 size 大于 300 的文档。
5.9 a n d 与 and 与 and与or 联合使用
查询 title 为 test5 并且 size 等于 500,或者 size 小于 400 的文档。
5.10$type 操作符
$type
操作符是基于 BSON 类型来检索集合中匹配的数据类型,并返回结果。
插入测试数据
查询 title 的值为 number 类型。
6 Limit 函数与 Skip 函数
6.1Limit 函数
如果需要在 MongoDB 中读取指定数量的数据记录,可以使用 MongoDB 的 Limit
函数,limit()
函数接受一个数字参数,该参数指定从 MongoDB 中读取的记录条数。
语法格式:db.COLLECTION_NAME.find().limit(NUMBER)
6.2Skip 函数
我们除了可以使用 limit()
函数来读取指定数量的数据外,还可以使用 skip()
函数来跳过指定数量的数据,skip
函数同样接受一个数字参数作为跳过的记录条数。
语法格式:db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
我们可以使用 skip
函数与 limit
函数实现 MongoDB 的分页查询,但是官方并不推荐这样做,因为会扫描全部文档然后在返回结果,效率过低。
7 MongoDB 排序
在 MongoDB 中使用 sort()
函数对查询到的文档进行排序,sort()
函数可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
语法格式:db.COLLECTION_NAME.find().sort({排序键:1})
7.1升序排序
查询 size 的值为 number 类型的文档,显示 title,size 的内容,并对 size 做升序排序。
7.2降序排序
查询 size 的值为 number 类型的文档,显示 title,size 的内容,并对 size 做降序排序。
7.3对字符串排序
对字符串排序的方式采用的是大小写分离排序。
8 MongoDB 索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对系统的性能是非常致命的。索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
8.1创建索引
在 MongoDB 中会自动为文档中的_Id
(文档的主键)键创建索引,与关系型数据的主键索引类似。
我们可以使用 createIndex()
函数来为其他的键创建索引。在创建索引时需要指定排序规则。1 按照升序规则创建索引,-1 按照降序规则创建索引。
在创建索引时,需要使用具有 dbAdmin
或者dbAdminAnyDatabase
角色的用户。
语法格式:db.COLLECTION_NAME.createIndex({创建索引的键:排序规则,......},{创建索引的参数(可选参数)})
参数说明
为 dev 集合中的 title 键创建索引,并让创建工作在后台运行。
8.2查看索引
8.2.1查看集合索引
我们可以通过 getIndexes()
或者 getIndexSpecs()
函数查看集合中的所有索引信息。
语法格式:db.COLLECTION_NAME.getIndexse()
语法格式:db.COLLECTION_NAME.getIndexSpecs()
使用 getIndexes()
函数查看当前 dev 集合中的索引
使用 getIndexSpecs()
函数查看当前 dev 集合中的索引
8.2.2查看索引键
我们可以通过使用 getIndexKeys()
函数查看集合的索引键。
语法格式:db.COLLECTION_NAME.getIndexKeys();
查看 dev 集合中的索引键
8.2.3查看索引大小
我们可以通过 totalIndexSize()
函数来查看当前集合中索引的大小,单位为字节。
语法格式:db.COLLECTION_NAME.totalIndexSize([detail](可选参数))
参数解释:detail 可选参数,传入除 0 或 false 外的任意数据,那么会显示该集合中每个索引的大小及集合中索引的总大小。如果传入 0 或 false 则只显示该集合中所有索引的总大小。默认值为 false。
查看 dev 集合中所有索引的总大小。
查看 dev 集合中的每个索引的大小以及总大小
8.3修改索引
MongoDB 没有单独的修改索引函数,如果要修改某个索引,需要先删除旧的索引,再创建新的索引。
8.4删除索引
8.4.1删除集合中的指定索引
我们可以通过 dropIndex()
函数来删除指定索引。
语法格式:db.COLLECTION_NAME.dropIndex("索引名称")。
删除 title 键的索引
8.4.2删除集合中的全部索引
我们可以使用 dropIndexes()
函数删除集合中的全部索引,_id
键的索引除外。
语法格式:db.COLLECTION_NAME.dropIndexes()
8.5重建索引
我可以使用 reIndex()
函数重建索引。重建索引可以减少索引存储空间,减少索引碎片,优化索引查询效率。一般在数据大量变化后,会使用重建索引来提升索引性能。重建索引是删除原索引重新创建的过程,不建议反复使用。
语法格式:db.COLLECTION_NAME.reIndex()
8.6MongoDB 中的索引类型
在 MongoDB 中支持多种类型的索引,包括单字段索引、复合索引、多 key 索引、文本索引等,每种类型的索引有不同的使用场合。
8.6.1单字段索引(Single Field Index)
所谓单字段索引是指在索引中只包含了一个键。查询时,可加速对该字段的各种查询请求,是最常见的索引形式。MongoDB 默认创建的_Id
索引也是这种类型。我们可以使用createIndexes({索引键:排序规则})
函数来创建单字段索引。
语法格式:db.COLLECTION_NAME.createIndexes({索引键名:排序规则})
为 dev 集合中的 title 键创建单字段索引
8.6.2交叉索引
所谓交叉索引就是为一个集合的多个字段分别建立索引,在查询的时候通过多个字段作为查询条件,这种情况称为交叉索引。
在查询文档时,在查询条件中包含一个交叉索引键或者在一次查询中使用多个交叉索引键作为查询条件都会触发交叉索引。
为 dev 集合中的 size 键创建交叉索引。
8.6.3复合索引(Compound Index)
复合索引是 Single Field Index 的升级版本,它针对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按第二个字段排序,依次类推。
语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则, 索引键名:排序规则,......});
复合索引能满足的查询场景比单字段索引更丰富,不光能满足多个字段组合起来的查询,也能满足所以能匹配符合索引前缀的查询。
删除 dev 中的交叉索引。
创建 title 与 size 的符合索引
查看索引
8.6.4多 key 索引 (Multikey Index)
当索引的字段为数组时,创建出的索引称为多 key 索引,多 key 索引会为数组的每个元素建立一条索引。
语法格式:db.COLLECTION_NAME.createIndex({数组键名:排序规则});
为 dev 集合中 tags 键创建多 Key 索引
查看索引
8.7索引额外属性
MongoDB 除了支持多种不同类型的索引,还能对索引定制一些特殊的属性。
8.7.1唯一索引 (unique index)
唯一索引会保证索引对应的键不会出现相同的值,比如_id 索引就是唯一索引
语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则},{unique:true})
如果唯一索引所在字段有重复数据写入时,抛出异常。
删除 dev 集合中的索引。为 dev 集合中的 title 键建立唯一索引
插入 title 相同的值测试唯一索引
8.7.2部分索引 (partial index):
部分索引是只针对符合某个特定条件的文档建立索引,3.2 版本才支持该特性。
MongoDB 部分索引只为那些在一个集合中,满足指定的筛选条件的文档创建索引。由于部分索引是一个集合文档的一个子集,因此部分索引具有较低的存储需求,并降低了索引创建和维护的性能成本。部分索引通过指定过滤条件来创建,可以为 MongoDB 支持的所有索引类型使用部分索引。
简单点说:部分索引就是带有过滤条件的索引,即索引只存在与某些文档之上
语 法 格 式 : db.COLLECTION_NAME.createIndex({ 索引键名 : 排 序 规则},{partialFilterExpression:{键名:{匹配条件:条件值}}})
为 dev 集合中的 size 键创建部分索引。条件为大于 300
查看索引
注意:部分索引只为集合中那些满足指定的筛选条件的文档创建索引。如果你指定的partialFilterExpression
和唯一约束、那么唯一性约束只适用于满足筛选条件的文档。具有唯一约束的部分索引不会阻止不符合唯一约束且不符合过滤条件的文档的插入。
8.7.3稀疏索引(sparse index)
稀疏索引仅包含具有索引字段的文档的条目,即使索引字段包含空值也是如此。索引会跳过缺少索引字段的任何文档。索引是“稀疏的”,因为它不包含集合的所有文档。相反,非稀疏索引包含集合中的所有文档,为那些不包含索引字段的文档存储空值。
语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则},{sparse:true})
为 dev 集合中的 tag 键创建稀疏索引
查看索引
注意:从 MongoDB 3.2 开始,MongoDB 提供了创建部分索引的选项 。部分索引提供了稀疏索引功能的超集。如果您使用的是 MongoDB 3.2 或更高版本,则部分索引应优先于稀疏索引。
8.8覆盖索引查询
官方的 MongoDB 的文档中说明,覆盖查询是以下的查询:
- 所有的查询字段是索引的一部分
- 所有的查询返回字段在同一个索引中
由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。因为索引存在于 RAM 中,从索引中获取数据比通过扫描文档读取数据要快得多。
如有如下索引:
db.stu.createIndex({title:1,:size:1})
那么执行如下查询时,该索引会覆盖查询:
db.stu.find({title:"dev"},{size:1,_id:0})
也就是说,对于上述查询,MongoDB 的不会去数据库文件中查找。相反,它会从索引中提取数据,这是非常快速的数据查询。
由于我们的索引中不包括 _id
字段,_id
在查询中会默认返回,我们可以在 MongoDB的查询结果集中排除它。
8.9查询计划
在 MongoDB 中通过 explain()
函数启动执行计划,我们可以使用查询计划分析索引的使用情况,可通过查看详细的查询计划来决定如何优化。
语法结构:db.COLLECTION_NAME.find().explain()
删除 dev 集合中的所有索引。通过查询计划查看查询 size 键的值大于 200 的查询结果
为 size 键创建单字段索引。再次查看查询结果。
创建索引
查看执行结果
8.10使用索引注意事项
既然索引可以加快查询速度,那么是不是只要是查询语句,就创建索引呢?答案是否定的。因为索引虽然加快了查询速度,但索引也是有代价的:索引文件本身要消耗存储空间,同时索引会加重插入、删除和修改记录时的负担,另外,数据库在运行时也要消耗资源维护索引,因此索引并不是越多越好。
那么什么情况不建议创建索引呢?例如一两千条甚至只有几百条记录的表,没必要建索引,让查询做全集合扫描就好了。至于多少条记录才算多?我个人建议以 2000 作为分界线,记录数不超过 2000 可以考虑不建索引,超过 2000 条可以酌情考虑创建索引。
8.10.1 如何创建合适的索引
8.10.1.1 建立合适的索引
为每一个常用查询结构建立合适的索引。
复合索引是创建的索引由多个字段组成,例如:
db.test.createIndex({"username":1, "age":-1})
交叉索引是每个字段单独建立索引,但是在查询的时候组合查找,例如:
db.test.createIndex({"username":1})
db.test.createIndex({"age":-1})
db.test.find({"username":"kaka", "age": 30})
交叉索引的查询效率较低,在使用时,当查询使用到多个字段的时候,尽量使用复合索引,而不是交叉索引。
8.10.1.2 复合索引的字段排列顺序
当我们的组合索引内容包含匹配条件以及范围条件的时候,比如包含用户名(匹配条件)以及年龄(范围条件),那么匹配条件应该放在范围条件之前。
比如需要查询:
db.test.find({"username":"kaka", "age": {$gt: 30}})
那么复合索引应该这样创建:
db.test.ensureIndex({"username":1, "age":-1})
8.10.1.3 查询时尽可能仅查询出索引字段
有时候仅需要查询少部分的字段内容,而且这部分内容刚好都建立了索引,那么尽可能只查询出这些索引内容,需要用到的字段显式声明(_id
字段需要显式忽略!)。因为这些数据需要把原始数据文档从磁盘读入内存,造成一定的损耗。
比如说我们的表有三个字段:
name, age, mobile
索引是这样建立的:
db.stu.createIndex({"name":1,"age":-1})
我们仅需要查到某个用户的年龄(age),那可以这样写:
db.stu.find({"name":"kaka"}, {"_id":0, "age":1})
注意到上面的语句,我们除了”age”:1
外,还加了”_id”:0
,因为默认情况下,_id
都是会被一并查询出来的,当不需要_id
的时候记得直接忽略,避免不必要的磁盘操作。
8.10.1.4 对现有的数据大表建立索引的时候,采用后台运行方式
在对数据集合建立索引的过程中,数据库会停止该集合的所有读写操作,因此如果建立索引的数据量大,建立过程慢的情况下,建议采用后台运行的方式,避免影响正常业务流程。
db.stu.ensureIndex({"name":1,"age":-1},{"background":true})
8.11索引限制
8.11.1 额外开销
每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。
所以,如果你很少对集合进行读取操作,建议不使用索引。
8.11.2 内存使用
由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。
如果索引的大小大于内存的限制,MongoDB 会删除一些索引,这将导致性能下降。
8.11.3 查询限制
索引不能被以下的查询使用:
正则表达式(最左匹配除外)及非操作符,如 $nin
, $not
, 等。
算术运算符,如 $mod
, 等。
所以,检测你的语句是否使用索引是一个好的习惯,可以用 explain
来查看。
8.11.4 最大范围
集合中索引不能超过 64 个
索引名的长度不能超过 128 个字符
一个复合索引最多可以有 31 个字段
8.12正则查询
MongoDB 中查询条件也可以使用正则表达式作为匹配约束。
语法格式:db.COLLECTION_NAME.find({字段名:正则表达式});
或
db.COLLECTION_NAME.find({字段名:{$regex:正则表达式,$options:正则选项}});
正则表达式格式:/xxx/
正则选项:
i
- 不区分大小写以匹配大小写的情况。
m
- 多行查找,如果内容里面不存在换行符号(例如 \n
)或者条件上没有(start/end
),
该选项没有任何效果
x
- 设置 x 选项后,正则表达式中的非转义的空白字符将被忽略。需要$regex
与$options
语法
s
- 允许点字符(即.)匹配包括换行符在内的所有字符。需要$regex
与$options
语法
i,m,x,s
可以组合使用。
查询 dev 集合中 title 字段以’S’开头的数据
db.dev.find({title:/^S/})
db.dev.find({title:{$regex:/^S/}})
查询 dev 集合中 title 字段以’g’结尾的数据
db.stu.find({title:/g$/})
db.stu.find({title:{$regex:/g$/}});
查询 dev 集合中 dev 字段中包含’ing’的数据
db.stu.find({title:/ing/});
db.stu.find({title:{$regex:/ing/}});
查询 dev 集合中 title 字段以’L’开头的数据,且忽略大小写
db.dev.find({title:/^S/i});
db.dev.find({title:{$regex:/^S/i}});
db.dev.find({title:{$regex:/^S/, $options:"i"}});
查询 dev 集合中 title 字段已’S’开头、'g’结尾的数据
db.dev.find({title:/^S.*g$/});
db.dev.find({title:{$regex:/^z.*n$/}});
查询 dev 集合中 title 字段以’S’或’t’开头的数据
db.dev.find({title:{$in:[/^S/, /^t/]}});
查询 dev 集合中 title 字段不以’S’开头的数据
db.dev.find({title:{$not:/^S/}});
查询 dev 集合中 title 字段不以’S’或’t’开头的数据
db.stu.find({title:{$nin:[/^S/, /^t/]}});
9 MongoDB 聚合查询
在 MongoDB 中我们可以通过 aggregate()
函数来完成一些聚合查询,aggregate()
函数主要用于处理诸如统计,平均值,求和等,并返回计算后的数据结果。
语法格式:
db.COLLECTION_NAME.aggregate([{$group:{_id:"$分组键名","$分组键名",.....,别名:{聚合运算:"$运算列"}}},{条件筛选:{键名:{运算条件:运算值}}}])
常见的 mongo 的聚合操作和 mysql 的查询做类比
9.1求和 - $sum
查询 dev 集合中一共有多少个文档。
相当于 sql 语句:
SELECT count(*) AS count FROM dev
db.dev.aggregate([{$group:{_id:null,count:{$sum:1}}}])
$group
:分组,代表聚合的分组条件
_id
:分组的字段。相当于 SQL 分组语法 group by column_name
中的 column_name 部分。
如果根据某字段的值分组,则定义为_id:'$字段名'
。所以此案例中的 null 代表一个固定的字面值'null'
。
count
:返回结果字段名。可以自定义,类似 SQL 中的字段别名。
$sum
:求和表达式。相当于 SQL 中的 sum()。
1:累加值。
查询 dev 集合中的所有 size 键中的值的总和。
相当于 sql 语句:SELECT sum(size) AS totalSize FROM dev
db.dev.aggregate([{$group:{_id:null,totalSize:{$sum:"$size"}}}])
"$size"
:代表文档中的 szie 字段的值。
对每一个 title 进行分组并计算每组中的 size 的总和
相当于 sql 语句:SELECT title AS _id , sum(size) AS totalSize FROM dev GROUP BY title
db.dev.aggregate([{$group:{_id:"$title",totalSize:{$sum:"$size"}}}])
9.2条件筛选 - $match
查询 dev 集合有多少文档的 size 大于 200。
db.dev.aggregate([{$match:{size:{$gt:200}}},{$group:{_id:null,totalSize:{$sum:1}}}])
相当于 SQL 语句:SELECT count(*) FROM dev WHERE size > 200
$match
:匹配条件,相当于 SQL 中的 where 子句,代表聚合之前进行条件筛选。
查询 dev 集合,根据 title 分组计算出每组的 size 的总和,并过滤掉总和小于等于 200的文档。
db.dev.aggregate([{$group:{_id:"$title",totalSize:{$sum:"$size"}}},{$match:{totalSize:{$gt: 200}}}])
相当于 SQL 语句:SELECT sum(size) AS totalSize FROM dev GROUP BY title HAVING totalSize > 200
9.3最大值 - $max
查询 dev 集合中 size 最大的文档。
db.dev.aggregate([{$group:{_id:null,maxSize:{$max:"$size"}}}])
$max:"$size"
:计算 size 键中的最大值。
相当于 SQL 语句:SELECT max(size) FROM dev
9.4最小值 - $min
查询 dev 集合中 size 最小的文档。
db.dev.aggregate([{$group:{_id:null,minSize:{$min:"$size"}}}])
$min:"$size"
:计算 size 键中的最小值。
相当于 SQL 语句:SELECT min(size) FROM dev
9.5平均值 - $avg
查询 dev 集合中 size 的平均值
db.dev.aggregate([{$group:{_id:null,sizeAvg:{$avg:"$size"}}}])
$avg:"$size"
:计算 size 键的平均值。
相当于 SQL 语句:SELECT avg(size) FROM dev
9.6统计结果返回数组 - $push
查询 dev 集合,按照 size 分组并返回他们的 title,如果 size 相同则使用数组返回他们的title。
db.dev.aggregate([{$group:{_id:"$size",title:{$push:"$title"}}}])
$push:"$title"
:如果 size 相同则使用数组返回他们不同的 title
9.7数组字段拆分 - $unwind
查询 dev 集合,将数组中的内容拆分显示。
db.dev.aggregate([{$unwind:"$tags"}])
$unwind:"$tags"
:对数组中的元素进行拆分显示。
9.8管道操作
什么是管道操作:
管道在 Unix 和 Linux 中一般用于将当前命令的输出结果作为下一个命令的参数。
MongoDB 的聚合管道将 MongoDB 文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
管道操作符是按照书写的顺序依次执行的,每个操作符都会接受一连串的文档,对这些文档做一些类型转换,最后将转换后的文档作为结果传递给下一个操作符(对于最后一个管道操作符,是将结果返回给客户端),称为流式工作方式。
管道操作符:$match、$group、$sort、$limit、$skip、$unwind
管道操作符,只能用于计算当前聚合管道的文档,不能处理其它的文档。
9.8.1$project-聚合投影约束
$project
操作符:我们可以使用$project
操作符做聚合投影操作。
查询 dev 集合,将数组中的内容拆分显示,并只显示 title 键与 tags 键的值。
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,tags:"$tags",title:"$title"}}])
tags:"$tags"
:显示 tags 的值,字段名为 tags。
title:"$title"
:显示 title 的值,字段名为 title。
查询 dev 集合,将数组中的内容拆分显示。要求只显示 title 键与 tags 键的值并将 title键修改为 Title。
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,tags:"$tags",Title:"$title"}}])
Title:"$title"
:显示 title 的值,字段名为 Title。
9.8.2$project-字符串处理
在$project
中我们可以通过 MongoDB 的字符串操作符对投影的内容做字符串处理。
查询 dev 集合,将数组中的内容拆分显示。将 title 中的值换为小写并命名为 New_Title
,将 tags 的值转换为大写并命名为 New_Tags
。
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,New_Title:{$toLower:"$title"},New_tags:{$toUpper:"$tags"}}}])
New_Title:{$toLower:"$title"}
:将 title 的值转换为小写,显示字段名为 New_Title。
New_tags:{$toUpper:"$tags"}
:将 tags 的值转换为大写,显示字段名为 New_Tags。
查询 dev 集合,将数组中的内容拆分显示。将 title 字段和 tags 字段的值拼接为一个完整字符串并在 Title_Tags
字段中显示。
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,Title_Tags:{$concat:["$title","-","$tags"]}}}])
Title_Tags:{$concat:["$title","-","$tags"]}
:将字段 title 与字符串'-'
和字段 tags 的值拼接为新的字符串,并显示字段名为 Title_Tags
查询 dev 集合,将数组中的内容拆分显示。只显示 title 字段的前 3 个字符,并命名为 Title_Prefix
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,Title_Prefix:{$substr:["$title",0,3]}}}])
Title_Prefix:{$substr:["$title",0,3]}
:将 title 的值从0 开始截取截3 位,并命名为 Title_Prefix
我们可以看到对于汉字部分并未截取三位,原因是$substr
只能匹配 ASCII 的数据,对于中文要使用$substrCP
9.8.3$project-算术运算
在$project
中我们可以通过 MongoDB 的算数作符对投影的内容做运算处理。
查询 dev 集合中数据,显示 title 和 size 字段,为 size 字段数据做加 1 操作,显示字段命名为 New_Size。
db.dev.aggregate([{$project:{_id:0,title:1,New_Size:{$add:["$size",1]}}}])
New_Size:{$add:["$size",1]}
:在查询结果中,对size的值做加1处理,并命名为New_Size。
排除那些没有 size 键的文档。
db.dev.aggregate([{$match:{size:{$ne:null}}},{$project:{_id:0,title:1,New_Size:{$add:["$size",1]}}}])
$match:{size:{$ne:null}
:排除那些没有 size 的文档。
查询 dev 集合中数据,显示 title 和 size 字段,为 size 字段数据做减 1 操作,显示字段命名为 New_Size。
db.dev.aggregate([{$match:{size:{$ne:null}}},{$project:{_id:0,title:1,New_Size:{$subtract:["$size",1]}}}])
New_Size:{$subtract:["$size",1]}
:在查询结果中,对 size 的值做减 1 处理,并命名为New_Size。
查询 dev 集合中数据,显示 title 和 size 字段,为 size 字段数据做乘 2 操作,显示字段命名为 New_Size。
db.dev.aggregate([{$match:{size:{$ne:null}}},{$project:{_id:0,title:1,New_Size:{$multiply:["$size",2]}}}])
New_Size:{$multiply:["$size",2]}
:在查询结果中,对 size 的值做乘 2 处理,并命名为 New_Size.
查询 dev 集合中数据,显示 title 和 size 字段,为 size 字段数据做除 2 操作,显示字段命名为 New_Size。
db.dev.aggregate([{$match:{size:{$ne:null}}},{$project:{_id:0,title:1,New_Size:{$divide:["$size",2]}}}])
New_Size:{$divide:["$size",2]}
:在查询结果中,对 size 的值做除 2 处理,并命名为 New_Size.
查询 dev 集合中数据,显示 title 和 size 字段,为 size 字段数据做模 2 操作,显示字段命名为 New_Size。
db.dev.aggregate([{$match:{size:{$ne:null}}},{$project:{_id:0,title:1,New_Size:{$mod:["$size",2]}}}])
New_Size:{$mod:["$size",2]}
:在查询结果中,对size的值做模2处理,并命名为New_Size.
9.8.4$project-日期操作
9.8.4.1 MongoDB 中的日期处理
插入当前时间 db.dev.insert({date:new Date()})
MongoDB 中的时间会比系统当前时间少 8 个小时。因为他的时间是 UTC 的时间,而中国的时区是东八区,比 UTC 快 8 个小时,所以会比当前时间少 8 个小时。
插入指定日期
方式一:
db.dev.insert({time:new Date("2018-05-01T14:20:23Z")})
new Date("2018-05-01T14:20:23Z")
:创建时间对象,日期格式为 yyyy-MM-ddThh:mm:ss
方式二:
db.dev.insert({time:ISODate("2019-06-01T16:30:00Z")})
ISODate("2019-06-01T16:30:00Z"):
查询时间
db.dev.find({time:{$eq:new Date("2018-05-01T14:20:23")}})
或者
db.dev.find({time:{$gt:new Date("2018-04-01")}})
或者
9.8.4.2 $project-日期处理
向 dev 集合中插入一个文档,该文档包含 name:”admin” birth:”1990-05-01T13:30:00Z”
查询 dev 集合中数据,显示 birth 字段的各部分数据,包括:年、月、日等信息。
显示年月日
db.dev.aggregate([{$match:{name:"admin"}},{$project:{ 年 份 :{$year:"$birth"}, 月份:
{$month:"$birth"},日:{$dayOfMonth:"$birth"}}}])
{$year:"$birth"}年份
{$month:"$birth"}月份
{$dayOfMonth:"$birth"}日期
显示小时、分钟、秒、毫秒
db.dev.aggregate([{$match:{name:"admin"}},{$project:{ 年 份 :{$year:"$birth"}, 月
份 :{$month:"$birth"}, 日 :{$dayOfMonth:"$birth"}, 时 :{$hour:"$birth"}, 分 :{$minute:"$birth"},
秒:{$second:"$birth"},毫秒:{$millisecond:"$birth"}}}])
{$hour:"$birth"}:小时
{$minute:"$birth"}:分钟
{$second:"$birth"}:秒
{$millisecond:"$birth"}:毫秒
显示星期、全年的第几周、全年中的第几天
db.dev.aggregate([{$match:{name:"admin"}},{$project:{ 年 份 :{$year:"$birth"}, 月
份 :{$month:"$birth"}, 日 :{$dayOfMonth:"$birth"}, 时 :{$hour:"$birth"}, 分 :{$minute:"$birth"},
秒:{$second:"$birth"},毫秒:{$millisecond:"$birth"},星期:{$dayOfWeek:"$birth"},全年的第几
周:{$week:"$birth"},全年中的第几天:{$dayOfYear:"$birth"}}}])
{$dayOfWeek:"$birth"}:星期日为 1,星期六为 7。
{$week:"$birth"}:全年的周计数从 0 开始。
{$dayOfYear:"$birth"}:全年中的第几天。
显示自定义日期格式
db.dev.aggregate([{$match:{name:"admin"}},{$project:{ 年 份 :{$year:"$birth"}, 月
份 :{$month:"$birth"}, 日 :{$dayOfMonth:"$birth"}, 时 :{$hour:"$birth"}, 分 :{$minute:"$birth"},
秒:{$second:"$birth"},毫秒:{$millisecond:"$birth"},星期:{$dayOfWeek:"$birth"},全年的第几
周 :{$week:"$birth"}, 全 年 中 的 第 几 天 :{$dayOfYear:"$birth"}, 自 定 义 日 期 格 式:{$dateToString:{format:"%Y 年%m 月%d 日 %H:%M:%S",date:"$birth"}}}}])
{$dateToString:{format:"%Y 年%m月%d 日 %H:%M:%S",date:"$birth"}:自定义日期格式
具体格式如下:
九、 Java 访问 MongoDB
1 连接 MongoDB 数据库
1.1创建工程
在 POM 文件中添加 MongoDB 驱动坐标
<!--添加MongoDB驱动坐标-->
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.8.2</version>
</dependency>
</dependencies>
1.2创建 MongoDB 连接
封装 MongoDBUtil
public class MongoDBUtil {
private static MongoClient client = null;
static{
if(client == null){
client = new MongoClient("192.168.70.156",27017);
}
}
//获取MongoDB数据库
public static MongoDatabase getDatabase(String dbName){
return client.getDatabase(dbName);
}
//获取MongoDB中的集合
public static MongoCollection getCollection(String dbName,String collName){
MongoDatabase database = getDatabase(dbName);
return database.getCollection(collName);
}
}
1.3创建 MongoDB 的认证连接
封装 MongoDBAuthUtil
/**
* 创建MongoDB拦截-使用用户认证
*/
public class MongoDBAuthUtil {
private static MongoClient client = null;
static{
if(client == null){
//创建一个封装用户认证信息
MongoCredential credential = MongoCredential.createCredential("itsxt","develop","itsxtpwd".toCharArray());
//封装MongoDB的地址与端口
ServerAddress address = new ServerAddress("192.168.70.156",27017);
client = new MongoClient(address, Arrays.asList(credential));
}
}
//获取MongoDB数据库
public static MongoDatabase getDatabase(String dbName){
return client.getDatabase(dbName);
}
//获取MongoDB中的集合
public static MongoCollection getCollection(String dbName, String collName){
MongoDatabase database = getDatabase(dbName);
return database.getCollection(collName);
}
}
1.4创建 MongoDB 的池连
封装 MongoDBPoolUtil
/**
* 使用池连的方式获取连接
*/
public class MongoDBPoolUtil {
private static MongoClient client = null;
static{
if(client == null){
MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
builder.connectionsPerHost(10);//每个地址的最大连接数
builder.connectTimeout(5000);//连接超时时间
builder.socketTimeout(5000);//设置读写操作超时时间
ServerAddress address = new ServerAddress("192.168.70.156",27017);
client = new MongoClient(address,builder.build());
}
}
//获取MongoDB数据库
public static MongoDatabase getDatabase(String dbName){
return client.getDatabase(dbName);
}
//获取MongoDB中的集合
public static MongoCollection getCollection(String dbName, String collName){
MongoDatabase database = getDatabase(dbName);
return database.getCollection(collName);
}
}
1.5创建 MongoDB 的认证池连
封装 MongoDBAuthPoolUtil
/**
* 支持用户认证的池连
*/
public class MongoDBAuthPoolUtil {
private static MongoClient client = null;
static{
if(client == null){
MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
builder.connectionsPerHost(10);//每个地址的最大连接数
builder.connectTimeout(5000);//连接超时时间
builder.socketTimeout(5000);//设置读写操作超时时间
MongoCredential credential = MongoCredential.createCredential("itsxt","develop","itsxtpwd".toCharArray());
ServerAddress address = new ServerAddress("192.168.70.156",27017);
client = new MongoClient(address,credential,builder.build());
}
}
//获取MongoDB数据库
public static MongoDatabase getDatabase(String dbName){
return client.getDatabase(dbName);
}
//获取MongoDB中的集合
public static MongoCollection getCollection(String dbName, String collName){
MongoDatabase database = getDatabase(dbName);
return database.getCollection(collName);
}
//创建集合
public static void createCollection(String dbName,String collName){
MongoDatabase database = getDatabase(dbName);
database.createCollection(collName);
}
}
2 操作集合
2.1创建集合
//创建集合
public static void createCollection(String dbName,String collName){
MongoDatabase database = getDatabase(dbName);
database.createCollection(collName);
}
2.2获取集合
//获取MongoDB中的集合
public static MongoCollection getCollection(String dbName, String collName){
MongoDatabase database = getDatabase(dbName);
return database.getCollection(collName);
}
2.3删除集合
//删除集合
public static void dropCollection(MongoCollection coll){
coll.drop();
}
3 操作文档
3.1添加文档
3.1.1添加单个文档
/**
* 添加单个文档
*/
public void inesrtSingleDocument(){
//获取集合
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
// {}---->Document
//append(String key,Object value)---->{key:value}
Document docu = new Document();
docu.append("username","lisi").append("userage",26).append("userdesc","Very Good").append("userlike", Arrays.asList(new String[]{"Music","Sport"}));
collection.insertOne(docu);
}
3.1.2添加多个文档
/**
* 文档的批量添加
*/
public void insertManyDocument(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
List<Document> list = new ArrayList<>();
for(int i=0;i<5;i++){
Document docu = new Document();
docu.append("username","zhangsan"+i);
docu.append("userage",20+i);
docu.append("userdesc","OK"+i);
docu.append("userlike",Arrays.asList(new String[]{"Music","Sport"}));
list.add(docu);
}
collection.insertMany(list);
}
3.2更新文档
3.2.1更新单个文档单个键
/**
* 更新单个文档单个键
*/
public void updateSingleDocumentSingleKey(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
//更新文档
//Filters封装了条件的一个工具类{$set:{userage:28}}
collection.updateOne(Filters.eq("username","lisi"),new Document("$set",new Document("userage",28)));
}
3.2.2更新单个文档多个键
/**
* 更新单个文档多个键
*/
public void updateSingleDocumentManyKey(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
collection.updateOne(Filters.eq("username","zhangsan0"),new Document("$set",new Document("userage",18).append("userdesc","Very Good")));
}
3.2.3更新多个文档中的单个键
/**
* 更新多个文档单个键
*/
public void updateManyDocumentSingleKey(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
collection.updateMany(Filters.ne("username",null),new Document("$set",new Document("userdesc","Very Good")));
}
3.2.4更新多个文档中的多个键
/**
* 更新多个文档多个键
*/
public void updateManyDocumentManyKey(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
collection.updateMany(Filters.ne("username",null),new Document("$set",new Document("userdesc","OK").append("userage",20)));
}
3.2.5更新文档中的数组
/**
* 更新文档中的数组
* {$push:{}}
*/
public void updateDocumentArray(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
collection.updateOne(Filters.eq("username","lisi"),new Document("$push",new Document("userlike","Art")));
}
3.3查询文档
3.3.1查询全部文档
/**
* 查询全部文档
*/
public void selectDocumentAll(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
//返回的是一个文档的迭代器
FindIterable<Document> iterable = collection.find();
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.2根据_id 查询文档-$eq
/**
* 根据_id查询文档
*/
public void selectDocumentById(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable<Document> iterable = collection.find(Filters.eq("_id",new ObjectId("5d398cd64b022206d87d168e")));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.3查询多个文档-$gt
/**
* 根据年龄查询文档,条件是年龄大于19
*/
public void selectDocumentConditionByGt(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.gt("userage",19));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.4查询多个文档-$type
/**
* 根据年龄查询文档,添加是年龄的值是整数类型(number)
*/
public void selectDocumentConditionByType(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.type("userage","number"));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.5查询多个文档-$in
/**
* 查询用户的名字为 zhangsan1,zhangsan2
*/
public void selectDocumentConditionByIn(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.in("username","zhangsan1","zhangsan2"));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.6查询多个文档-$nin
/**
* 查询用户的名字不是 zhangsan1,zhangsan2
*/
public void selectDocumentConditionByNin(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.nin("username","zhangsan1","zhangsan2"));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.7查询多个文档-$regex
/**
* 查询用户的名字是z开头2结尾的。
*/
public void selectDocumentConditionByRegex(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.regex("username", Pattern.compile("^z.*2$")));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.8逻辑运算符-$and
/**
* 查询用户username是zhangsan1并且年龄为20岁的用户
*/
public void selectDocumentConditionUseAnd(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.and(Filters.eq("username","zhangsan1"),Filters.eq("userage",21),Filters.eq("userdesc","Very Good")));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.9逻辑运算符-$or
/**
* 查询用户要求username是list,或者userage是20 或者 userdesc是Very Good
*/
public void selectDocumentConditionUseOr(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.or(Filters.eq("username","lisi"),Filters.eq("userage",20),Filters.eq("userdesc","Very Good")));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.10 逻辑运算符-$and
与$or
联合使用
/**
* 查询文档中username为lisi并且年龄为20岁,或者userdesc为Very Good
*/
public void selectDocumentConditionAndOr(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.or(Filters.and(Filters.eq("username","lisi"),Filters.eq("userage",20)),Filters.eq("userdesc","Very Good")));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.3.11 查询文档-排序处理
/**
* 查询文档中username是z开头的,根据username对结果做降序排序。1升序排序, -1降序排序规则 $sort:{username,-1}
*/
public void selectDocumentSorting(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
FindIterable iterable = collection.find(Filters.regex("username",Pattern.compile("^z"))).sort(new Document("username",-1));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike"));
}
}
3.4日期操作
3.4.1插入系统当前日期
/**
* 插入系统当前日期
*/
public void insertDocumentSystemDate(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
Document docu = new Document();
docu.put("username","wangwu");
docu.put("userage",22);
docu.put("userdesc","Very Good");
docu.put("userlike", Arrays.asList(new String[]{"Music","Art"}));
docu.put("userbirth",new Date());
collection.insertOne(docu);
}
3.4.2插入指定日期
创建日期处理工具类
public class DateUtil {
/**
* Date To String
*/
public static String dateToString(String pattern, Date date){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
return simpleDateFormat.format(date);
}
/**
* String To Date
*/
public static Date stringToDate(String pattern, String date){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
Date d = null;
try{
d = simpleDateFormat.parse(date);
}catch(Exception e){
e.printStackTrace();
}
return d;
}
}
添加指定日期
/**
* 插入指定日期
*/
public void insertDocumentCustoDate(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
Date date= DateUtil.stringToDate("yyyy-MM-dd HH:mm:ss","2019-05-01 13:32:13");
Document docu = new Document();
docu.put("username","zhaoliu");
docu.put("userage",24);
docu.put("userdesc","Very Good");
docu.put("userlike", Arrays.asList(new String[]{"Music","Art"}));
docu.put("userbirth",date);
collection.insertOne(docu);
}
3.4.3查询日期-$eq
/**
* 查询日期:查询用的生日为2019-05-01 13:32:13的用户信息
*/
public void selectDocumentDateUseEq(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
Date date = DateUtil.stringToDate("yyyy-MM-dd HH:mm:ss","2019-05-01 13:32:13");
FindIterable iterable = collection.find(Filters.eq("userbirth",date));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
String temp = DateUtil.dateToString("yyyy-MM-dd HH:mm:ss",(Date) docu.get("userbirth"));
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike")+"\t"+temp);
}
}
3.4.4查询日期-$gt
/**
* 查询日期:查询用的生日大于2019-01-01 00:00:00的用户信息
*/
public void selectDocumentDateUseGt(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
Date date = DateUtil.stringToDate("yyyy-MM-dd HH:mm:ss","2019-01-01 00:00:00");
FindIterable iterable = collection.find(Filters.gt("userbirth",date));
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
String temp = DateUtil.dateToString("yyyy-MM-dd HH:mm:ss",(Date) docu.get("userbirth"));
System.out.println(docu.get("username")+"\t"+docu.get("userage")+"\t"+docu.get("userdesc")+"\t"+docu.get("userlike")+"\t"+temp);
}
}
3.5聚合操作
3.5.1聚合操作-计算文档总数-$sum
需求:查询集合中的文档数量
Mongo Shell:db.dev.aggregate([{$group:{_id:null,count:{$sum:1}}}])
/**
* 需求:查询集合中的文档数量
Mongo Shell:db.dev.aggregate([{$group:{_id:null,count:{$sum:1}}}])
*/
public void selectDocumentAggregateCount(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document sum = new Document();
sum.put("$sum",1);
Document count = new Document();
count.put("_id",null);
count.put("count",sum);
Document group = new Document();
group.put("$group",count);
List<Document> list = new ArrayList<>();
list.add(group);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("count"));
}
}
3.5.2聚合操作-计算值的总和-$sum
需求:查询集合中所有 size 键中的值的总和
Mongo Shell:db.dev.aggregate([{$group:{_id:null,totalSize:{$sum:"$size"}}}])
/**
* 需求:查询集合中所有size键中的值的总和
Mongo Shell:db.dev.aggregate([{$group:{_id:null,totalSize:{$sum:"$size"}}}])
*/
public void selectDocumentAggregateSum(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document sum = new Document();
sum.put("$sum","$size");
Document totalSize = new Document();
totalSize.put("_id",null);
totalSize.put("totalSize",sum);
Document group = new Document();
group.put("$group",totalSize);
List<Document> list = new ArrayList<>();
list.add(group);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("totalSize"));
}
}
3.5.3聚合操作-在分组中计算值的总和-$sum
需求:对 title 进行分组,计算每组中的 size 的总和
Mongo Shell:db.dev.aggregate([{$group:{_id:"$title",totalSize:{$sum:"$size"}}}])
/**
* 需求:对title进行分组,计算每组中的size的总和
Mongo Shell:db.dev.aggregate([{$group:{_id:"$title",totalSize:{$sum:"$size"}}}])
*/
public void selectDocumentAggregateGroupBySum(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document sum= new Document();
sum.put("$sum","$size");
Document totalSize = new Document();
totalSize.put("_id","$title");
totalSize.put("totalSize",sum);
Document group = new Document();
group.put("$group",totalSize);
List<Document> list = new ArrayList<>();
list.add(group);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("totalSize"));
}
}
3.5.4聚合操作-分组前的数据过滤-$match
需求:查询 dev 集合有多少文档的 size 大于 200。
Mongo Shell:
db.dev.aggregate([{$match:{size:{$gt:200}}},{$group:{_id:null,totalSize:{$sum:1}}}])
/**
* 需求:查询dev集合有多少文档的size大于200。
Mongo Shell:
db.dev.aggregate([{$match:{size:{$gt:200}}},{$group:{_id:null,totalSize:{$sum:1}}}])
*/
public void selectDocumentAggregateGroupByWhere(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document gt = new Document();
gt.put("$gt",200);
Document size = new Document();
size.put("size",gt);
Document match = new Document();
match.put("$match",size);
Document sum = new Document();
sum.put("$sum",1);
Document totalSize = new Document();
totalSize.put("_id",null);
totalSize.put("totalSize",sum);
Document group = new Document();
group.put("$group",totalSize);
List<Document> list = new ArrayList<>();
list.add(match);
list.add(group);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("totalSize"));
}
}
3.5.5聚合操作-分组后的数据过滤-$match
需求:查询 dev 集合,根据 title 分组计算出每组的 size 的总和,并过滤掉总和小于 200的文档。
Mongo Shell:
db.dev.aggregate([{$group:{_id:"$title",totalSize:{$sum:"$size"}}},{$match:{totalSize:{$gt: 200}}}])
/**
* 需求:查询dev集合,根据title分组计算出每组的size的总和,并过滤掉总和小于200的文档。
Mongo Shell:
db.dev.aggregate([{$group:{_id:"$title",totalSize:{$sum:"$size"}}},{$match:{totalSize:{$gt:200}}}])
*/
public void selectDocumentAggregateGroupByHaving(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document sum = new Document();
sum.put("$sum","$size");
Document totalSize = new Document();
totalSize.put("_id","$title");
totalSize.put("totalSize",sum);
Document group = new Document();
group.put("$group",totalSize);
//{$match:{totalSize:{$gt:200}}}
Document gt = new Document();
gt.put("$gt",200);
Document mtotalSize = new Document();
mtotalSize.put("totalSize",gt);
Document match = new Document();
match.put("$match",mtotalSize);
List<Document> list = new ArrayList<>();
list.add(group);
list.add(match);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu.get("totalSize"));
}
}
3.5.6聚合操作-$project-聚合投影约束
需求:查询 dev 集合,将数组中的内容拆分显示,并只显示 title 键与 tags 键的值。
Mongo Shell:
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,tags:"$tags",title:"$title"}}])
/**
* 需求:查询dev集合,将数组中的内容拆分显示,并只显示title键与tags键的值。
Mongo Shell:
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,tags:"$tags",title:"$title"}}])
*/
public void selectDocumentProject(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document unwind = new Document();
unwind.put("$unwind","$tags");
Document pro = new Document();
pro.put("_id",0);
pro.put("tags","$tags");
pro.put("title","$title");
Document project = new Document();
project.put("$project",pro);
List<Document> list = new ArrayList<>();
list.add(unwind);
list.add(project);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
System.out.println(cursor.next());
}
}
3.5.7聚合操作-$project-字符串处理
需求:查询 dev 集合,将数组中的内容拆分显示。将 title 字段和 tags 字段的值拼接为一个完整字符串并在 Title_Tags 字段中显示。
Mongo Shell:
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,Title_Tags:{$concat:["$title","-","$tags"]}}}])
/**
* 需求:查询dev集合,将数组中的内容拆分显示。将title字段和tags字段的值拼接为一个完整字符串并在Title_Tags字段中显示。
Mongo Shell:
db.dev.aggregate([{$unwind:"$tags"},{$project:{_id:0,Title_Tags:{$concat:["$title","-","$tags"]}}}])
*/
public void selectDocumentProjectConcat(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document unwind = new Document();
unwind.put("$unwind","$tags");
Document concat = new Document();
concat.put("$concat", Arrays.asList(new String[]{"$title","-","$tags"}));
Document title = new Document();
title.put("_id",0);
title.put("Title_Tags",concat);
Document project = new Document();
project.put("$project",title);
List<Document> list = new ArrayList<>();
list.add(unwind);
list.add(project);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
System.out.println(cursor.next());
}
}
3.5.8聚合操作-$project 算术运算
需求:查询 dev 集合中数据,显示 title 和 size 字段,为 size 字段数据做加 1 操作,显示字段命名为 New_Size。排除那些没有 size 键的文档。
Mongo Shell:
db.dev.aggregate([{$match:{size:{$ne:null}}},{$project:{_id:0,title:1,New_Size:{$add:["$size",1]}}}])
/**
* 需求:查询dev集合中数据,显示title和size字段,为size字段数据做加1操作,显示字段命名为New_Size。排除那些没有size键的文档。
Mongo Shell:
db.dev.aggregate([{$match:{size:{$ne:null}}},{$project:{_id:0,title:1,New_Size:{$add:["$size",1]}}}])
*/
public void selectDocumentProjectAdd(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document ne = new Document();
ne.put("$ne",null);
Document size = new Document();
size.put("size",ne);
Document match = new Document();
match.put("$match",size);
//{$project:{_id:0,title:1,New_Size:{$add:["$size",1]}}}
Document add = new Document();
add.put("$add",Arrays.asList(new Object[]{"$size",1}));
Document new_Size = new Document();
new_Size.put("_id",0);
new_Size.put("title",1);
new_Size.put("New_Size",add);
Document project = new Document();
project.put("$project",new_Size);
List<Document> list = new ArrayList<>();
list.add(match);
list.add(project);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
System.out.println(cursor.next());
}
}
3.5.9聚合操作-$project 日期处理
需求:查询 devtest 集合查询那些有生日的用户,并按照 YYYY 年 mm 月 dd 日 HH:MM:SS 格式显示日期。
Mongo Shell:
db.devtest.aggregate([{$match:{userbirth:{$ne:null}}},{$project:{ 自定义日期格式:{$dateToString:{format:"%Y 年%m 月%d 日 %H:%M:%S",date:"$userbirth"}}}}])
/**
* 需求:查询devtest集合查询那些有生日的用户,并按照YYYY年mm月dd日 HH:MM:SS格式显示日期。
* 注意:如果直接在MongoDB中做日期的格式化处理,那么是按照表示UTC时间来处理的,会少8个小时。建议在程序中
* 通过java.util.Date来做日期的转换。
Mongo Shell:
db.devtest.aggregate([{$match:{userbirth:{$ne:null}}},{$project:{自定义日期格式:{$dateToString:{format:"%Y年%m月%d日 %H:%M:%S",date:"$userbirth"}}}}])
*/
public void selectDocumentProjectDate(){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","devtest");
Document ne = new Document();
ne.put("$ne",null);
Document birth = new Document();
birth.put("userbirth",ne);
Document match = new Document();
match.put("$match",birth);
//{$project:{自定义日期格式:{$dateToString:{format:"%Y年%m月%d日 %H:%M:%S",date:"$userbirth"}}}}
Document format = new Document();
format.put("format","%Y年%m月%d日 %H:%M:%S");
format.put("date","$userbirth");
Document dateToString = new Document();
dateToString.put("$dateToString",format);
Document custoDate = new Document();
custoDate.put("自定义日期格式",dateToString);
Document project = new Document();
project.put("$project",custoDate);
List<Document> list = new ArrayList<>();
list.add(match);
list.add(project);
AggregateIterable iterable = collection.aggregate(list);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
System.out.println(cursor.next());
}
}
3.6分页查询
3.6.1使用 skip 与 limit 方法分页
/**
* 通过skip与limit方法实现分页
*/
public void selectDocumentByPageUseSkipAndLimit(int pageIndex){
int page = (pageIndex-1)*2;
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document condition = new Document("size",new Document("$ne",null));
long countNum = collection.countDocuments(condition);
System.out.println(countNum);
FindIterable iterable = collection.find(condition).skip(page).limit(2);
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu);
}
}
3.6.2优化分页查询
使用条件判断替换 skip 方法
/**
* 通过条件判断实现分页
*/
public void selectDocumentByPageUseCondition(int pageIndex,int pageSize,String lastId){
MongoCollection collection = MongoDBAuthPoolUtil.getCollection("develop","dev");
Document condition = new Document("size",new Document("$ne",null));
long countNum = collection.countDocuments(condition);
System.out.println(countNum);
FindIterable iterable = null;
if(pageIndex == 1){
iterable = collection.find(condition).limit(pageSize);
}else{
if(lastId != null){
condition.append("_id",new Document("$gt",new ObjectId(lastId)));
iterable = collection.find(condition).limit(pageSize);
}
}
MongoCursor<Document> cursor = iterable.iterator();
while(cursor.hasNext()){
Document docu = cursor.next();
System.out.println(docu);
}
}