1 MongoDB中的基本概念及原理
1.1 MongoDB介绍
官网地址:https://www.mongodb.com/
MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。 在高负载的情况下,添加更多的节点,可以保证服务器性能。 MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。 MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。
1.2 MongoDB概念解析
不管我们学习什么数据库都应该学习其中的基础概念,在mongodb中基本的概念是文档、集合、数据库,下面我们挨个介绍。
下表将帮助您更容易理解Mongo中的一些概念:
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | -- | 表连接,MongoDB不支持 |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
通过下图实例,我们也可以更直观的了解Mongo中的一些概念:
1.2.1 数据库
一个mongodb中可以建立多个数据库。MongoDB的默认数据库为"test",该数据库存储在data目录中(安装时,可以默认可以指定,但是该目录必须是存在的)。MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库放置在不同的文件中。
执行 "show dbs" 命令可以显示所有数据库的列表。
> show dbs
local 0.078GB
test 0.078GB
>
执行 "db" 命令可以显示当前数据库对象或集合。
> db
test
>
执行 "use"命令,可以连接到一个指定的数据库。
> use local
switched to db local
> db
local
>
数据库命名规范
数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。
不能是空字符串
不得含有' '(空格)、.、$、/、\和\0 (空字符)
应全部小写
最多64字节
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
1.2.2 集合
集合就是 MongoDB 文档组,类似于 RDBMS中的表。
集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。比如,我们可以将以下不同数据结构的文档插入到集合中:
{"site":"www.baidu.com"}
{"site":"www.google.com","name":"Google"}
当第一个文档插入时,集合就会被创建。
一个collection(集合)中的所有field(域)是collection(集合)中所有document(文档)中包含 的field(域)的并集。
集合命名规范
集合名不能是空字符串""。
集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
集合名不能以"system."开头,这是为系统集合保留的前缀。
用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现。
如下实例:
db.col.findOne()
capped collections
Capped collections 就是固定大小的collection。它有很高的性能以及队列过期的特性(过期按照插入的顺序)。有点和 "RRD" 概念类似。Capped collections 是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能。和标准的collection 不同,你必须要显式的创建一个capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。
Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。
由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。
要注意的是指定的存储大小包含了数据库的头信息
db.createCollection("mycoll", {capped:true, size:100000})
在 capped collection 中,你能添加新的对象。
能进行更新,然而,对象不会增加存储空间。如果增加,更新就会失败 。
使用 Capped Collection 不能删除一个文档,可以使用 drop() 方法删除 collection 所有的行。
删除之后,你必须显式的重新创建这个 collection。
在32bit机器中,capped collection 最大存储为 1e9( 1X10的9次方)个字节。
1.2.3 文档
文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
一个简单的文档例子如下:
{"site":"www.baidu.com", "name":"百度"}
下表列出了 RDBMS 与 MongoDB 对应的术语:
RDBMS | MongoDB |
---|---|
数据库 | 数据库 |
表格 | 集合 |
行 | 文档 |
列 | 字段 |
表联合 | 嵌入文档 |
主键 | 主键 (MongoDB 提供了 key 为 _id ) |
需要注意的是:
文档中的键/值对是有序的。
文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
MongoDB区分类型和大小写。
MongoDB的文档不能有重复的键。
文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
文档键命名规范:
键不能含有\0 (空字符)。这个字符用来表示键的结尾。
.和$有特别的意义,只有在特定环境下才能使用。
以下划线"_"开头的键是保留的(不是严格要求的)。
1.2.4 元数据
数据库的信息是存储在集合中。它们使用了系统的命名空间:
dbname.system.*
在MongoDB数据库中名字空间 .system.* 是包含多种系统信息的特殊集合(Collection),如下:
集合命名空间 | 描述 |
---|---|
dbname.system.namespaces | 列出所有名字空间 |
dbname.system.indexes | 列出所有索引 |
dbname.system.profifile | 包含数据库概要(profifile)信息 |
dbname.system.users | 列出所有可访问数据库的用户 |
dbname.system.sources | 包含复制对端(slave)的服务器信息和状态 |
对于修改系统集合中的对象有如下限制:
在{{system.indexes}}插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的drop index命令将自动更新相关信息)。
{{system.users}}是可修改的。
{{system.profile}}是可删除的。
1.3 MongoDB 数据类型
下表为MongoDB中常用的几种数据类型:
数据类型 | 说明 | 解释 | 举例 |
---|---|---|---|
String | 字符串 | UTF-8 编码的字符串才是合法的。 | {“v”:“baidu”} |
Integer | 整型数值 | 根据你所采用的服务器,可分为32 位或 64 位。 | {“v”:1} |
Boolean | 布尔值 | 用于存储布尔值(真/假)。 | {“v”:true} |
Double | 双精度浮点值 | 用于存储浮点值 | {“v”:3.14} |
ObjectID | 对象ID | 用于创建文档的ID | {“_id”:ObjectId(“123123”)} |
Array | 数组 | 用于将数组或列表或多个值存储为一个键 | {“arr”:[“a”,“b”]} |
Timestamp | 时间戳 | 从开始纪元开始的毫秒数 | |
Object | 内嵌文档 | 文档可以作为文档中某个key的value | {“o”:{“foo”:“bar”}} |
Null | 空值 | 表示空值或者未定义的对象 | {“v”:null} |
Date | 日期 | 日期时间,用Unix日期格式来存储当前日期或时间。 | {“date”:new Date()} |
Regular | 正则表达式 | 文档中可以包含正则表达式,遵循JS语法 | {“v”:/bd/i} |
Code | 代码 | 可以包含JS代码 | {“x”:function(){}} |
File | 文件 | 1、二进制转码(Base64)后存储(<16M) 2、GridFS(>16M) | GridFS 用两个集合来存储一个文件:fs.files与fs.chunks |
下面说明下几种重要的数据类型。
1.3.1 ObjectId
ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:
前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
接下来的 3 个字节是机器标识码
紧接的两个字节由进程 id 组成 PID
最后三个字节是随机数
MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象。由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过getTimestamp 函数来获取文档的创建时间:
> var newObject = ObjectId()
> newObject.getTimestamp()
ISODate("2017-11-25T07:21:10Z")
ObjectId 转为字符串
> newObject.str
5a1919e63df83ce79df8b38f
1.3.2 字符串
BSON 字符串都是 UTF-8 编码。
1.3.3 时间戳
BSON有一个特殊的时间戳类型用于MongoDB内部使用,与普通的日期类型不相关。时间戳值是一个 64 位的值。其中:前32位是一个 time_t 值(与Unix新纪元相差的秒数),后32位是在某秒中操作的一个递增的序数。
在单个 mongod 实例中,时间戳值通常是唯一的
在复制集中, oplog 有一个 ts 字段。这个字段中的值使用BSON时间戳表示了操作时间。
BSON 时间戳类型主要用于 MongoDB 内部使用。在大多数情况下的应用开发中,你可以使用BSON 日期类型。
1.3.4 日期
表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期。
> var mydate1 = new Date() //格林尼治时间
> mydate1 ISODate("2018-03-04T14:58:51.233Z")
> typeof mydate1
object
> var mydate2 = ISODate() //格林尼治时间
> mydate2 ISODate("2018-03-04T15:00:45.479Z")
> typeof mydate2
object
这样创建的时间是日期类型,可以使用 JS 中的 Date 类型的方法。
返回一个时间类型的字符串:
> var mydate1str = mydate1.toString()
> mydate1str
Sun Mar 04 2018 14:58:51 GMT+0000 (UTC)
> typeof mydate1str
string
或者
> Date()
Sun Mar 04 2018 1
2 MongoDB安装和常用命令
2.1 安装
下载地址:https://www.mongodb.com/download-center#community
下载安装包并解压 :
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-3.6.11.tgz
tar -zxvf mongodb-linux-x86_64-rhel70-3.6.11.tgz
mv mongodb-linux-x86_64-rhel70-3.6.11 mongodb
#一定注意要创建数据存储目录,默认路径/data/db
mkdir -p /data/db
配置环境变量:
vim /etc/profile
export PATH=$JAVA_HOME/bin:/root/mongodb/bin:$PATH
source /etc/profile
2.2 启动
通过配置文件方式启动
mongod --config 配置文件路径
mongod -f 配置文件路径
说明:-f是--config的缩写。配置文件,需要手动创建,参考以下配置即可。
创建mongodb.cfg配置文件
#数据库文件位置
dbpath=/root/mongodb/singleton/data
#日志文件位置
logpath=/root/mongodb/singleton/logs/mongodb.log
#以追加方式写入日志
logappend=true
#是否以守护进程方式运行
fork=true
#绑定客户端访问的ip 0.0.0.0 不绑定ip
bind_ip=192.168.10.135
#默认端口号27017
port=27017
创建数据存储目录和日志文件目录:
mkdir /root/mongodb/singleton/data -p
mkdir /root/mongodb/singleton/logs -p
通过配置文件方式启动:
mongod -f /root/mongodb/sinleton/mongodb.cfg
2.3 连接客户端
mongo 192.168.10.135:27017
常用客户端
自带命令行客户端
Navicat for mongodb
MongoVUE
Studio 3T
Robo 3T
RockMongo
2.4 常用命令
2.4.1 创建数据库
MongoDB 创建数据库的语法格式如下:
use DATABASE_NAME
如果数据库不存在,则创建数据库,否则切换到指定数据库。以下实例我们创建了数据库 zhoutian:
> use zhoutian
switched to db zhoutian
> db
zhoutian
>
如果你想查看所有数据库,可以使用 show dbs 命令:
> show dbs
admin 0.000GB
local 0.000GB
>
可以看到,我们刚创建的数据库zhoutian并不在数据库的列表中,要显示它,我们需要向zhoutian数据库插入一些数据。
> db.mycollection.insert({"name":"周天"})
WriteResult({ "nInserted" : 1 })
> show dbs
local 0.078GB
zhoutian 0.078GB
test 0.078GB
>
2.4.2 删除数据库
MongoDB 删除数据库的语法格式如下:
db.dropDatabase()
删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名。以下实例我们删除了数据库 zhoutian。
首先,查看所有数据库:
> show dbs
local 0.078GB
zhoutian 0.078GB
test 0.078GB
接下来我们切换到数据库 zhoutian:
> use zhoutian
switched to db zhoutian
>
执行删除命令:
> db.dropDatabase()
{ "dropped" : "zhoutian", "ok" : 1 }
最后,我们再通过show dbs命令数据库是否删除成功:
show dbs
local 0.078GB
test 0.078GB
>
2.4.3 创建集合
MongoDB中使用 createCollection()方法来创建集合。
语法格式:
db.createCollection(name, options)
参数说明:
name: 要创建的集合名称
options: 可选参数, 指定有关内存大小及索引的选项。options 可以是如下参数:
字段 | 类型 | 描述 |
---|---|---|
capped | 布尔 | (可选)如果为 true,则创建固定集合。 固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数。 |
autoIndexId | 布尔 | (可选)如为 true,自动在 _id 字段创建索引。默认为 false。 |
size | 数值 | (可选)为固定集合指定一个最大值(以字节计)。 如果 capped 为true,也需要指定该字段。 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量。 |
在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。
在 test 数据库中创建 mycollection集合:
> use test
switched to db test
> db.createCollection("mycollection")
{ "ok" : 1 }
>
如果要查看已有集合,可以使用 show collections或show tables 命令:
> show collections
mycollection
system.indexes
下面是带有几个关键参数的 createCollection() 的用法:
创建固定集合 mycol,整个集合空间大小 6142800 KB, 文档最大个数为 10000 个。
> db.createCollection("mycol", { capped : true, autoIndexId : true, size : 6142800, max : 10000 } )
{ "ok" : 1 }
>
在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。
> db.mycol2.insert({"name" : "嗨"})
> show collections
mycol2
...
2.4.4 删除集合
集合删除语法格式如下:
db.collection_name.drop()
以下实例删除了zhoutian数据库中的集合 site:
> use zhoutian
switched to db zhoutian
> show tables
site
> db.site.drop()
true
> show tables
>
2.4.5 插入文档
MongoDB 使用 insert() 或 save() 方法向集合中插入文档,语法如下:
db.COLLECTION_NAME.insert(document)
或
db.COLLECTION_NAME.save(document)
save():如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,可以使用 db.collection.insertOne() 或 db.collection.replaceOne() 来代替。
insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。
3.2 版本之后新增了 db.collection.insertOne() 和 db.collection.insertMany()。
以下文档可以存储在 MongoDB 的zhoutian数据库 的 mycollection 集合中:
>db.mycollection.insert({title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
by: '周天',
url: 'http://www.zhoutian.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
>var document = {title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
by: '周天',
url: 'http://www.zhoutian.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
}
>db.mycollection.insert(document)
2.4.6 更新文档
MongoDB 使用 update() 和 save() 方法来更新集合中的文档。
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 : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
writeConcern :可选,抛出异常的级别。
我们在集合 mycollection中插入如下数据:
>db.mycollection.insert({
title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
by: '周天',
url: 'http://www.zhoutian.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
接着我们通过 update() 方法来更新标题(title):
>db.mycollection.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) # 输出信息
> db.col.find().pretty()
{
"_id" : ObjectId("56064f89ade2f21f36b03136"),
"title" : "MongoDB",
"description" : "MongoDB 是一个 Nosql 数据库",
"by" : "周天",
"url" : "http://www.zhoutian.com",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
>
可以看到标题(title)由原来的 "MongoDB 教程" 更新为了 "MongoDB"。
以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,则需要设置 multi 参数为 true。
>db.mycollection.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}},{multi:true})
save() 方法通过传入的文档来替换已有文档,_id 主键存在就更新,不存在就插入。语法格式如下:
db.COLLECTION_NAME.save(
<document>,
{
writeConcern: <document>
}
)
参数说明:
document : 文档数据。
writeConcern :可选,抛出异常的级别。
以下实例中我们替换了 _id 为 56064f89ade2f21f36b03136 的文档数据:
>db.mycollection.save({
"_id" : ObjectId("56064f89ade2f21f36b03136"),
"title" : "MongoDB",
"description" : "MongoDB 是一个 Nosql 数据库",
"by" : "zhoutian",
"url" : "http://www.zhoutian.com",
"tags" : [
"mongodb",
"NoSQL"
],
"likes" : 110
})
替换成功后,我们可以通过 find() 命令来查看替换后的数据
>db.mycollection.find().pretty()
{
"_id" : ObjectId("56064f89ade2f21f36b03136"),
"title" : "MongoDB",
"description" : "MongoDB 是一个 Nosql 数据库",
"by" : "zhoutian",
"url" : "http://www.zhoutian.com",
"tags" : [
"mongodb",
"NoSQL"
],
"likes" : 110
}
>
2.4.7 删除文档
MongoDB 使用remove()方法是用来移除集合中的数据。在执行remove()方法前先执行find()方法来判断执行的条件是否正确,这是一个比较好的习惯。
remove() 方法的基本语法格式如下所示:
db.COLLECTION_NAME.remove( <query>, <justOne> )
如果你的 MongoDB 是 2.6 版本以后的,语法格式如下:
db.COLLECTION_NAME.remove( <query>, { justOne: <boolean>, writeConcern: <document> } )
参数说明:
query :(可选)删除的文档的条件。
justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值false,则删除所有匹配条件的文档。
writeConcern :(可选)抛出异常的级别。
以下文档我们执行两次插入操作:
>db.mycollection.insert({
title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
by: '周天',
url: 'http://www.zhoutian.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
使用 find() 函数查询数据:
> db.mycollection.find()
{ "_id" : ObjectId("56066169ade2f21f36b03137"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "周天", "url" : "http://www.zhoutian.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
{ "_id" : ObjectId("5606616dade2f21f36b03138"), "title" : "MongoDB 教程","description" : "MongoDB 是一个 Nosql 数据库", "by" : "周天", "url" : "http://www.zhoutian.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
接下来我们移除 title 为 'MongoDB 教程' 的文档:
>db.mycollection.remove({'title':'MongoDB 教程'})
WriteResult({ "nRemoved" : 2 }) # 删除了两条数据
>db.mycollection.find()
…… # 没有数据
如果你只想删除第一条找到的记录可以设置 justOne 为 1,如下所示:
>db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)
如果你想删除所有数据,可以使用以下方式:
>db.COLLECTION_NAME.remove({})
>db.COLLECTION_NAME.find()
>
2.4.8 查询文档
MongoDB 查询文档使用 find() 方法。find() 方法以非结构化的方式来显示所有文档。MongoDB 查询数据的语法格式如下:
db.COLLECTION_NAME.find(query, projection)
query :可选,使用查询操作符指定查询条件
projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值,只需省略该参数即可(默认省略)。
如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:
>db.COLLECTION_NAME.find().pretty()
以下实例我们查询了集合 mycollection 中的数据:
> db.mycollection.find().pretty()
{
"_id" : ObjectId("56063f17ade2f21f36b03133"),
"title" : "MongoDB 教程",
"description" : "MongoDB 是一个 Nosql 数据库",
"by" : "周天",
"url" : "http://www.zhoutian.com",
"tags" : ["mongodb", "database", "NoSQL" ],
"likes" : 100
}
通过下表可以更好的理解 MongoDB 的条件语句查询:
操作 | 格式 | 范例 | RDBMS中的类似语句 |
---|---|---|---|
等于 | {:} | db.col.find({"by":"教程"}).pretty() | where by = '教程' |
小于 | {:{$lt:}} | db.col.find({"likes":{$lt:50}}).pretty() | where likes < 50 |
小于或等于 | {:{$lte:}} | db.col.find({"likes":{$lte:50}}).pretty() | where likes <= 50 |
大于 | {:{$gt:}} | db.col.find({"likes":{$gt:50}}).pretty() | where likes > 50 |
大于或等于 | {:{$gte:}} | db.col.find({"likes":{$gte:50}}).pretty() | where likes >= 50 |
不等于 | {:{$ne:}} | db.col.find({"likes":{$ne:50}}).pretty() | where likes != 50 |
MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。
语法格式如下:
>db.COLLECTION_NAME.find({key1:value1, key2:value2}).pretty()
> db.mycollection.find({"by":"周天", "title":"MongoDB 教程"}).pretty()
MongoDB OR 条件语句使用了关键字 $or,语法格式如下:
>db.COLLECTION_NAME.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
>db.mycollection.find({$or:[{"by":"周天"},{"title": "MongoDB 教程"}]}).pretty()
以下实例演示了 AND 和 OR 联合使用,类似常规 SQL 语句为: 'where likes>50 AND (by = '周天' OR title = 'MongoDB 教程')'
>db.mycollection.find({"likes": {$gt:50}, $or: [{"by": "周天"},{"title": "MongoDB 教程"}]}).pretty()
如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。limit()方法基本语法如下所示:
>db.COLLECTION_NAME.find().limit(NUMBER)
集合 mycollection 中的数据如下:
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP 教程", "description" : "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", "by" : "周天", "url" : "http://www.zhoutian.com", "tags" : [ "php" ], "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "description" : "Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。", "by" : "周天", "url" : "http://www.zhoutian.com", "tags" : [ "java" ], "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "周天", "url" : "http://www.zhoutian.com", "tags" : [ "mongodb" ], "likes" : 100 }
以下实例为显示查询文档中的两条记录:
> db.mycollection .find({},{"title":1,_id:0}).limit(2)
{ "title" : "PHP 教程" }
{ "title" : "Java 教程" }
>
注:如果你们没有指定limit()方法中的参数则显示集合中的所有数据。
我们除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。skip() 方法脚本语法格式如下:
>db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
以下实例只会显示第二条文档数据
>db.mycollection.find({},{"title":1,_id:0}).limit(1).skip(1)
{ "title" : "Java 教程" }
>
注:skip()方法默认参数为 0 。
在 MongoDB 中使用 sort() 方法对数据(文档)进行排序,sort() 方法可以通过参数指定排序的字段,并使用1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。sort()方法基本语法如下所示:
>db.COLLECTION_NAME.find().sort({KEY:1})
mycollection集合中的数据如下:
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP 教程", "description" : "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", "by" : "周天", "url" : "http://www.zhoutian.com", "tags" : [ "php" ], "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "description" : "Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。", "by" : "周天", "url" : "http://www.zhoutian.com", "tags" : [ "java" ], "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "周天", "url" : "http://www.zhoutian.com", "tags" : [ "mongodb" ], "likes" : 100 }
以下实例演示了 mycollection 集合中的数据按字段 likes 的降序排列:
>db.mycollection.find({},{"title":1,_id:0}).sort({"likes":-1})
{ "title" : "PHP 教程" }
{ "title" : "Java 教程" }
{ "title" : "MongoDB 教程" }
>
$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。
如果想获取 "col" 集合中 title 为 String 的数据,你可以使用以下命令:
db.col.find({"title" : {$type : 2}})
或
db.col.find({"title" : {$type : 'string'}})
2.4.9 聚合查询
MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。 MongoDB中聚合的方法使用aggregate()。
基本语法为:
>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
为了便于理解,先将常见的mongo的聚合表达式和mysql的查询做下类比:
表达式 | SQL操作 | 描述 |
---|---|---|
$group | group by | 分组 |
$sum | count()、sum() | 计算总和。 |
$avg | avg() | 计算平均值 |
$min | min() | 获取集合中所有文档对应值得最小值。 |
$max | max() | 获取集合中所有文档对应值得最大值。 |
$match | where、having | 查询条件 |
$sort | order by | 排序 |
$limit | limit | 取条数 |
$project | select | 选择 |
$lookup(v3.2 新增) | join | 连接 |
下面以一个案例来演示,集合中的数据如下
> db.mycollection.find()
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854cf"), "name" : "zhaoyun1", "age" : 1, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d0"), "name" : "zhaoyun2", "age" : 2, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d1"), "name" : "zhaoyun3", "age" : 3, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d2"), "name" : "zhaoyun4", "age" : 4, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d3"), "name" : "zhaoyun5", "age" : 5, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d4"), "name" : "zhaoyun6", "age" : 6, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d5"), "name" : "zhaoyun7", "age" : 7, "city" : "TJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d6"), "name" : "zhaoyun8", "age" : 8, "city" : "TJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d7"), "name" : "zhaoyun9", "age" : 9, "city" : "TJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d8"), "name" : "zhaoyun10", "age" : 10, "city" : "TJ"}
$group
按照城市分组对年龄进行求和
> db.mycollection.aggregate([{$group : {_id : "$city", snum : {$sum : "$age"}}}])
{ "_id" : "TJ", "snum" : 34 }
{ "_id" : "BJ", "snum" : 21 }
$sum
按照城市分组对年龄进行求和
> db.mycollection.aggregate([{$group : {_id : "$city", snum : {$sum : "$age"}}}])
{ "_id" : "TJ", "snum" : 34 }
{ "_id" : "BJ", "snum" : 21 }
按照城市分组求总个数
> db.mycollection.aggregate([{$group : {_id : "$city", sc : {$sum : 1}}}])
{ "_id" : "TJ", "sc" : 4 }
{ "_id" : "BJ", "sc" : 6 }
$avg
按照城市分组对年龄进行求平均
> db.mycollection.aggregate([{$group : {_id : "$city", avg : {$avg : "$age"}}}])
{ "_id" : "TJ", "avg" : 8.5 }
{ "_id" : "BJ", "avg" : 3.5 }
$min
按照城市分组获得每组最小年龄
> db.mycollection.aggregate([{$group : {_id : "$city", min : {$min : "$age"}}}])
{ "_id" : "TJ", "min" : 7 }
{ "_id" : "BJ", "min" : 1 }
$max
按照城市分组获得每组最大年龄
> db.mycollection.aggregate([{$group : {_id : "$city", max : {$max : "$age"}}}])
{ "_id" : "TJ", "max" : 10 }
{ "_id" : "BJ", "max" : 6 }
$match
取年龄大于3并且小于10的记录再按照城市分组求个数和
> db.mycollection.aggregate( [{ $match : { age : { $gt : 3, $lte : 10 } } },{ $group: { _id: "$city",count: { $sum: 1 } } }] );
{ "_id" : "TJ", "count" : 4 }
{ "_id" : "BJ", "count" : 3 }
$sort
按照年龄的倒序显示
> db.mycollection.aggregate( [{ $sort:{ age :-1}}] )
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d8"), "name" : "zhaoyun10", "age" : 10, "city" : "TJ"}
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d7"), "name" : "zhaoyun9", "age" : 9, "city" : "TJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d6"), "name" : "zhaoyun8", "age" : 8, "city" : "TJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d5"), "name" : "zhaoyun7", "age" : 7, "city" : "TJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d4"), "name" : "zhaoyun6", "age" : 6, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d3"), "name" : "zhaoyun5", "age" : 5, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d2"), "name" : "zhaoyun4", "age" : 4, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d1"), "name" : "zhaoyun3", "age" : 3, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d0"), "name" : "zhaoyun2", "age" : 2, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854cf"), "name" : "zhaoyun1", "age" : 1, "city" : "BJ" }
$limit
取得前5条数据
> db.mycollection.aggregate( { $limit:5} )
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854cf"), "name" : "zhaoyun1", "age" : 1, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d0"), "name" : "zhaoyun2", "age" : 2, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d1"), "name" : "zhaoyun3", "age" : 3, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d2"), "name" : "zhaoyun4", "age" : 4, "city" : "BJ" }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d3"), "name" : "zhaoyun5", "age" : 5, "city" : "BJ" }
$project
不显示_id字段,显示 name和city字段
> db.mycollection.aggregate( [ {$project :{_id:0,name:1,city:1} } ] )
{ "name" : "zhaoyun1", "city" : "BJ" }
{ "name" : "zhaoyun2", "city" : "BJ" }
{ "name" : "zhaoyun3", "city" : "BJ" }
{ "name" : "zhaoyun4", "city" : "BJ" }
{ "name" : "zhaoyun5", "city" : "BJ" }
{ "name" : "zhaoyun6", "city" : "BJ" }
{ "name" : "zhaoyun7", "city" : "TJ" }
{ "name" : "zhaoyun8", "city" : "TJ" }
{ "name" : "zhaoyun9", "city" : "TJ" }
{ "name" : "zhaoyun10", "city" : "TJ" }
$lookup
用于多文档关联,类似于SQL的多表关联
建立新的集合
> db.sex.find()
{ "_id" : 1, "name" : "male" }
{ "_id" : 2, "name" : "female" }
给mycollection新增字段sex
> db.mycollection.update({},{$set:{sex:1}},{multi:1})
WriteResult({ "nMatched" : 10, "nUpserted" : 0, "nModified" : 10 })
> db.mycollection.find()
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854cf"), "name" : "zhaoyun1", "age" : 1, "city" : "BJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d0"), "name" : "zhaoyun2", "age" : 2, "city" : "BJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d1"), "name" : "zhaoyun3", "age" : 3, "city" : "BJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d2"), "name" : "zhaoyun4", "age" : 4, "city" : "BJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d3"), "name" : "zhaoyun5", "age" : 5, "city" : "BJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d4"), "name" : "zhaoyun6", "age" : 6, "city" : "BJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d5"), "name" : "zhaoyun7", "age" : 7, "city" : "TJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d6"), "name" : "zhaoyun8", "age" : 8, "city" : "TJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d7"), "name" : "zhaoyun9", "age" : 9, "city" : "TJ", "sex" : 1 }
{ "_id" : ObjectId("5cceaf55e7a4cf28dc5854d8"), "name" : "zhaoyun10", "age" : 10, "city" : "TJ", "sex" : 1 }
将两个文档关联
> db.mycollection.aggregate([{$lookup: {from:"sex",localField:"sex",foreignField:"_id",as:"docs"}},{$project: {"_id":0}}])
{ "name" : "zhaoyun1", "age" : 1, "city" : "BJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun2", "age" : 2, "city" : "BJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun3", "age" : 3, "city" : "BJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun4", "age" : 4, "city" : "BJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun5", "age" : 5, "city" : "BJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun6", "age" : 6, "city" : "BJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun7", "age" : 7, "city" : "TJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun8", "age" : 8, "city" : "TJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun9", "age" : 9, "city" : "TJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
{ "name" : "zhaoyun10", "age" : 10, "city" : "TJ", "sex" : 1, "docs" : [ { "_id" : 1, "name" : "male" } ] }
mycollection中的数据字段sex关联sex中的数据字段_id,在mycollection中显示sex对应的中文,显示在docs中,不显示mycollection中的__id
$lookup中的参数:
from:需要关联的表【sex】
localField: 【mycollection】表需要关联的键。
foreignField:【sex】的matching key 主键
as:对应的外键集合的数据,【因为是一对多的】
3 MongoDB Java客户端
3.1 mongodb-java-API
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.10.1</version>
</dependency>
public class MongodbDemo {
private MongoClient client;
@Before public void init() {
client = new MongoClient("192.168.10.135",27017);
}
@Test public void connectDB() {
MongoDatabase database = client.getDatabase("zhoutian");
System.out.println("connect successful----"+database.getName());
}
@Test public void createCollection() {
MongoDatabase database = client.getDatabase("zhoutian");
database.createCollection("mycollection");
System.out.println("集合创建成功");
}
@Test public void getCollection() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
System.out.println("获取集合成功:"+collection.getNamespace());
MongoIterable<String> collectionNames = database.listCollectionNames();
for (String string : collectionNames) {
System.out.println("collection name : "+ string);
}
}
@Test public void insertDocument() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
Document document = new Document("name", "James");
document.append("age", 34);
document.append("sex", "男");
Document document2 = new Document("name", "wade");
document2.append("age", 36);
document2.append("sex", "男");
Document document3 = new Document("name", "cp3");
document3.append("age", 32);
document3.append("sex", "男");
List<Document> documents = new ArrayList<>();
documents.add(document);
documents.add(document2);
documents.add(document3);
collection.insertMany(documents);
System.out.println("文档插入成功");
}
@Test public void findDocuments() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
FindIterable<Document> iterable = collection.find();
for (Document document : iterable) {
System.out.println(document.toJson());
}
// MongoCursor<Document> mongoCursor = iterable.iterator();
// while (mongoCursor.hasNext()) {
// Document document = mongoCursor.next();
// System.out.println(document.toJson());
// }
}
@Test public void updateDocuments() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
collection.updateMany(
Filters.eq("age",18), new Document("$set", new Document("age", 20))
);
FindIterable<Document> iterable = collection.find();
for (Document document : iterable) {
System.out.println(document.toJson());
}
}
@Test public void deleteDocuments() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
// 删除符合条件的第一个文档
collection.deleteOne(Filters.eq("age",20));
collection.deleteOne(Filters.eq("name","James"));
// 删除符合条件的所有文档
collection.deleteMany(Filters.eq("age",20));
FindIterable<Document> iterable = collection.find();
for (Document document : iterable) {
System.out.println(document.toJson());
}
}
@Test
public void testQuery1() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
//获取第一条记录
Document first = collection.find().first();
//根据key值获得数据
System.out.println(first.get("name"));
//查询指定字段
Document qdoc=new Document();
qdoc.append("_id", 0);//0是不包含字段
qdoc.append("name", 1);//1是包含字段
FindIterable<Document> findIterable = collection.find().projection(qdoc);
for (Document document : findIterable) {
System.out.println(document.toJson());
}
}
@Test public void testQuery2() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
//按指定字段排序
Document sdoc=new Document();
//按年龄的倒序
sdoc.append("age", -1);
FindIterable<Document> findIterable = collection.find().sort(sdoc);
for (Document document : findIterable) {
System.out.println(document.toJson());
}
}
}
Filters
利用Filters可以进行条件过滤,比如:eq、ne、gt、lt、gte、lte、in、nin、and、or、not、nor、exists、type、mod、regex、text、where、all、elemMatch、size、bitsAllClear、bitsAllSet、bitsAnyClear、bitsAnySet、geoWithin、geoWithin、geoWithinBox、geoWithinPolygon、geoWithinCenter、geoWithinCenterSphere、geoIntersects、near、nearSphere 等。
需要引入com.mongodb.client.model.Filters类
介绍几个常用的过滤操作。
@Test public void testFilters1() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
//获得name等于zhaoyun1的记录
FindIterable<Document> findIterable =collection.find(Filters.eq("name", "zhaoyun1"));
for (Document document : findIterable) {
System.out.println(document.toJson());
}
}
@Test public void testFilters2() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
//获得age大于3的记录
FindIterable<Document> findIterable =collection.find(Filters.gt("age", 3));
for (Document document : findIterable) {
System.out.println(document.toJson());
}
}
@Testpublic void testFilters3() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
//获得age大于3的记录 或者 name=zhaoyun1
FindIterable<Document> findIterable =
collection.find(Filters.or(Filters.eq("name", "zhaoyun1"),Filters.gt("age", 3)));
for (Document document : findIterable) {
System.out.println(document.toJson());
}
}
BasicDBObject
利用BasicDBObject可以实现模糊查询和分页查询
@Test public void testLike() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
//利用正则 模糊匹配 包含zhaoyun1
Pattern pattern = Pattern.compile("^.*zhaoyun1.*$", Pattern.CASE_INSENSITIVE);
BasicDBObject query = new BasicDBObject();
//name包含zhaoyun1
query.put("name",pattern);
FindIterable<Document> findIterable = collection.find(query);
for (Document document : findIterable) {
System.out.println(document.toJson());
}
}
@Test public void testPage() {
MongoDatabase database = client.getDatabase("zhoutian");
MongoCollection<Document> collection = database.getCollection("mycollection");
//利用正则 模糊匹配 name包含zhaoyun1
Pattern pattern = Pattern.compile("^.*zhaoyun.*$", Pattern.CASE_INSENSITIVE);
//查询条件
BasicDBObject query = new BasicDBObject();
query.put("name",pattern);
//排序
BasicDBObject sort = new BasicDBObject();
//按年龄倒序
sort.put("age", -1);
//获得总条数
System.out.println("共计:"+collection.count()+"条记录");
/** 取出第1至3条记录
* query : 查询条件
* sort: 排序
* skip : 起始数 从0开始
* limit:取几条
*/
FindIterable<Document> findIterable = collection.find(query).sort(sort).skip(0).limit(3);
for (Document document : findIterable) {
System.out.println(document.toJson());
}
}