biancheng-MongoDB教程

目录http://c.biancheng.net/mongodb2/

1NoSQL是什么
2MongoDB是什么
3Windows安装MongoDB
4Linux安装MongoDB
5MacOS安装MongoDB
6MongoDB常用概念
7MongoDB数据类型
8MongoDB数据模型
9MongoDB连接数据库
10MongoDB创建数据库
11MongoDB删除数据库
12MongoDB创建集合
13MongoDB删除集合
14MongoDB插入文档
15MongoDB查询文档
16MongoDB更新文档
17MongoDB删除文档
18MongoDB投影
19MongoDB限制查询条数
20MongoDB排序
21MongoDB索引
22MongoDB聚合查询
23MongoDB复制
24MongoDB分片
25MongoDB备份和恢复
26MongoDB监控
27Java操作MongoDB
28PHP操作MongoDB
29MongoDB关系
30MongoDB DBRefs
31MongoDB覆盖索引查询
32MongoDB查询分析
33MongoDB原子操作
34MongoDB高级索引
35MongoDB索引使用注意事项
36MongoDB ObjectId
37MongoDB MapReduce
38MongoDB全文检索
39MongoDB正则表达式
40MongoDB管理工具
41MongoDB GridFS
42MongoDB固定集合
43MongoDB自动增长

NoSQL是什么

最常见的数据库可以分为下面的两种类型:

    • RDBMS(关系型数据库):常见的关系型数据库有 Oracle、DB2、Microsoft SQL Server、Microsoft Access、MySQL;
    • NoSQL(非关系型数据库):常见的非关系型数据库有 MongoDB、Redis、Voldemort、Cassandra、Riak、Couchbase、CouchDB 等。

相对于 RDBMS(关系型数据库),NoSQL 具有以下优点:

    • 易扩展:NoSQL 数据库种类繁多,但它们都有一个共同的特点,那就是都去掉了关系型数据库的关系型特性,数据与数据之间没有关系,这样就非常容易扩展,无形之间也在架构的层面上带来了可扩展的能力;
    • 大数据量,高性能:NoSQL 数据库都具有非常高的读写性能,尤其是在处理庞大数据时表现优秀;
    • 灵活:NoSQL 随时都可以存储任意类型的数据,无须提前为要存储的数据建立字段;
    • 高可用:NoSQL 在不太影响性能的情况下,就可以方便地实现高可用的架构,比如 Cassandra、HBase 模型,通过复制模型也能实现高可用。

体系框架

NoSQL 整体框架分为四层,由下至上分别为数据持久层(data persistence)、整体分布层(data distribution model)、数据逻辑模型层(data logical model)、和接口层(interface),层次之间相辅相成,协调工作。

1、数据持久层

数据持久层定义了数据的存储形式,主要包括基于内存、硬盘、内存与硬盘相结合、订制可插拔四种形式。

  • 基于内存形式的数据存取速度最快,但可能会造成数据丢失;
  • 基于硬盘的数据可能保存很久,但存取速度较基于内存的形式慢;
  • 内存和硬盘相结合的形式,结合了前两种形式的优点,既保证了速度,又保证了数据不丢失;
  • 订制可插拔则保证了数据存取具有较高的灵活性。

2、数据分布层

数据分布层定义了数据是如何分布的,相对于关系型数据库,NoSQL 可选的机制比较多,主要有三种形式:

  • CAP 支持:可用于水平扩展;
  • 多数据中心支持:可以保证在横跨多数据中心时也能够平稳运行;
  • 动态部署支持:可以在运行着的集群中动态地添加或删除节点。

3、数据逻辑层

数据逻辑层表述了数据的逻辑表现形式,与关系型数据库相比,NoSQL 在逻辑表现形式上相当灵活,主要有四种形式:

  • 键值模型:这种模型在表现形式上比较单一,却有很强的扩展性;
  • 列式模型:这种模型相比于键值模型能够支持较为复杂的数据,但扩展性相对较差;
  • 文档模型:这种模型对于复杂数据的支持和扩展性都有很大优势;
  • 图模型:这种模型的使用场景不多,通常是基于图数据结构的数据定制的。

4、接口层

接口层为上层应用提供了方便的数据调用接口,提供的选择远多于关系型数据库。接口层提供了五种选择,分别是 Rest、Thrift、Map/Reduce、Get/Put 和特定语言 API,使得应用程序在与数据库交互时更加方便。

NoSQL 分层架构并不代表每个产品在每一层只有一种选择。相反,这种分层设计提供了很大的灵活性和兼容性,每种数据库在不同层面可以支持多种特性。

适用场景

NoSQL 数据库在以下的这几种情况下比较适用:

  • 数据模型比较简单;
  • 对灵活性要求很强的系统;
  • 对数据库性能要求较高;
  • 不需要高度的数据一致性;
  • 对于给定 key,比较容易映射复杂值的环境。

 MongoDB

市面上的数据库软件众多,我们为什么要选择 MongoDB 呢?换句话说就是 MongDB 有什么优势呢?下面列举了几点原因。

1) 面向文档

由于 MongoDB 是 NoSQL 类型的数据库,它不是像关系类型数据库那样以固定的格式存储数据,而是将数据存储在文档中,这使 MongoDB 非常灵活,可以适应实际的商业环境和需求;

2) 临时查询

MongoDB 支持按字段、范围和正则表达式查询并返回文档中的数据;

3) 索引

可以创建索引以提高 MongoDB 中的搜索性能,文档中的任何字段都可以建立索引;

4) 复制

MongoDB 支持高可用性的副本集。副本集由两个或多个 MongoDB 数据库实例组成,每个副本集成员可以随时充当主副本或辅助副本的角色,主副本是与客户端交互并执行所有读/写操作的主服务器。辅助副本使用内置复制维护主副本种的数据。当主副本失败时,副本集将自动切换到辅助副本,然后将辅助副本作为主服务器;

5) 负载平衡

MongoDB 可以在多台服务器上运行,以平衡负载或复制数据,以便在硬件出现故障时保持系统正常运行。

适用场景

MongoDB 的主要目标是在键/值存储方式和传统的 RDBMS(关系型数据库)系统之间架起一座桥梁,它集两者的优势于一身。根据官方网站的描述,MongoDB 适用于以下场景。

1) 网站数据

MongoDB 非常适合实时的插入、更新与查询数据,并具备网站实时存储数据所需的复制及高度伸缩的特性;

2) 缓存

由于性能很高,MongoDB 也适合作为信息基础设施的缓存层,在系统重启之后,由 MongoDB 搭建的持久化缓存层可以避免下层的数据源过载;

3) 庞大的、低价值的数据

使用传统的关系型数据库存取大量数据时,数据库的运行效率往往并不尽人意,而 MongoDB 的出现使这个问题迎刃而解,MongoDB 非常适合庞大数据的存储;

4) 高伸缩性的场景

MongoDB 内置了 MapReduce 引擎,因此非常适合由数十或数百台服务器组成的数据库;

5) 用于对象及 JSON 数据的存储

MongoDB 的 BSON 数据格式非常适合文档化格式的存储及查询。

MacOS安装MongoDB(图解)

下载 MongoDB

https://www.mongodb.com/try/download/community

与在 Linux 系统下安装 MongoDB 相同,首先我们需要在 MongoDB 的官网获得 MongoDB 安装包的下载链接,如下图所示:

 

 

 

得到下载链接后,使用cd命令进入 /usr/local 目录,然后使用wget命令下载 MongoDB 的压缩包,命令如下:

cd  /usr/local
sudo wget https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-4.4.3.tgz

安装 MongoDB

待压缩包下载完成后就可以尝试安装 MongoDB 了,具体的安装步骤如下:

【步骤 1】解压缩刚刚下载的压缩包,并将其重命名为 mongodb:

sudo tar -zxvf mongodb-macos-x86_64-4.4.3.tgz          # 解压 MongoDB 压缩包
sudo mv mongodb-osx-ssl-x86_64-4.0.17/ mongodb   # 重命名解压得到的文件夹

【步骤 2】在 /usr/local/mongodb 目录下新建两个文件夹 data 和 log,用于存储 MongoDB 的数据和日志。

sudo mkdir -p /usr/local/mongodb/data
sudo mkdir -p /usr/local/mongodb/log

使用如下命令为当前用户分配目录的读写权限:

sudo chown biancheng /usr/local/mongodb/data
sudo chown biancheng /usr/local/mongodb/log

其中“biancheng”为当前的用户名,您需要修改为您当前系统的用户名。

【步骤 3】配置 PATH。在终端中输入open -e .bash_profile命令打开 bash_profile 配置文件,然后将 MongoDB 的安装目录下的 bin 目录添加到环境变量中,如下所示:

export PATH=${PATH}:/usr/local/mongodb/bin

编辑完成后保存并退出,然后使用source .bash_profile命令使配置立即生效。

【步骤 4】使用下面的命令来启动 MongoDB 服务:

mongod --dbpath /usr/local/mongodb/data --logpath /usr/local/mongodb/log/mongo.log --fork

参数说明如下:

  • --dbpath 用来设置数据的存放目录;
  • --logpath 用来设置日志的存放目录;
  • --fork 用来设置在后台运行。


至此 MongoDB 就安装完成了。

验证安装

您可以使用mongod -version命令来验证 MongoDB 是否安装成功,如果出现类似下面所示的内容,则说明 MongoDB 安装成功。

mongod -version

db version v4.0.10
git version: c389e7f69f637f7a1ac3cc9fae843b635f20b766
allocator: tcmalloc
modules: none
build environment:
    distmod: 2008plus-ssl
    distarch: x86_64
    target_arch: x86_64

MongoDB常用概念解析

集合

集合就是一组 MongoDB 文档的组合,类似于关系型数据库(例如 MySQL)中的数据表。集合存在于数据库中,且没有固定的结构,您可以向集合中插入不同格式或类型的数据。

文档

文档是 MongoDB 中数据的基本单位,由 BSON 格式(一种计算机数据交换格式,类似于 JSON)的键/值对组成,类似于关系型数据库中的一行行数据,但要相对复杂一些。

文档具有动态模式,所谓动态模式就是同一集合中的文档不需要具有相同的字段,即使是相同的字段也可以是不同的类型,这与关系型数据库有很大的区别,也是 MongoDB 最突出的特点之一。

下表列举了关系型数据库与 MongoDB 中的一些差异:

关系型数据库MongoDB解释说明
database database 数据库
table collection 数据表/集合
row document 数据行/文档
column field 字段/域
index index 索引
table joins   表连接,MongoDB 中不支持
primary key primary key 主键,MongoDB 会自动将 _id 字段设置为主键


下面的示例中展示了一个简单的文档结构:

MongoDB数据类型汇总

MongoDB 中常用的几种数据类型:

数据类型描述
String 字符串类型,是最常用的数据类型,不过在 MongoDB 中,只有 UTF-8 编码的字符串才是合法的
Integer 整型,用于存储数值。根据您使用服务器的不同,整型可以分为 32 位或 64 位两种
Boolean 布尔型,用于存储布尔类型的值(true/false)
Double 双精度浮点型,用于存储浮点型(小数)数据
Min/Max keys 将一个值与 BSON 元素的最低值和最高值相对比
Array 数组类型,用于将数组、列表或多个值存储在一个键中
Timestamp 时间戳,记录文档修改或添加的具体时间
Object 用于内嵌文档
Null 用于创建空值
Symbol 符号,该数据类型于字符串类型类似,不同的是,它一般用于采用特殊符号类型的语言
Date 日期时间,用 UNIX 时间格式来存储当前日期或时间,您可以创建 Date 对象并将 date、month、year 的值传递给 Date 对象来指定自己的日期时间
Object ID 对象 ID,用于创建文档的 ID
Binary Data 二进制数据,用于存储二进制数据
Code 代码类型,用于在文档中存储 JavaScript 代码
Regular expression 正则表达式类型,用于存储正则表达式


下面简单介绍一下其中几个比较重要的类型:

Object ID

Object ID 类似于关系型数据库中的主键 ID,在 MongoDB 中 Object ID 由 12 字节的字符组成,其中:

  • 前 4 个字节表示当前的 Unix 时间戳;
  • 之后的 3 个字节是当前设备的机器标识码;
  • 再之后的 2 个字节是 MongoDB 服务器的进程 id;
  • 最后 3 个字节为计数器,计数器的起始值随机获得。
601e2b6b aa203c c89f 2d31aa
  ↑      ↑     ↑     ↑
时间戳  机器码 进程id 计数器

Timestamps

Timestamps(时间戳)类型用来在 MongoDB 的内部使用,与 Date 类型不同,Timestamps 由一个 64 位的值构成,其中:

  • 前 32 位是一个 Unix 时间戳(由 Unix 纪元(1970.1.1)开始到现在经过的秒数);
  • 后 32 位是一秒内的操作序数。


在一个 MongoDB 实例中,时间戳是唯一的,而且主要在 MongoDB 内部使用,在应用开发中,您可以使用 Date 类型来定义时间日期。

Date

Date 类型是一个 64 位的对象,其中存放了从 Unix 纪元(1970.1.1)开始到现在经历的毫秒数,Date 类型是有符号的,负值则表示 1970.1.1 之前的时间。

> var mydate = new Date()
> mydate
ISODate("2021-02-06T08:50:37.024Z")
> typeof mydate
object

数据模型设计

嵌入式数据模型

嵌入式数据模型也称为非规范化数据模型,在该模型中您可以将所有相关的数据存储到一个文档中,例如我们在三个不同的文档中分别存储了一个员工的个人信息、联系方式和地址等信息,您还可以将这些信息整合到一个文档中,如下所示:

_id: ObjectId("601f4be6e646844cd045c8a4"),
    Emp_ID: "10025AE336",
    Personal_details:{
        First_Name: "Radhika",
        Last_Name: "Sharma",
        Date_Of_Birth: "1995-09-26"
    },
    Contact: {
        e-mail: "biancheng.net@gmail.com",
        phone: "9848022338"
    },
    Address: {
        city: "Hyderabad",
        Area: "Madapur",
        State: "Telangana"
    }

规范化数据模型

在规范化数据模型中,您可以通过引用来将原始文档与子文档关联起来,例如您可以将上面的文档信息以规范化数据模型重写为以下几个文档:

Employee:

{
    _id: ObjectId("601f4be6e646844cd045c8a4"),
    Emp_ID: "10025AE336"
}

Personal_details:

{
    _id: ObjectId("601f50bae646844cd045c8a5"),
    empDocID: ObjectId("601f4be6e646844cd045c8a4"),
    First_Name: "Radhika",
    Last_Name: "Sharma",
    Date_Of_Birth: "1995-09-26"
}

MongoDB连接数据库

MongoDB 的标准 URI 连接语法如下所示:

mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]

语法说明如下:

  • mongodb://:这是固定的格式,必须要指定;
  • username:password@:可选项,如果设置,在连接数据库服务器之后,驱动会尝试以给出的用户名(username)和密码(password)登录这个数据库;
  • host1:必填参数,用来指定要连接服务器的地址,如果要连接复制集,则需要指定多个主机地址;
  • portX:可选项,用来指定端口,如果不填,默认为 27017;
  • /database:可选项,如果指定了 username:password@,则连接并验证登录指定的数据库,若不指定,则默认打开 test 数据库;
  • ?options:可选项,用来定义连接选项,连接选项的可选值如下表所示,如果不使用 /database,则前面需要使用/与前面的内容分隔,所有连接选项都是键值对 name=value 的形式,键值对之间通过&;(分号)隔开。

 

选项描述
connect=direct | replicaset
  • direct:直接建立一个到服务器的连接。如果指定了多个 host,将按先后顺序挨个尝试建立连接,直到连接建立成功为止。如果只指定了一个 host,则 direct 为默认值;
  • replicaset:使用 creplica set semantics 建立连接(即使只提供了一个 host),指定的 host 作为种子列表来查找完整的 replica set,当指定多个 host 时 replicaset 为默认值。
replicaSet=name 验证建立连接的 replica set 的名称,应用于 connect=replicaSet。
slaveOk=true | false
  • true:在 connect=direct 模式下,驱动会连接第一台机器,即使这台服务器不是主服务器。在 connect=replicaSet 模式下,驱动会发送所有的写请求到主服务器并且把读取操作分布在其他从服务器;
  • false:在 connect=direct 模式下,驱动会自动寻找主服务器. 在 connect=replicaSet 模式下,驱动仅会连接主服务器,并且所有的读写命令都会连接到主服务器。
safe=true | false
  • true:在执行更新操作之后,驱动都会发送 getLastError 命令来确保更新成功;
  • false:在每次更新之后,驱动不会发送 getLastError 来确保更新成功。
w=n w 代表 server 的数量(应用于 safe=true):
  • w=-1:不等待,不做异常检查;
  • w=0:不等待,只返回网络错误;
  • w=1:检查本机,并检查网络错误;
  • w>1:检查 w 个 server,并返回网络错误。
wtimeoutMS=ms 设置写操作的超时事件,应用于 safe=true。
fsync=true | false 设置是否等待刷新数据到磁盘,应用于 safe=true。
journal=true | false 如果设置为 true,则等待数据写入到日志并刷新到磁盘,应用于 safe=true。
connectTimeoutMS=ms 可以打开连接的时间。
socketTimeoutMS=ms 发送和接受 sockets 的时间。


下面通过一些简单的示例来演示一下:

连接到一个运行在本机的,端口为 27017 的 MongoDB:

mongodb://localhost

连接到一个运行在本机的,端口为 27017 的 MongoDB,并以用户名"fred"和密码"foobar"登录,登录后将默认使用 admin 数据库:

mongodb://fred:foobar@localhost

连接到一个运行在本机的,端口为 27017 的 MongoDB,并以用户名"fred"和密码"foobar"登录,登录后使用 baz 数据库:

mongodb://fred:foobar@localhost/baz

连接到一个 replica pair,一台服务器在 c.biancheng.net,另一台在 www.biancheng.net:

mongodb://c.biancheng.net:27017,www.biancheng.net:27017

连接到本机的一个 replica set,端口分别为 27017、27018、27019:

mongodb://localhost,localhost:27018,localhost:27019

连接 replica set 中的三台服务器, 写入操作应用在主服务器 并且分布查询到从服务器:

mongodb://host1,host2,host3/?slaveOk=true

直接连接第一个服务器,无论该服务器是否为 replica set 的一部分,也无论它是主服务器还是从服务器:

mongodb://host1,host2,host3/?connect=direct;slaveOk=true

注意:上述的连接主要用于在您偏好使用某台服务器,但又有可供替换的服务器时。

使用安全模式连接到 localhost:

mongodb://localhost/?safe=true

以安全模式连接到 replica set,并且等待至少两个复制服务器成功写入,超时时间设置为 2 秒:

mongodb://host1,host2,host3/?safe=true;w=2;wtimeoutMS=2000

MongoDB创建数据库

如果我们想要创建一个名为 biancheng 的数据库,则可以使用如下所示的命令:

> use biancheng
switched to db biancheng

数据库创建成功后,您可以使用db命令来查看当前选择的数据库,如下所示:

> db
biancheng

如果您想查看 MongoDB 中所有的数据库,则可以使用show dbs命令,如下所示:

> show dbs
admin   0.000GB
config   0.000GB
local   0.000GB

通过上面的运行结果您会发现,我们刚刚创建的数据库(biancheng)并不在列表中,这是因为该数据库中并没有数据,要显示它您还需要向该数据库中插入一些数据才行,如下所示:

> db.website.insert({title:"编程帮",url:"www.biancheng.net"})
WriteResult({ "nInserted" : 1 })
> show dbs
admin      0.000GB
biancheng      0.000GB
config      0.000GB
local      0.000GB

MongoDB删除数据库

MongoDB 中删除当前数据库的语法格式如下:

db.dropDatabase()

注意:使用 db.dropDatabase() 命令会删除当前数据库,在删除数据库之前您可以使用 db 命令来查看当前数据库的名称。

下面通过示例来演示一下如何删除指定的数据库。

首先,使用show dbs命令来查看 MongoDB 中所有的数据库,如下所示:

> show dbs
admin      0.000GB
biancheng  0.000GB
config     0.000GB
local      0.000GB

假如我们要删除名为 biancheng 的数据库,则需要先使用use命令进入该数据库,然后再使用db.dropDatabase()命令来删除该数据库,如下所示:

> use biancheng
switched to db biancheng
> db.dropDatabase()
{ "dropped" : "biancheng", "ok" : 1 }

注意:使用 db.dropDatabase() 命令会删除当前数据库,在删除数据库之前您可以使用 db 命令来查看当前数据库的名称。

MongoDB创建集合

在 MongoDB 中,您可以使用 createCollection() 方法来创建集合,语法格式如下:

db.createCollection(name, options)

参数说明如下:

  • name: 要创建的集合名称;
  • options: 可选参数, 指定有关内存大小及索引的选项,可选值如下表所示:

 

字段类型描述
capped 布尔 (可选)如果为 true,则创建固定集合,固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档,注意:如果该值为 true,则必须指定 size 参数
autoIndexId 布尔 (可选)如为 true,则自动在 _id 字段创建索引,默认为 false,注意:MongoDB 3.2 之后不再支持该参数
size 数值 (可选)为固定集合指定一个最大值,即字节数,如果 capped 为 true,则需要指定该字段
max 数值 (可选)指定固定集合中包含文档的最大数量


当我们在集合中插入文档时,MongoDB 会首先检查固定集合的 size 字段,然后检查 max 字段。

【示例】创建一个名为“bianchengbang”的数据库,并创建一个名为“user”的集合:

> use bianchengbang
switched to db bianchengbang
> db.createCollection("user")
{ "ok" : 1 }

集合创建完成后,您可以使用show collections命令或者show tables命令来查看数据库中的集合:

> show collections
user
> show tables
user
【示例】创建固定集合“mycol”,整个集合空间大小为 102400 KB, 文档最大个数为 1000 个。
> db.createCollection("mycol", { capped : true, autoIndexId : true, size : 102400, max : 1000 } )
{
    "note" : "the autoIndexId option is deprecated and will be removed in a future release",
    "ok" : 1
}
> show tables
mycol
user

MongoDB删除集合

db.collection_name.drop()

MongoDB插入文档(3中方法)

insert() 与 save() 方法

您可以使用 MongoDB 中的 insert() 或 save() 方法向集合中插入文档,语法如下:

db.collection_name.insert(document)
db.collection_name.save(document)

语法说明如下:

    • save():如果 _id 主键存在则更新数据,如果不存在就插入数据。不过该方法在新版本的 MongoDB 中已废弃,您可以使用 insertOne() 或 replaceOne() 方法来代替;
    • insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,并提示主键重复,不保存当前数据。

【示例】使用 insert() 方法向集合中插入文档:

> db.user.insert([
... {
... name:"张三",
... phone:"15012345678"
... },{
... name:"李四",
... phone:"18687654321"
... },{
... name:"王五",
... phone:"13100001111"
... }
... ])

insertOne() 方法

insert() 方法可以同时插入多个文档,但如果您只需要将一个文档插入到集合中的话,可以使用 insertOne() 方法,该方法的语法格式如下:

db.collection_name.insertOne(document)

insertMany() 方法

与 insert() 方法相同,您可以使用 insertMany() 方法向集合中插入多个文档,但在使用 insertMany() 方法时您需要向方法中传递一个文档数组,如下例所示:

MongoDB查询文档

find() 方法

想要查询集合中的文档,可以使用 MongoDB 中的 find() 方法,find() 方法可以将查询结果以非结构化的方式展示出来,其语法格式如下:

db.collection_name.find(query, projection)

语法说明如下:

  • query:可选参数,使用查询操作符指定查询条件;
  • projection:可选参数,使用投影操作符指定返回的键。查询时若要返回文档中所有键值,只需省略该参数即可(默认省略)。

 

findOne() 方法

除了可以使用 find() 方法外,还可以使用 findOne() 方法查询集合中的文档,但 findOne() 方法仅能返回一个查询到的文档,其语法格式如下:

db.collection_name.findOne(query, projection)

【示例】使用 findOne() 方法从集合中查询“title”=“MongoDB教程”的文档:

> db.mycol.findOne({title:"MongoDB教程"})
{
        "_id" : ObjectId("6031c02ae492ab9be9450302"),
        "title" : "MongoDB教程",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "编程帮",
        "url" : "http://www.biancheng.net",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 999
}

条件查询

MongoDB 中也支持类似关系型数据库中 where 字句的条件查询,下表中列举了 MongoDB 中条件查询与关系型数据库中 where 字句的比较:

操作格式范例关系型数据库中的类似语句
等于 {<key>:<value>} db.col.find({"by":"编程帮"}) where by = '编程帮'
小于 {<key>:{$lt:<value>}} db.col.find({"likes":{$lt:50}}) where likes < 50
小于或等于 {<key>:{$lte:<value>}} db.col.find({"likes":{$lte:50}}) where likes <= 50
大于 {<key>:{$gt:<value>}} db.col.find({"likes":{$gt:50}}) where likes > 50
大于或等于 {<key>:{$gte:<value>}} db.col.find({"likes":{$gte:50}}) where likes >= 50
不等于 {<key>:{$ne:<value>}} db.col.find({"likes":{$ne:50}}) where likes != 50
数组中的值 {<key>:{$in:[<value1>, <value2>, ...<valueN>]}} db.col.find({title:{$in:["编程帮", "MongoDB教程"]}}) where title in("编程帮", "MongoDB教程")
不数组中的值 {<key>:{$nin:[<value1>, <value2>, ...<valueN>]}} db.col.find({title:{$nin:["编程帮", "MongoDB教程"]}}) where title not in("编程帮", "MongoDB教程")

AND条件语句

MongoDB 的 find() 方法可以传入多个键(key),每个键以逗号隔开,类似于常规 SQL 语句中的 AND 条件语句。语法格式如下:

db.collection_name.find({$and:[{<key1>:<value1>}, {<key2>:<value2>}], ...})

【示例】在集合中查询“title”=“MongoDB教程”同时“by”=“编程帮”的文档:

> db.mycol.find({$and:[{title:"MongoDB教程"}, {by:"编程帮"}]}).pretty()
{
        "_id" : ObjectId("6031c02ae492ab9be9450302"),
        "title" : "MongoDB教程",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "编程帮",
        "url" : "http://www.biancheng.net",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 999
}

上面给出的示例等效于 SQL 语句中的“where by ='编程帮' AND title ='MongoDB教程'”。您可以在 find() 方法中传递任意数量的键/值对。另外,在使用 AND 条件语句时您也可以省略其中的 $and 关键字,如下所示:

> db.mycol.find({title:"MongoDB教程", by:"编程帮"}).pretty()
{
        "_id" : ObjectId("6031c02ae492ab9be9450302"),
        "title" : "MongoDB教程",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "编程帮",
        "url" : "http://www.biancheng.net",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 999
}

OR 条件语句

MongoDB 中的 OR 条件语句需要使用 $or 关键字,语法格式如下:

db.collection_name.find({$or:[{<key1>: <value1>}, {<key2>:<value2>}]})

MongoDB更新文档

update() 方法

update() 方法用于更新现有文档中的值,其语法格式如下:

db.collection_name.update(
    <query>,
    <update>,
    {
        upsert: <boolean>,
        multi: <boolean>,
        writeConcern: <document>
    }
)

 

参数说明如下:

  • query:update 的查询条件,类似 SQL 中 update 语句内 where 后面的内容;
  • update:update 的对象和一些更新的操作符(如 $、$inc...)等,也可以理解为 SQL 中 update 语句内 set 后面的内容;
  • upsert:可选参数,默认值为 false,用来定义当要更新的记录不存在时,是否当作新记录插入到集合中,当值为 true 时表示插入,值为 false 时不插入;
  • multi:可选参数,默认值为 false,用来表示只更新找到的第一条记录,当值为 true 时,则把按条件查出来的多条记录全部更新;
  • writeConcern:可选参数,用来定义抛出异常的级别。

接着使用 update() 方法修改 title 为“MongoDB教程”的文档中的内容。

> db.course.update({title:"MongoDB教程"},{$set:{url:"http://www.biancheng.net/mongodb/index.html"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.course.find().pretty()
{
        "_id" : ObjectId("603209d8e492ab9be9450304"),
        "title" : "MongoDB教程",
        "author" : "编程帮",
        "url" : "http://www.biancheng.net/mongodb/index.html"
}
{
        "_id" : ObjectId("603209d8e492ab9be9450305"),
        "title" : "HTML教程",
        "author" : "编程帮",
        "url" : "http://www.biancheng.com/html/index.html"
}

save() 方法

MongoDB 中的 save() 方法可以使用传入的文档来替换已有文档,若 _id 主键存在则更新已有文档,若 _id 主键不存在则作为新数据插入到集合中。语法格式如下:

db.collection_name.save(
    <document>,
    {
        writeConcern: <document>
    }
)

参数说明如下:
    • document : 文档数据;
    • writeConcern :可选,抛出异常的级别。

MongoDB删除文档

db.collection_name.remove(
    <query>,
    {
        justOne: <boolean>,
        writeConcern: <document>
    }
)

然后使用 remove() 方法删除集合中 title 为“MongoDB教程”的文档。

> db.course.remove({'title':'MongoDB教程'})
WriteResult({ "nRemoved" : 1 })

参数说明:

    • query:可选参数,定义要删除文档的条件;
    • justOne:可选参数,如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档;
    • writeConcern:可选参数,定义抛出异常的级别。

MongoDB limit()与skip():限制查询条数

limit() 方法

MongoDB 中要限制查询的记录条数可以使用 limit() 方法,该方法接收一个数字作为参数,用来设置要显示的记录数,如下所示:

db.collection_name.find().limit(number)

【示例】假设在集合“course”中有如下数据:

> db.course.find()
{ "_id" : ObjectId("60331a7eee79704753940391"), "title" : "HTML教程", "author" : "编程帮", "url" : "http://www.biancheng.com/html/index.html" }
{ "_id" : ObjectId("60331a7eee79704753940392"), "title" : "C#教程", "author" : "编程帮", "url" : "http://www.biancheng.com/csharp/index.html" }
{ "_id" : ObjectId("60331a7eee79704753940393"), "title" : "MongoDB教程", "author" : "编程帮", "url" : "http://www.biancheng.com/mongodb/index.html" }

若要查询其中的两条,则可以像下面这样:

> db.course.find({},{"title":1,_id:0}).limit(2)
{ "title" : "HTML教程" }
{ "title" : "C#教程" }

注意:如果没有指定 limit() 方法中的参数,则会显示集合中的所有数据。

skip() 方法

除了 limit() 方法外,MongoDB 中还提供了另一种方法 skip(),它同样也可以接收一个数字类型的参数,用来设置要跳过的文档数。其语法格式如下:

db.collection_name.find().skip(number)

【示例】查询集合中的所有文档,并跳过两条记录:

> db.course.find({},{"title":1,_id:0}).skip(2)
{ "title" : "MongoDB教程" }

MongoDB排序:sort()方法

db.collection_name.find().sort({key:1})

其中 key 用来定义要根据那个字段进行排序,后面的值 1 则表示以升序进行排序,若要以降序进行排序则需要将其设置为 -1。

若要将集合中的数据按照 title 字段降序排列,示例代码如下:

> db.course.find({}, {"title":1,_id:0}).sort({"title":-1})
{ "title" : "MongoDB教程" }
{ "title" : "HTML教程" }
{ "title" : "C#教程" }

MongoDB索引

createIndex() 方法

索引是特殊的数据结构,存储在一个方便遍历和读取的数据集合中。索引在任何数据库中都非常重要,通过使用索引,可以大大提高查询语句的执行效率。

MongoDB 中您可以使用 createIndex() 方法来创建索引,其语法格式如下:

db.collection_name.createIndex(keys, options)

参数说明如下:

  • keys:由键/值对组成,其中键用来定义要创建索引的字段,值用来定义创建索引的顺序,1 表示按升序创建索引,-1 表示按降序来创建索引;
  • options:可选参数,其中包含一组控制索引创建的选项,可选值如下表所示。

 

参数类型描述
background Boolean 可选参数,当值为 true 时,表示在后台构建索引,避免在创建索引的过程阻塞其它数据库操作,默认值为 false
unique Boolean 创建唯一索引,当值为 true 时表示创建唯一索引,以避免重复数据的插入,默认为 false
name string 索引的名称。如果未指定,MongoDB 将通过连接索引的字段名和排序顺序生成一个索引名称
dropDups Boolean 在建立唯一索引时是否删除重复记录,设置为 true 则表示创建唯一索引,默认值为 false,3.0 版本之后废弃
sparse Boolean 对文档中不存在的字段数据不启用索引,这个参数需要特别注意,如果设置为 true 的话,则在索引字段中不会查询出不包含对应字段的文档。默认值为 false
expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL 设定,设定集合的生存时间
v index version 索引的版本号,默认的索引版本取决于 mongod 创建索引时运行的版本
weights document 索引权重值,数值在 1 到 99999 之间,表示该索引相对于其他索引字段的得分权重
default_language string 对于文本索引,该语言用于确定停用词列表以及词干分析器和令牌生成器的规则,默认为英语
language_override string 对于文本索引,指定文档中包含要替代默认语言的语言的字段名称,默认值为 language


【示例】为集合“course”中的 title 字段创建索引:

> db.course.createIndex({"title":1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

dropIndex() 方法

除了可以创建索引外,您还可以使用 dropIndex() 方法来删除指定的索引,其语法格式如下:

db.collection_name.dropIndex(index)

其中 index 用来指定要删除的索引,可以是索引名称key的形式,也可以是{key:1}的形式。

【示例】删除集合“course”中“title”字段的索引:

> db.course.dropIndex({"title":1})
{ "nIndexesWas" : 3, "ok" : 1 }

dropIndexes() 方法

dropIndexes() 方法同样用来删除索引,与 dropIndex() 方法不同 dropIndexes() 方法可以同时删除集合中的多个索引,其语法格式如下:

db.collection_name.dropIndexes()

注意:在使用 dropIndexes() 方法不需要提供任何参数。

【示例】同时删除集合“course”中 title 与 author 字段的索引:

> db.course.dropIndexes()
{
        "nIndexesWas" : 2,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : 1
}

getIndexes() 方法

getIndexes() 方法可以获取集合中所有索引,语法格式如下:

db.collection_name.getIndexes()

MongoDB聚合查询

aggregate() 方法

您可以使用 MongoDB 中的 aggregate() 方法来执行聚合操作,其语法格式如下:

db.collection_name.aggregate(aggregate_operation)

若要统计每个作者“author”一共编写了多少教程,可以使用下面的 aggregate() 方法:

> db.course.aggregate([{$group : {_id : "$author", sum : {$sum : 1}}}])
{ "_id" : "编程帮", "sum" : 3 }

上述示例类似于 SQL 语句中的SELECT author, count(*) FROM course GROUP BY author

下表中展示了一些聚合表达式:

表达式描述实例
$sum 计算总和 db.mycol.aggregate([{$group : {_id : "$author", num_tutorial : {$sum : "$likes"}}}])
$avg 计算平均值 db.mycol.aggregate([{$group : {_id : "$author", num_tutorial : {$avg : "$likes"}}}])
$min 获取集合中所有文档对应值得最小值 db.mycol.aggregate([{$group : {_id : "$author", num_tutorial : {$min : "$likes"}}}])
$max 获取集合中所有文档对应值得最大值 db.mycol.aggregate([{$group : {_id : "$author", num_tutorial : {$max : "$likes"}}}])
$push 在结果文档中插入值到一个数组中 db.mycol.aggregate([{$group : {_id : "$author", url : {$push: "$url"}}}])
$addToSet 在结果文档中插入值到一个数组中,但不创建副本 db.mycol.aggregate([{$group : {_id : "$author", url : {$addToSet : "$url"}}}])
$first 根据资源文档的排序获取第一个文档数据 db.mycol.aggregate([{$group : {_id : "$author", first_url : {$first : "$url"}}}])
$last 根据资源文档的排序获取最后一个文档数据 db.mycol.aggregate([{$group : {_id : "$author", last_url : {$last : "$url"}}}])

管道

在 UNIX 命令中,管道意味着可以将某些操作的输出结果作为下一个命令的参数,以此类推。MongoDB 中同样也支持管道,即 MongoDB 会在一个管道处理完毕后将结果传递给下一个管道处理,而且管道操作是可以重复的。

下面介绍了聚合框架中几个常用的操作:

  • $project:用于从集合中选择要输出的字段;
  • $match:用于过滤数据,只输出符合条件的文档,可以减少作为下一阶段输入的文档数量;
  • $group:对集合中的文档进行分组,可用于统计结果;
  • $sort:将输入文档进行排序后输出;
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档;
  • $limit:用来限制 MongoDB 聚合管道返回的文档数量;
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。


下面通过几个简单的示例来演示 MongoDB 中管道的使用:

1) $project

【示例】使用 $project 来选择要输出的字段:

> db.course.aggregate({$project:{title:1, author:1}}).pretty()
{
        "_id" : ObjectId("60331a7eee79704753940391"),
        "title" : "HTML教程",
        "author" : "编程帮"
}
{
        "_id" : ObjectId("60331a7eee79704753940392"),
        "title" : "C#教程",
        "author" : "编程帮"
}
{
        "_id" : ObjectId("60331a7eee79704753940393"),
        "title" : "MongoDB教程",
        "author" : "编程帮"
}

通过运行结果可以看出,文档中的 _id 字段默认是选中的,如果不想显示 _id 字段的话,可以像下面这样:

> db.course.aggregate({$project:{_id:0, title:1, author:1}}).pretty()
{ "title" : "HTML教程", "author" : "编程帮" }
{ "title" : "C#教程", "author" : "编程帮" }
{ "title" : "MongoDB教程", "author" : "编程帮" }

2) $skip

【示例】使用 $skip 跳过指定数量的文档:

> db.course.aggregate({$skip:2}).pretty()
{
        "_id" : ObjectId("60331a7eee79704753940393"),
        "title" : "MongoDB教程",
        "author" : "编程帮",
        "url" : "http://www.biancheng.com/mongodb/index.html"
}

MongoDB复制(副本集)

 

为什么使用复制

下面列举了几个使用复制的好处:

  • 确保您数据的安全;
  • 保障数据的高可用性;
  • 数据恢复;
  • 维护过程无需停机(例如备份、索引重建、压缩);
  • 分布式读取数据;
  • 副本集对应用程序是透明的。

复制的工作方式

MongoDB 通过使用副本集来实现复制。副本集是一组托管相同数据集的 mongod 实例。在副本中,一个节点是接收所有写操作的主节点,其余的所有实例,例如第二实例,都将应用来自第一个实例的操作,以便它们具有相同的数据集。副本集只能有一个主节点。

    • 副本集是一组两个或更多节点(通常最少需要 3 个节点);
    • 在副本集中,一个节点是主要节点,其余节点是从节点;
    • 所有数据从主节点复制到从节点;
    • 在自动故障转移或维护时,将为主节点建立选举,并选举一个新的主节点;
    • 恢复失败的节点后,它再次加入副本集并用作辅助节点。

设置副本集

接下来我们来看一下如何将独立的 MongoDB 实例转换为副本集。要转换为副本集,需要按照以下几个步骤操作:

  • 关闭正在运行的 MongoDB 服务器;
  • 通过指定 --replSet 选项启动 MongoDB 服务器,--replSet 的基本语法如下所示:

mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"

【示例】在 27017 端口上启动名为 rs0 的 mongod 实例:

mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0

MongoDB分片

 

 

 在上图中,有三个主要组件

    • Shards(碎片):用于存储实际的数据块,在生产环境中,每个分片都是一个单独的副本集,它们提供了高可用性和数据一致性;
    • Config Servers(配置服务器):用于存储集群的元数据,此数据包含集群数据集到碎片的映射。查询路由器使用此元数据将操作定向到特定的碎片。在生产环境中,分片群集恰好具有 3 个配置服务器;
    • Query Routers(查询路由器):查询路由器基本上都是 mongo 实例,可与客户端应用程序接口并将操作定向到适当的分片。查询路由器处理操作并将其定位到分片,然后将结果返回给客户端。分片群集可以包含多个查询路由器来划分客户端请求负载。客户端将请求发送到一个查询路由器。通常,分片群集具有许多查询路由器。

MongoDB备份和恢复

数据备份

在 MongoDB 中我们可以使用 mongodump 命令来对 MongoDB 进行数据备份,该命令可以导出所有数据到指定目录中,也可以通过参数将导出数据转存的服务器。其语法格式如下:

mongodump -h dbhost -d dbname -o dbdirectory

参数说明如下:

  • -h:MongDB 所在服务器的地址,例如:127.0.0.1,同时也可以指定端口号,例如:127.0.0.1:27017;
  • -d:需要备份的数据库实例,例如:test;
  • -o:备份数据存放的位置,例如:c:\data\dump,该目录需要提前建立,在备份完成后,系统会自动在 dump 目录下建立一个 test 目录,并在这个目录里面存放该数据库实例的备份数据。


【示例】假设在电脑本地(127.0.0.1)的 27017 端口运行 MongoDB 服务,然后打开命令提示符(CMD)窗口并进入 MongoDB 安装目录下的 bin 目录,最后输入mongodump命令,运行结果如下:

C:\Users\79330>D:

D:\>cd install/MongoDB/bin

D:\install\Mongodb\bin>mongodump
2021-02-25T11:22:39.468+0800    writing admin.system.version to
2021-02-25T11:22:39.516+0800    done dumping admin.system.version (1 document)
2021-02-25T11:22:39.516+0800    writing bianchengbang.course to
2021-02-25T11:22:39.517+0800    done dumping bianchengbang.course (3 documents)

mongodump命令将连接到运行于127.0.0.1:27017的 MongoDB 服务器上,并将服务器中的所有数据备份到 MongoDB 安装目录下的 /bin/dump/ 目录下。

下表中列举了可以与mongodump命令一起使用的参数列表:

语法描述实例
mongodump --host HOST_NAME --port PORT_NUMBER 该命令将备份所有 MongoDB 数据 mongodump --host runoob.com --port 27017
mongodump --dbpath DB_PATH --out BACKUP_DIRECTORY 该命令将仅备份指定路径上的指定数据库 mongodump --dbpath /data/db/ --out /data/backup/
mongodump --collection COLLECTION --db DB_NAME 该命令将备份指定数据库的集合 mongodump --collection mycol --db test

恢复数据

要想还原备份的数据,您可以使用 MongoDB 的mongorestore命令,此命令会从备份目录还原所有数据,语法格式如下:

mongorestore

【示例】下面通过一个简单的示例演示一下mongorestore命令的使用:

D:\install\Mongodb\bin>mongorestore

MongoDB监控运行状态

 

mongostat

mongostat命令能够检查所有正在运行的 mongod 实例的状态,并返回数据库操作的计数器。这些计数器包括插入、查询、更新、删除和游标。当您的内存不足、写入量不足或者出现一些性能问题时,该命令还会显示发生错误的时间,并显示锁定百分比。

要运行该命令,您需要先启动您的 mongod 实例,并在另一个命令提示符(CMD)中转到 MongoDB 安装目录下的 bin 目录,最后输入mongostat命令并运行,运行结果如下:

D:\install\Mongodb\bin>mongostat

mongotop

mongotop命令可以跟踪并报告 MongoDB 实例的读写活动。默认情况下,mongotop 能够提供每个集合的水平统计数据,并每秒钟返回一次,您也可以根据需要对其进行修改。

要运行该命令,您需要先启动您的 mongod 实例,并在另一个命令提示符(CMD)中转到 MongoDB 安装目录下的 bin 目录,最后输入mongotop命令并运行,运行结果如下:
D:\install\Mongodb\bin>mongotop
2021-02-25T16:46:24.038+0800    connected to: 127.0.0.1

MongoDB $explain与$hint:查询分析

$explain

在 mongo shell 中,您可以通过 $explain 的辅助方法 explain() 来检索查询的相关信息:

> db.users.find({gender:"M"}, {name:1,_id:0}).explain()

关于上面的运行结果,有以下几点需要说明:

    • indexOnly:若字段的值为 true,则表示此查询中使用了索引;
    • cursor:指定使用的游标类型,BTreeCursor 类型表示使用了索引,还提供了所用索引的名称,BasicCursor 表示在不使用任何索引的情况下进行了完全扫描;
    • n:表示返回的匹配文档数;
    • nscannedObjects:表示扫描文档的总数;
    • nscanned:表示扫描的文档或索引条目的总数。

$hint

$hint 运算符(也叫“强制查询优化器”)能够使用指定的索引来进行查询,以此来测试查询的性能。当您想要测试具有不同索引的查询性能时,此功能特别有用。在 mongo shell 中您可以使用 $hint 的辅助方法 hint() 来使用此功能,例如下面的查询指定了要使用 gender 和 name 字段的索引:

> db.users.find({gender:"M"},{name:1, _id:0}).hint({gender:1, name:1})
{ "name" : "bianchengbang" }

MongoDB原子操作

常用的原子操作命令

$set

用来指定一个键并更新键值,若键不存在则创建。

{ $set : { field : value } }

$unset

用来删除一个键。

{ $unset : { field : 1} }

$inc

用来对文档的某个数值类型的键值进行增减操作。

{ $inc : { field : value } }

$push

用来向文档中追加一些信息。

{ $push : { field : value } }

把 value 追加到 field 里面去,field 一定要是数组类型才行,如果 field 不存在,则会新增一个数组类型加进去。

$pushAll

与 $push 类似,它可以一次追加多个值到一个数组类型的字段内。

{ $pushAll : { field : value_array } }

$pull

从数组 field 内删除一个等于 value 的值。

{ $pull : { field : _value } }

$pop

删除数组的第一个或最后一个元素。

{ $pop : { field : 1 } }

$rename

修改字段的名称。

{ $rename : { old_field_name : new_field_name } }

$bit

位操作,integer 类型。

{$bit : { field : {and : 5}}}

MongoDB索引使用注意事项

额外开销

每个索引都会占用一些空间,并且在每次执行插入、更新和删除等操作时也需要对索引进行操作,导致额外的开销。因此,如果您很少将某个集合用于读取操作,最好不要在集合中使用索引。

RAM 使用

由于索引存储在 RAM(内存)中,因此应确保索引的总大小不超过 RAM 的限制。如果总大小大于 RAM 的大小,那么 MongoDB 将删除一些索引,这就会导致性能下降。

查询限制

在以下的查询中,不能使用索引:

  • 正则表达式或否定运算符,例如 $nin、$not 等;
  • 算术运算符,例如 $mod 等;
  • $where 子句。


因此,建议经常使用 explain() 来检查查询时索引的使用情况。

索引键限制

从 2.6 版本开始,如果现有索引字段的值超过索引键的限制,那么 MongoDB 将不会创建索引。

插入超过索引键限制的文档

如果文档索引字段的值超过了索引键的限制,那么 MongoDB 不会将任何文档插入到集合中。mongorestore 和 mongoimport 实用程序也是如此。

最大范围

在定义索引时有以下几点需要注意:

    • 集合的索引不能超过 64 个;
    • 索引名称的长度不能超过 128 个字符;
    • 复合索引最多可以拥有 31 个字段。

MongoDB ObjectId

MongoDB 中的 ObjectId 的组成,ObjectId 是一个 12 字节的 BSON 类型,具有以下结构:

  • 前 4 个字节表示时间戳;
  • 接下来的 3 个字节表示机器标识符;
  • 紧接着的 2 个字节由进程 id(PID)组成;
  • 最后 3 个字节是一个随机计数器的值。

创建新的 ObjectId

MongoDB 使用 ObjectId 作为每个文档 _id 字段的默认值,而且该值可以在创建文档时自动生成。ObjectId 的复杂组合方式使得所有的 id 字段都是唯一的。

要生成一个新的 ObjectId,可以使用下面的代码:

> newObjectId = ObjectId()

执行上面的语句,会生成一个唯一的 _id,如下所示:

ObjectId("6040966d8bafb296d166fe47")

除了可以使用 MongoDB 自动生成的 ObjectId 外,您也可以手动提供一个 12 字节的 ID,如下所示:

> myObjectId = ObjectId("6040966d8bafb296d166fe50")
ObjectId("6040966d8bafb296d166fe50")

创建文档的时间戳

由于 ObjectId(_id)中默认存储了 4 个字节的时间戳,因此在大多数情况下,您再不需要单独存储任何文档的创建时间,只要使用 getTimestamp() 方法就可以获取文档的创建时间,getTimestamp() 方法可以使用 ISO 格式返回 ObjectId 中包含的时间,如下所示:

> ObjectId("6040966d8bafb296d166fe50").getTimestamp()
ISODate("2021-03-04T08:12:29Z")

将 ObjectId 转换为 String

在某些情况下,您可能需要将 ObjectId 的值转换为字符串格式。要将 ObjectId 转换为字符串,可以使用以下代码:

> newObjectId.str

上面的代码将返回 Guid 的字符串格式:

604099da8bafb296d166fe48

MongoDB MapReduce

mapReduce 是个灵活且强大的数据聚合工具,它的好处是可以把一个聚合任务分解为多个小的任务,分配到多个服务器上并行处理。

在 MongoDB 中我们可以使用 mapReduce 命令来执行 mapReduce 操作。

mapReduce 命令

在 mapReduce 命令中要实现两个函数,分别是 map 函数和 reduce 函数,其中 map 函数调用 emit(key, value),遍历集合中的所有记录,并将 key 与 value 传递给 reduce 函数进行处理,如下所示:

> db.collection_name.mapReduce(
    function() {emit(key, value);},                  // map 函数
    function(key, values) {return reduceFunction},   // reduce 函数
    {
        out: collection,
        query: document,
        sort: document,
        limit: number
    }
)

参数说明如下:

  • map 函数:一个 javascript 函数,它用一个键映射一个值并发出一个键值对;
  • reduce 函数:一个 javascript 函数,用于减少或分组具有相同键的所有文档;
  • out:指定 map-reduce 查询结果的位置;
  • query:指定用于选择文档的可选选择条件;
  • sort:指定可选的排序条件;
  • limit:指定要返回的最大文档数(可选)。

使用 mapReduce

假如有一个存储用户帖子信息的集合,其中存储了若干用户的用户名、状态等信息,例如下面这样:

{
    "post_text": "编程帮(biancheng.net),一个在线学习编程的网站,专注于分享优质编程教程。",
    "user_name": "bianchengbang",
    "status":"active"
}

接下来在上述的集合中使用 mapReduce 命令来选择集合中所有"status":"active"的文档,然后根据用户名对它们进行分组,最后统计每个用户的发帖数量。示例代码如下:

> db.posts.mapReduce(
... function() { emit(this.user_name,1); },
... function(key, values) {return Array.sum(values)},
... {
...    query:{status:"active"},
...    out:"post_total"
... }
... )

上面 mapReduce 命令的输出结果如下所示:

{
        "result" : "post_total",
        "timeMillis" : 48,
        "counts" : {
                "input" : 11,
                "emit" : 11,
                "reduce" : 2,
                "output" : 2
        },
        "ok" : 1
}

关于运行结果,有如下几点需要说明:

  • result:储存结果的 collection 的名字,这是个临时集合,mapReduce 的连接关闭后会被自动删除;
  • timeMillis:执行花费的时间,单位为毫秒;
  • input:满足条件被发送到 map 函数的文档个数;
  • emit:在 map 函数中 emit 被调用的次数,也就是所有集合中的数据总量;
  • ouput:结果集合中的文档个数(count 对调试非常有帮助);
  • ok:查询是否执行成功,成功为 1;
  • err:若执行失败,则会在这里显示失败原因。


通过运行结果可以发现,共有 11 个文档与查询匹配(状态为“active”),map 函数生成了 11 个具有键值对的文档,最后 reduce 函数将具有相同键值的映射文档分为 2 个组。

若要查看 mapReduce 查询的结果,可以使用 find 方法,如下所示:

> db.posts.mapReduce(
... function() { emit(this.user_name,1); },
... function(key, values) {return Array.sum(values)},
... {
...    query:{status:"active"},
...    out:"post_total"
... }
... ).find()

上面的查询语句,执行结果如下:

{ "_id" : "biancheng", "value" : 5 }
{ "_id" : "bianchengbang", "value" : 6 }

MongoDB全文检索

MongoDB 支持大约 15 种语言的全文索引,例如 danish、dutch、english、finnish、french、german、hungarian、italian、norwegian、portuguese、romanian、russian、spanish、swedish、turkish 等。

启用全文检索

最初,全文检索是一个实验性功能,但 MongoDB 在 2.6 版本以后默认开启了此功能,如果您使用 2.6 之前的版本,则需要使用以下代码来启用全文检索:

>db.adminCommand({setParameter:true, textSearchEnabled:true})

或者使用命令:

mongod --setParameter textSearchEnabled=true

创建全文索引

假如我们在 posts 集合中插入以下文档:

> db.posts.insert([
... {
...    "post_text": "enjoy the mongodb articles on bianchengbang",
...    "tags": ["mongodb", "bianchengbang"]
... },
... {
...     "post_text" : "writing tutorials on mongodb",
...     "tags" : [ "mongodb", "tutorial" ]
... }
... ])
BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 2,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})

若要在 post_text 字段上创建全文索引,以便我们可以直接搜索字段中的内容,可以像下面这样:

> db.posts.createIndex({post_text:"text"})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

使用全文索引

使用全文索引可以提高搜索效率,前面我们已经在 post_text 字段上创建了全文索引,下面通过一个示例来演示全文索引的使用。

【示例】搜索 post_text 字段中包含关键词“bianchengbang”的所有文档:

> db.posts.find({$text:{$search:"bianchengbang"}}).pretty()
{
        "_id" : ObjectId("6041dfc3835e4aa734b591df"),
        "post_text" : "enjoy the mongodb articles on bianchengbang",
        "tags" : [
                "mongodb",
                "bianchengbang"
        ]
}

MongoDB GridFS

GridFS 是 MongoDB 的一种规范,用于存储和检索大型文件,如图像、音频、视频等。GridFS 也是一种存储文件的文件系统,但其数据是存储在 MongoDB 集合中的,GridFS 甚至可以存储超过 16MB 的文件。在存储文件时 GridFS 可以将一个文件分为多个数据块,并将每个数据块存储在一个单独的文档中,每个文档最大为 255KB。

默认情况下,GridFS 使用 fs.files 和 fs.chunks 两个集合来存储文件的元数据和块。每个区块都由其唯一的 ObjectId(_id)字段标识。fs.files 用作父文档,fs.chunks 文档中的 files_id 字段将块链接到其父级。

下面展示了一个简单的 fs.files 集合文档:

{
    "filename": "test.txt",
    "chunkSize": NumberInt(261120),
    "uploadDate": ISODate("2014-04-13T11:32:33.557Z"),
    "md5": "7b762939321e146569b07f72c62cca4f",
    "length": NumberInt(646)
}

该文档指定了文件的文件名、块大小、上传日期和长度。下面展示了一个简单的 fs.chunks 集合文档:

{
    "files_id": ObjectId("534a75d19f54bfec8a2fe44b"),
    "n": NumberInt(0),
    "data": "Mongo Binary Data"
}

将文件添加到 GridFS

想要将文件添加到 GridFS 中,可以使用 put 命令。下面的示例中演示了使用 put 命令在 GridFS 中存储 mp3 文件的过程。在存储文件时我们需要使用 mongofiles.exe,它在 MongoDB 安装目录下的 bin 目录中。

打开命令行工具,跳转到 mongofiles.exe 所在的目录并输入以下代码:

C:\Users\79330>mongofiles.exe -d gridfs put F:/code/music/song.mp3
2021-03-09T09:38:22.271+0800    connected to: localhost
2021-03-09T09:38:22.333+0800    added file: F:/code/music/song.mp3

提示:若已经将 MongoDB 安装目录下的 bin 目录添加到了 Path 环境变量中,则可以省略跳转到 bin 目录的步骤,直接在命令行工具中输入以上命令即可。

上面命令中 gridfs 为要存储文件的数据库名称,如果数据库不存在,那么 MongoDB 会自动创建;F:/code/music/song.mp3 为要存储文件的路径以及名称。

若要查看我们上面保存在数据库中的文档,可以使用 find() 命令:

> db.fs.files.find().pretty()
{
        "_id" : ObjectId("6046d18e01ef664194f5a78c"),
        "chunkSize" : 261120,
        "uploadDate" : ISODate("2021-03-09T01:38:22.341Z"),
        "length" : 4148442,
        "md5" : "cc3495f49ece8212906ef5c59e7440f0",
        "filename" : "F:/code/music/song.mp3"
}

我们还可以通过上面查询中返回的文档 ID 来查看 fs.chunks 集合中与存储文件相关的所有数据:

> db.fs.chunks.find({files_id:ObjectId('6046d18e01ef664194f5a78c')})

上面的查询返回了 15 个文档的数据,这意味着前面我们存储的 song.mp3 文件被存储到了 15 个区块中。 

Java操作MongoDB(详细)

MongoDB 不仅可以在命令行窗口(CMD)中运行,它还可以与编程语言配合使用,本节我们就来介绍一下在 Java 中如何使用 MongoDB。

环境配置

想要在 Java 程序中使用 MongoDB,需要确保您的电脑上已经安装了 MongoDB,并搭建好了 Java 的环境。您可以查看《Java 教程》来了解不同系统下 Java 的安装教程。

另外您还需要确认安装了 MongoDB JDBC 驱动,您可以通过 http://mongodb.github.io/mongo-java-driver/ 来下载最新版的 mongodb-driver-3.11.2.jar 以及其依赖项 mongodb-driver-core-3.11.2.jar,并将它们包含进 classpath 中。

连接数据库

要连接数据库,首先需要指定要连接的数据库名称,如果数据库不存在的话,MongoDB 则会自动创建它。下面通过简单的代码来演示如何使用 Java 代码连接 MongoDB 数据库:

  1. import com.mongodb.client.MongoDatabase;
  2. import com.mongodb.MongoClient;
  3. import com.mongodb.MongoCredential;
  4. public class ConnectToDB {
  5. public static void main( String args[] ) {
  6. // 创建 MongoDB 连接
  7. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  8. // 连接到 MongoDB
  9. MongoCredential credential;
  10. credential = MongoCredential.createCredential("sampleUser", "myDb", "password".toCharArray());
  11. System.out.println("Connected to the database successfully");
  12. // 访问数据库
  13. MongoDatabase database = mongo.getDatabase("myDb");
  14. System.out.println("Credentials ::"+ credential);
  15. }
  16. }

编译并运行上面的程序,即可创建名为“myDb”的数据库,并输出如下所示的内容:

$javac ConnectToDB.java
$java ConnectToDB
Connected to the database successfully
Credentials ::MongoCredential{
    mechanism = null,
    userName = 'sampleUser',
    source = 'myDb',
    password = <hidden>,
    mechanismProperties = {}
}

创建集合

若要创建集合,您可以使用 com.mongodb.client.MongoDatabase 类的 createCollection() 方法,示例代码如下:

  1. import com.mongodb.client.MongoDatabase;
  2. import com.mongodb.MongoClient;
  3. import com.mongodb.MongoCredential;
  4. public class CreatingCollection {
  5. public static void main( String args[] ) {
  6. // 创建 MongoDB 连接
  7. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  8. // 连接到 MongoDB
  9. MongoCredential credential;
  10. credential = MongoCredential.createCredential("sampleUser", "myDb", "password".toCharArray());
  11. System.out.println("数据库连接成功");
  12. // 访问数据库
  13. MongoDatabase database = mongo.getDatabase("myDb");
  14. // 创建集合
  15. database.createCollection("tutorial");
  16. System.out.println("集合创建成功");
  17. }
  18. }

编译并运行上面的程序,运行结果如下所示:

数据库连接成功
集合创建成功

获取/选择集合

要获取/选择数据库中的集合,您可以使用 com.mongodb.client.MongoDatabase 类的 getCollection() 方法,示例代码如下:

  1. import com.mongodb.client.MongoCollection;
  2. import com.mongodb.client.MongoDatabase;
  3. import org.bson.Document;
  4. import com.mongodb.MongoClient;
  5. import com.mongodb.MongoCredential;
  6. public class selectingCollection {
  7. public static void main( String args[] ) {
  8. // 创建 MongoDB 连接
  9. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  10. // 连接到 MongoDB
  11. MongoCredential credential;
  12. credential = MongoCredential.createCredential("sampleUser", "myDb", "password".toCharArray());
  13. System.out.println("数据库连接成功");
  14. // 访问数据库
  15. MongoDatabase database = mongo.getDatabase("myDb");
  16. // 创建集合
  17. database.createCollection("tutorial");
  18. System.out.println("集合创建成功");
  19. // 检索集合
  20. MongoCollection<Document> collection = database.getCollection("tutorial");
  21. System.out.println("集合选择成功 tutorial");
  22. }
  23. }

编译并运行上面的程序,运行结果如下所示:

数据库连接成功
集合创建成功
集合选择成功 tutorial

插入文档

想要向集合中插入文档,您可以使用 com.mongodb.client.MongoCollection 类的 insert() 方法,示例代码如下:

  1. import com.mongodb.client.MongoCollection;
  2. import com.mongodb.client.MongoDatabase;
  3. import org.bson.Document;
  4. import com.mongodb.MongoClient;
  5. public class InsertingDocument {
  6. public static void main( String args[] ) {
  7. // 创建 MongoDB 连接
  8. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  9. // 连接到数据库
  10. MongoDatabase database = mongo.getDatabase("myDb");
  11. // 创建集合
  12. database.createCollection("tutorial");
  13. System.out.println("集合创建成功");
  14. // 检索集合
  15. MongoCollection<Document> collection = database.getCollection("tutorial");
  16. System.out.println("集合选择成功");
  17. Document document = new Document("title", "MongoDB")
  18. .append("description", "database")
  19. .append("likes", 100)
  20. .append("url", "http://www.biancheng.net/mongodb/")
  21. .append("by", "编程帮");
  22. // 将文档插入到集合中
  23. collection.insertOne(document);
  24. System.out.println("文档插入成功");
  25. }

编译并运行上面的程序,运行结果如下所示:

集合创建成功
集合选择成功
文档插入成功

查询文档

想要查询集合中的文档,您可以使用 com.mongodb.client.MongoCollection 类的 find() 方法,此方法会返回一个游标,因此想要查询集合中的所有文档您只需要不断迭代此游标即可,示例代码如下:

  1. import com.mongodb.client.FindIterable;
  2. import com.mongodb.client.MongoCollection;
  3. import com.mongodb.client.MongoDatabase;
  4. import java.util.ArrayList;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import org.bson.Document;
  8. import com.mongodb.MongoClient;
  9. import com.mongodb.MongoCredential;
  10. public class RetrievingAllDocuments {
  11. public static void main( String args[] ) {
  12. // 创建 MongoDB 连接
  13. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  14. // 连接到 MongoDB
  15. MongoCredential credential;
  16. credential = MongoCredential.createCredential("sampleUser", "myDb", "password".toCharArray());
  17. System.out.println("数据库连接成功");
  18. // 连接到数据库
  19. MongoDatabase database = mongo.getDatabase("myDb");
  20. // 检索集合
  21. MongoCollection<Document> collection = database.getCollection("tutorial");
  22. System.out.println("成功选择了集合 tutorial");
  23. Document document1 = new Document("title", "MongoDB")
  24. .append("description", "database")
  25. .append("likes", 100)
  26. .append("url", "http://www.biancheng.net/mongodb/")
  27. .append("by", "编程帮");
  28. Document document2 = new Document("title", "html")
  29. .append("description", "database")
  30. .append("likes", 200)
  31. .append("url", "http://www.biancheng.net/html/")
  32. .append("by", "编程帮");
  33. List<Document> list = new ArrayList<Document>();
  34. list.add(document1);
  35. list.add(document2);
  36. collection.insertMany(list);
  37. // 获取 iterable 对象
  38. FindIterable<Document> iterDoc = collection.find();
  39. int i = 1;
  40. // 获取迭代器
  41. Iterator it = iterDoc.iterator();
  42. while (it.hasNext()) {
  43. System.out.println(it.next());
  44. i++;
  45. }
  46. }
  47. }

编译并运行上面的程序,运行结果如下所示:

数据库连接成功
成功选择了集合 tutorial
Document{{_id=5dce4e9ff68a9c2449e197b2, title=MongoDB, description=database, likes=100, url=http://www.biancheng.net/mongodb/, by=编程帮}}
Document{{_id=5dce4e9ff68a9c2449e197b3, title=html, description=database, likes=200, url=http://www.biancheng.net/html/, by=编程帮}}

更新文档

要更新(修改)集合中文档的数据,您可以使用 com.mongodb.client.MongoCollection 类的 updateOne() 方法,示例代码如下:

  1. import com.mongodb.client.FindIterable;
  2. import com.mongodb.client.MongoCollection;
  3. import com.mongodb.client.MongoDatabase;
  4. import com.mongodb.client.model.Filters;
  5. import com.mongodb.client.model.Updates;
  6. import java.util.Iterator;
  7. import org.bson.Document;
  8. import com.mongodb.MongoClient;
  9. import com.mongodb.MongoCredential;
  10. public class UpdatingDocuments {
  11. public static void main( String args[] ) {
  12. // 创建 MongoDB 连接
  13. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  14. // 连接到 MongoDB
  15. MongoCredential credential;
  16. credential = MongoCredential.createCredential("sampleUser", "myDb", "password".toCharArray());
  17. System.out.println("数据库连接成功");
  18. // 连接到数据库
  19. MongoDatabase database = mongo.getDatabase("myDb");
  20. // 检索集合
  21. MongoCollection<Document> collection = database.getCollection("tutorial");
  22. System.out.println("成功选择了集合 tutorial");
  23. collection.updateOne(Filters.eq("title", 1), Updates.set("likes", 150));
  24. System.out.println("文档更新成功...");
  25. // 更新后检索文档
  26. // 获取 iterable 对象
  27. FindIterable<Document> iterDoc = collection.find();
  28. int i = 1;
  29. // 获取迭代器
  30. Iterator it = iterDoc.iterator();
  31. while (it.hasNext()) {
  32. System.out.println(it.next());
  33. i++;
  34. }
  35. }
  36. }

编译并运行上面的程序,运行结果如下所示:

数据库连接成功
成功选择了集合 tutorial
文档更新成功...
Document{{_id=5dce4e9ff68a9c2449e197b2, title=MongoDB, description=database, likes=100, url=http://www.biancheng.net/mongodb/, by=编程帮}}
Document{{_id=5dce4e9ff68a9c2449e197b3, title=html, description=database, likes=200, url=http://www.biancheng.net/html/, by=编程帮}}

删除文档

想要从集合中删除文档,您可以使用 com.mongodb.client.MongoCollection 类的 deleteOne() 方法,示例代码如下:

  1. import com.mongodb.client.FindIterable;
  2. import com.mongodb.client.MongoCollection;
  3. import com.mongodb.client.MongoDatabase;
  4. import com.mongodb.client.model.Filters;
  5. import java.util.Iterator;
  6. import org.bson.Document;
  7. import com.mongodb.MongoClient;
  8. import com.mongodb.MongoCredential;
  9. public class DeletingDocuments {
  10. public static void main( String args[] ) {
  11. // 创建 MongoDB 连接
  12. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  13. // 连接到 MongoDB
  14. MongoCredential credential;
  15. credential = MongoCredential.createCredential("sampleUser", "myDb", "password".toCharArray());
  16. System.out.println("数据库连接成功");
  17. // 访问数据库
  18. MongoDatabase database = mongo.getDatabase("myDb");
  19. // 检索集合
  20. MongoCollection<Document> collection = database.getCollection("tutorial");
  21. System.out.println("成功选择了集合 tutorial");
  22. // 删除文档
  23. collection.deleteOne(Filters.eq("title", "MongoDB"));
  24. System.out.println("文档删除成功...");
  25. // 更新后检索文档
  26. // 获取 iterable 对象
  27. FindIterable<Document> iterDoc = collection.find();
  28. int i = 1;
  29. // 获取迭代器
  30. Iterator it = iterDoc.iterator();
  31. while (it.hasNext()) {
  32. System.out.println(it.next());
  33. i++;
  34. }
  35. }
  36. }

编译并运行上面的程序,运行结果如下所示:

数据库连接成功
成功选择了集合 tutorial
文档删除成功...
Document{{_id=5dce4e9ff68a9c2449e197b3, title=html, description=database, likes=200, url=http://www.biancheng.net/html/, by=编程帮}}

删除集合

要从数据库中删除集合,您可以使用 com.mongodb.client.MongoCollection 类中的 drop() 方法,示例代码如下:

  1. import com.mongodb.client.MongoCollection;
  2. import com.mongodb.client.MongoDatabase;
  3. import org.bson.Document;
  4. import com.mongodb.MongoClient;
  5. import com.mongodb.MongoCredential;
  6. public class DropingCollection {
  7. public static void main( String args[] ) {
  8. // 创建 MongoDB 连接
  9. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  10. // 连接到 MongoDB
  11. MongoCredential credential;
  12. credential = MongoCredential.createCredential("sampleUser", "myDb", "password".toCharArray());
  13. System.out.println("数据库连接成功");
  14. // 访问数据库
  15. MongoDatabase database = mongo.getDatabase("myDb");
  16. // 创建集合
  17. System.out.println("集合创建成功");
  18. // 检索集合
  19. MongoCollection<Document> collection = database.getCollection("tutorial");
  20. // 删除集合
  21. collection.drop();
  22. System.out.println("集合删除成功");
  23. }
  24. }

编译并运行上面的程序,运行结果如下所示:

数据库连接成功
集合创建成功
集合删除成功

列出所有集合

要列出数据库中的所有集合,您需要使用 com.mongodb.client.MongoDatabase 类的 listCollectionNames() 方法,示例代码如下:

  1. import com.mongodb.client.MongoDatabase;
  2. import com.mongodb.MongoClient;
  3. import com.mongodb.MongoCredential;
  4. public class ListOfCollection {
  5. public static void main( String args[] ) {
  6. // 创建 MongoDB 连接
  7. MongoClient mongo = new MongoClient( "localhost" , 27017 );
  8. // 连接到 MongoDB
  9. MongoCredential credential;
  10. credential = MongoCredential.createCredential("sampleUser", "myDb", "password".toCharArray());
  11. System.out.println("数据库连接成功");
  12. // 访问数据库
  13. MongoDatabase database = mongo.getDatabase("myDb");
  14. System.out.println("集合创建成功");
  15. for (String name : database.listCollectionNames()) {
  16. System.out.println(name);
  17. }
  18. }
  19. }

编译并运行上面的程序,运行结果如下所示:

数据库连接成功
集合创建成功
myCollection
myCollection1
myCollection5

文档数据格式:BSON 

在本指南中,您可以了解 BSON 数据格式、MongoDB 如何使用它以及如何独立于 MongoDB Java 驱动程序安装 BSON 库。

BSON或二进制 JSON 是 MongoDB 用于组织和存储数据的数据格式。此数据格式包括所有 JSON 数据结构类型,并增加了对日期、不同大小整数、ObjectId 和二进制数据等类型的支持。有关受支持类型的完整列表,请参阅 BSON 类型服务器手册页。

二进制格式不是人类可读的,但您可以使用 Java BSON 库将其转换为 JSON 表示。您可以在我们关于JSON 和 BSON 的文章中阅读有关这些格式之间关系的更多信息。

使用 BSON 库的 MongoDB Java 驱动程序允许您使用实现BSON 接口的对象类型之一来处理 BSON 数据 ,包括:

有关使用这些对象类型的更多信息,请参阅我们的 文档指南

这些说明向您展示了如何将 BSON 库作为依赖项添加到您的项目中。如果您将 MongoDB Java 驱动程序作为依赖项添加到您的项目中,则可以跳过此步骤,因为 BSON 库已作为驱动程序的必需依赖项包含在内。有关如何将 MongoDB Java 驱动程序作为依赖项添加到您的项目的说明,请参阅我们的快速入门指南的 驱动程序安装部分。

我们建议您使用Maven或 Gradle构建自动化工具来管理项目的依赖项。从以下选项卡中选择以查看该工具的依赖项声明:

 

以下代码段显示了文件dependencies部分中的依赖项声明 pom.xml

复制代码
<dependencies>
    <dependency>
        <groupId>org.mongodb</groupId>
        <artifactId>bson</artifactId>
        <version>4.3.2</version>
    </dependency>
</dependencies>
复制代码

 

以下代码段显示了文件中dependencies对象中的依赖项声明 build.gradle

dependencies {
   compile 'org.mongodb:bson:4.3.2'
}

 

如果您没有使用上述工具之一,您可以通过直接从sonatype 存储库下载 JAR 文件将其包含在您的项目中 。

 

文档数据格式:扩展 JSON 

在本指南中,您可以了解如何在 MongoDB Java 驱动程序中使用扩展 JSON 格式。

JSON 是一种数据格式,表示对象、数组、数字、字符串、布尔值和空值的值。的扩展JSON格式定义一个保留的密钥集合与前缀“ $”来表示字段类型信息直接对应于每种类型在BSON,所述格式MongoDB使用来存储数据。

本指南解释了以下主题:

  • 不同的 MongoDB 扩展 JSON 格式
  • 如何使用 BSON 库在扩展 JSON 和 Java 对象之间进行转换
  • 如何创建 BSON 类型的自定义转换

有关这些格式之间差异的更多信息,请参阅我们 关于 JSON 和 BSON 的文章

MongoDB 扩展 JSON 具有不同的字符串格式来表示 BSON 数据。每种不同的格式都符合 JSON RFC 并满足特定用例。在扩展格式中,也被称为 规范格式,特征在于用于而不丢失信息每BSON类型的双向转换特定表示。的宽松模式 格式是更简洁,更接近普通JSON,但并不代表所有类型的信息,例如数量字段的特定的字节大小。

请参阅下表以查看每种格式的说明:

 
 
Name
描述
Extended
也称为规范格式,这种 JSON 表示避免了 BSON 类型信息的丢失。
这种格式在失去人类可读性和与旧格式的互操作性的情况下优先保留类型。
Relaxed Mode
描述具有某些类型信息丢失的 BSON 文档的 JSON 表示。
这种格式在丢失某些类型信息的情况下优先考虑人类可读性和互操作性。
Shell
与 MongoDB shell 中使用的语法匹配的 JSON 表示。
这种格式优先考虑与 MongoDB shell 的兼容性,它经常使用 JavaScript 函数来表示类型。
笔记

驱动程序将$uuid扩展 JSON 类型从字符串解析BsonBinary为二进制子类型 4的 对象。有关$uuid字段解析的更多信息,请参阅 扩展 JSON 规范中解析 $uuid 字段的 特殊规则部分。

有关这些格式的更多详细信息,请参阅以下资源:

以下示例显示了一个包含以每个扩展 JSON 格式表示的 ObjectId、日期和长数字字段的文档。单击与您要查看的示例的格式对应的选项卡:

 
{
  "_id": { "$oid": "573a1391f29313caabcd9637" },
  "createdAt": { "$date": { "$numberLong": "1601499609" }},
  "numViews": { "$numberLong": "36520312" }
}

 

{
  "_id": { "$oid": "573a1391f29313caabcd9637" },
  "createdAt": { "$date": "2020-09-30T18:22:51.648Z" },
  "numViews": 36520312
}

 

{
  "_id:": ObjectId("573a1391f29313caabcd9637"),
  "createdAt": ISODate("2020-09-30T18:22:51.648Z"),
  "numViews": NumberLong("36520312")
}

 

您可以通过parse()DocumentBsonDocument 类调用静态方法将扩展 JSON 字符串读入 Java 文档对象,具体取决于您需要的对象类型。此方法解析任何格式的扩展 JSON 字符串,并返回包含数据的该类的实例。

以下示例显示了如何使用Document该类将示例扩展 JSON 字符串读取到Document使用该parse()方法的对象中 :

String ejsonStr = "{ \"_id\": { \"$oid\": \"507f1f77bcf86cd799439011\"}," +
                  "\"myNumber\": {\"$numberLong\": \"4794261\" }}}";
Document doc = Document.parse(ejsonStr);
System.out.println(doc);

 

上述代码的输出应如下所示:

Document{{_id=507f1f77bcf86cd799439011, myNumber=4794261}}

 

有关更多信息,请参阅我们的文档基础页面。

您还可以通过使用JsonReader类将扩展 JSON 字符串读入 Java 对象,而无需使用 MongoDB Java 驱动程序的文档类。该类包含顺序解析扩展 JSON 字符串的任何格式的字段和值的方法,并将它们作为 Java 对象返回。驱动程序的文档类也使用这个类来解析扩展的 JSON。

以下代码示例展示了如何使用JsonReader该类将扩展 JSON 字符串转换为 Java 对象:

复制代码
String ejsonStr = "{ \"_id\": { \"$oid\": \"507f1f77bcf86cd799439011\"}," +
                  "\"myNumber\": {\"$numberLong\": \"4794261\" }}}";
JsonReader jsonReader = new JsonReader(ejsonStr);
jsonReader.readStartDocument();
jsonReader.readName("_id");
ObjectId id = jsonReader.readObjectId();
jsonReader.readName("myNumber");
Long myNumber = jsonReader.readInt64();
jsonReader.readEndDocument();
System.out.println(id + " is type: " + id.getClass().getName());
System.out.println(myNumber + " is type: " + myNumber.getClass().getName());
jsonReader.close();
复制代码

 

此代码示例的输出应如下所示:

507f1f77bcf86cd799439011 is type: org.bson.types.ObjectId
4794261 is type: java.lang.Long

 

有关更多信息,请参阅JsonReader API 文档。

您可以从 的实例Document或 BsonDocument通过调用toJson()方法编写扩展 JSON 字符串,可选择将 的实例传递给它JsonWriterSettings以指定扩展 JSON 格式。

在此示例中,我们以轻松模式格式输出扩展 JSON。

Document myDoc = new Document();
myDoc.append("_id", new ObjectId("507f1f77bcf86cd799439012")).append("myNumber", 11223344);
JsonWriterSettings settings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();
System.out.println(doc.toJson(settings));

 

此代码示例的输出应如下所示:

{"_id": {"$oid": "507f1f77bcf86cd799439012"}, "myNumber": 11223344}

 

您还可以使用 BSON 库和JsonWriter类从 Java 对象中的数据输出扩展 JSON 字符串。要构造 的实例JsonWriter,请传递 Java 的子类Writer以指定您希望如何输出扩展 JSON。您可以选择传递一个JsonWriterSettings 实例来指定扩展 JSON 格式等选项。默认情况下, JsonWriter使用轻松模式格式。MongoDB Java 驱动程序的文档类也使用这个类将 BSON 转换为扩展 JSON。

以下代码示例展示了如何使用JsonWriter创建扩展 JSON 字符串并将其输出到System.out. 我们通过向outputMode()builder 方法传递JsonMode.EXTENDED常量来指定格式:

复制代码
JsonWriterSettings settings = JsonWriterSettings.builder().outputMode(JsonMode.EXTENDED).build();
try (JsonWriter jsonWriter = new JsonWriter(new BufferedWriter(new OutputStreamWriter(System.out)), settings)) {
    jsonWriter.writeStartDocument();
    jsonWriter.writeObjectId("_id", new ObjectId("507f1f77bcf86cd799439012"));
    jsonWriter.writeInt64("myNumber", 11223344);
    jsonWriter.writeEndDocument();
    jsonWriter.flush();
}
复制代码

 

此代码示例的输出应如下所示:

{"_id": {"$oid": "507f1f77bcf86cd799439012"}, "myNumber": {"$numberLong": "11223344"}}

 

有关本节中提到的方法和类的更多信息,请参阅以下 API 文档:

除了指定outputMode()要格式化 JSON 输出之外,您还可以通过将转换器添加到 JsonWriterSettings.Builder. 这些转换器方法检测 Java 类型并执行Converter传递给它们的定义的逻辑。

以下示例代码显示了如何附加定义为 lambda 表达式的转换器以简化轻松模式 JSON 输出。

复制代码
JsonWriterSettings settings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED)
        .objectIdConverter((value, writer) -> writer.writeString(value.toHexString()))
        .dateTimeConverter(
                (value, writer) -> {
                    ZonedDateTime zonedDateTime = Instant.ofEpochMilli(value).atZone(ZoneOffset.UTC);
                    writer.writeString(DateTimeFormatter.ISO_DATE_TIME.format(zonedDateTime));
                })
        .build();
Document doc = new Document()
     .append("_id", new ObjectId("507f1f77bcf86cd799439012"))
     .append("createdAt", Date.from(Instant.ofEpochMilli(1601499609000L)))
     .append("myNumber", 4794261);
System.out.println(doc.toJson(settings)));
复制代码

 

此代码的输出应如下所示:

{"_id": "507f1f77bcf86cd799439012", "createdAt": "2020-09-30T21:00:09Z", "myNumber": 4794261}

 

在不指定转换器的情况下,轻松模式 JSON 输出应如下所示:

{"_id": {"$oid": "507f1f77bcf86cd799439012"}, "createdAt": {"$date": "2020-09-30T21:00:09Z"}, "myNumber": 4794261}

 

有关本节中提到的方法和类的更多信息,请参阅以下 API 文档:

 

posted @ 2022-03-08 14:27  hanease  阅读(123)  评论(0编辑  收藏  举报