MongoDB
如果你之前只接触过关系型数据库如 Oracle、Mysql 或 SQL Server,在学习 MongoDB 时可能会感到不安,突然有一款数据库不支持外键,不支持事务,不支持数据类型约定,会给人一种没法用的感觉。
MongoDB 就是这样一款非关系型的数据库,什么叫非关系型?就是把数据直接放进一个大仓库,不标号、不连线、单纯的堆起来。传统数据库由于受到各种关系的累赘,各种数据形式的束缚,难以处理海量数据以及超高并发的业务场景。
为了解决上述问题,必须有一款自废武功,以求在更高层次上突破瓶颈的数据库系统。就像张无忌忘记招式从而学习太极一样,摈弃了固有模式的 MongoDB 才能应对 Facebook 上亿比特的海量数据。
简介
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富、最像关系数据库的。
由于关系型数据库存储对数据之间存在高度的关联,在数据量达到上万亿比特时,关系型数据库所特有的约束和关联就会成为性能瓶颈。非关系型数据库采用了另一种思维方式,即不考虑数据之间千丝万缕的联系,存储也不需要固定的模式,这样无需多余的操作就能成倍地扩展数据量。
MongoDB 支持的数据结构非常松散,是类似 json 的 bson 格式,因此可以存储比较复杂的数据类型,比如:
{
title: '上周实验学习报告',
body: '上周我们学习了 MongoDB 的知识',
by: 'shiyanlou',
}
如上是一个基本的文档形式,它存储了一封邮件的简易信息,从这里也可以看出 MongoDB 的‘松散’,它和关系型数据库不同,一个集合里面可以有不同格式的文档,而关系型数据库的每一条记录都拥有相同的字段。
面向集合的存储
在 MongoDB 中,一个数据库包含多个集合,类似于 MySQL 中一个数据库包含多个表;一个集合包含多个文档,类似于 MySQL 中一个表包含多条数据。
可以把集合记为表,文档记为一条记录。
这样命名是有原因的,因为 MongoDB 没有行列统一的表格式排列,而是采用一个大仓库的形式将所有数据包纳其中。文档也一样,它是一段自由独立的数据,受外部限制少,所以区别于关系型数据库的记录。
虚拟机开机配置
启动 MongoDB 服务,因为 MongoDB 并不随系统一起启动,可能以下命令运行后会等一小段的时间才会启动完毕。
sudo service mongodb start
进入 MongoDB 命令行操作界面(可能会出现 connect failed,多试几次就行),在命令行中敲 exit
可以退出。
mongo
实验中的布尔类型的 ture 用 1 代替,false 用 0 代替。
基本概念
数据库
- 一个 MongoDB 可以创建多个数据库
- 使用
show dbs
可以查看所有数据库的列表 - 执行
db
命令则可以查看当前数据库对象或者集合 - 运行
use
命令可以连接到指定的数据库
$ mongo # 进入到mongo命令行
> use test # 连接到test数据库
注意:数据库名可以是任何字符,但是不能有空格、点号和 $ 字符。
文档
文档是 MongoDB 的核心,类似于 SQLite 数据库(关系数据库)中的每一行数据。多个键及其关联的值放在一起就是文档。在 Mongodb 中使用一种类 json 的 bson 存储数据,bson 数据可以理解为在 json 的基础上添加了一些 json 中没有的数据类型。
例如:
{"company":"Chenshi keji"}
文档的逻辑联系
假设有两个文档:
# user文档
{
"name": "Tom Hanks",
"contact": "987654321",
"dob": "01-01-1991"
}
# address文档
{
"building": "22 A, Indiana Apt",
"pincode": 123456,
"city": "chengdu",
"state": "sichuan"
}
关系 1:嵌入式关系:把 address 文档嵌入到 user 文档中
# 这就是嵌入式的关系
{
"name": "Tom Hanks",
"contact": "987654321",
"dob": "01-01-1991",
"address":
[{
"building": "22 A, Indiana Apt",
"pincode": 123456,
"city": "chengdu",
"state": "sichuan"
},
{
"building": "170 A, Acropolis Apt",
"pincode": 456789,
"city": "beijing",
"state": "beijing"
}]
}
关系 2:引用式关系:将两个文档分开,通过引用文档的_id 字段来建立关系
# 这就是引用式关系
{
"name": "Tom Benzamin",
"contact": "987654321",
"dob": "01-01-1991",
"address_ids": [
ObjectId("52ffc4a5d85242602e000000") #对应address文档的id字段
]
}
在实际应用的时候,嵌入式关系比较适合一对一的关系,引用式关系比较适合一对多或者多对多的情况。
集合
集合就是一组文档的组合,就相当于是关系数据库中的表,在 MongoDB 中可以存储不同的文档结构的文档。
例如:
{"company":"Chenshi keji"} {"people":"man","name":"peter"}
上面两个文档就可以存储在同一个集合中,在关系型数据库中是很难实现上述数据结构的,要么需要定义大量的字段,对于一些字段名不确定的属性,关系型数据库会更加力不从心。
MongoDB 的做法也不是完美的。比如在存储用户信息的时候,用户名和密码分别用 username 和 password 字段表示。 关系型数据库只需要把字段名作为表结构的一部分保存起来就可以了,而 MongoDB 需要将这两个字段名存储多次,每一条记录都会存储一次字段名,在一些原本就比较碎片化的字段上,用于存储字段名所耗费的空间甚至会超过存储数值的空间,比较好的解决办法是采用尽可能短的字段名,不过这又涉及到了可读性的问题,需要大家在二者之间进行权衡。
元数据
数据库的信息存储在集合中,他们统一使用系统的命名空间:DBNAME.system.*
。
DBNAME 可用 db 或数据库名替代:
- DBNAME.system.namespaces :列出所有名字空间
- DBNAME.system.indexs :列出所有索引
- DBNAME.system.profile :列出数据库概要信息
- DBNAME.system.users :列出访问数据库的用户
- DBNAME.system.sources
数据库和集合基本操作
数据库的创建与销毁
创建数据库
启动服务后,进入 MongoDB 命令行操作界面:
mongo
使用 use 命令创建数据库:
use mydb
查看当前连接的数据库:
db
查看所有的数据库:
show dbs
列出的所有数据库中看不到 mydb 或者显示 mydb(empty) ,因为 mydb 为空,里面没有任何东西,MongoDB 不显示或显示 mydb(empty)。
销毁数据库
使用 db.dropDatabase() 销毁数据库:
> use local
switched to db local
> db.dropDatabase()
查看所有的数据库:
> show dbs
可以发现 local 数据库已经被删除了。
集合的创建和删除
创建数据库
启动服务后,进入 MongoDB 命令行操作界面:
mongo
使用 use 命令创建数据库:
use mydb
查看当前连接的数据库:
db
查看所有的数据库:
show dbs
列出的所有数据库中看不到 mydb 或者显示 mydb(empty) ,因为 mydb 为空,里面没有任何东西,MongoDB 不显示或显示 mydb(empty)。
销毁数据库
使用 db.dropDatabase() 销毁数据库:
> use local
switched to db local
> db.dropDatabase()
查看所有的数据库:
> show dbs
可以发现 local 数据库已经被删除了。
向集合中插入数据
使用 insert()
插入数据时,如果 users 集合没有创建会自动创建。
> use mydb
switched to db mydb
> db.users.insert([
... { name : "jam",
... email : "jam@qq.com"
... },
... { name : "tom",
... email : "tom@qq.com"
... }
... ])
使用 save()
插入数据时,如果 users 集合没有创建会自动创建。
> use mydb2
switched to db mydb2
> db.users.save([
... { name : "jam",
... email : "jam@qq.com"
... },
... { name : "tom",
... email : "tom@qq.com"
... }
... ])
insert 和 save 的区别:为了方便记忆,可以先从字面上进行理解,insert 是插入,侧重于新增一个记录的含义;save 是保存,可以保存一个新的记录,也可以保存对一个记录的修改。因此,insert 不能插入一条已经存在的记录,如果已经有了一条记录(以主键为准),insert 操作会报错,而使用 save 指令则会更新原记录。
查询语句
find() 语句
启动 MongoDB 服务,因为 MongoDB 并不随系统一起启动,可能以下命令运行后会等一小段的时间才会启动完毕。
sudo service mongodb start
进入 MongoDB 命令行操作界面,在命令行中敲 exit
可以退出。
mongo
find() 用法:db.COLLECTION_NAME.find()
> use post
> db.post.insert([
{
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by: 'shiyanlou',
url: 'http://www.shiyanlou.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
},
{
title: 'NoSQL Database',
description: "NoSQL database doesn't have tables",
by: 'shiyanlou',
url: 'http://www.shiyanlou.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 20,
comments: [
{
user:'user1',
message: 'My first comment',
dateCreated: new Date(2013,11,10,2,35),
like: 0
}
]
}
])
查询数据,不加任何参数默认返回所有数据记录:
> db.post.find()
这条语句会返回 post 集合中的所有文档,实际应用中不常见,因为这样会导致大量的数据传输,造成服务器响应迟缓甚至失去响应。
pretty() 语句
pretty() 可以使查询输出的结果更美观。
> db.post.find().pretty()
例如:
> db.users.find()
{ "_id" : ObjectId("5c35657e6ee1e0307e215fc8"), "name" : "shiyanlou", "age" : "21" }
> db.users.find().pretty()
{
"_id" : ObjectId("5c35657e6ee1e0307e215fc8"),
"name" : "shiyanlou",
"age" : "21"
}
如果你想让 mongo shell 始终以 pretty 的方式显示返回数据,可以通过下面的指令实现:
echo "DBQuery.prototype._prettyShell = true" >> ~/.mongorc.js
这样就把默认的显示方式设置为 pretty 了。
> db.users.find()
{
"_id" : ObjectId("5c35657e6ee1e0307e215fc8"),
"name" : "bage",
"age" : "21"
}
MongoDB中的AND
MongoDB 不需要类似于其他数据库的 AND 运算符,当 find() 中传入多个键值对时,MongoDB 就会将其作为 AND 查询处理。
用法:db.mycol.find({ key1: value1, key2: value2 }).pretty()
> db.post.find({"by":"shiyanlou","to": "chenshi"}).pretty()
如上语句就可以查找出 by 字段为 ‘shiyanlou’,to 字段为 ‘chenshi’ 的所有记录,意思是找出系统中由 shiyanlou 发送给 chenshi 的所有邮件。
它对应的关系型 SQL 语句为:
SELECT * FROM post WHERE by = 'shiyanlou' AND to = 'chenshi'
MongoDB中的OR
MongoDB 中,OR 查询语句以 $or
作为关键词,用法如下:
> db.post.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
操作示例:
> db.post.find({
$or:[
{"by":"shiyanlou"},
{"title": "MongoDB Overview"}
]
}).pretty()
它对应的关系型 SQL 语句为:
SELECT * FROM post WHERE by = 'shiyanlou' OR title = 'MongoDB Overview'
同时使用AND和OR
操作范例:
> db.post.find({
"likes": {$gt:10},
$or: [
{"by": "shiyanlou"},
{"title": "MongoDB Overview"}
]
}).pretty()
{$gt:10}
表示大于 10,另外,$lt
表示小于、$gte
表示大于等于、$lte
表示小于等于、$ne
表示不等于。
如果这样的符号记起来稍微有点麻烦,可以根据它们的全写配合记忆:
gt
:大于 greater thanlt
:小于 less thangte
:大于或等于 greater than equallte
:小于或等于 less than equal
练习
提示:以下代码仅供参考,为了获得更好的学习体验,请按照自己的想法实现练习题任务,如果你遇到了困难可以仔细对照下面代码检查你的错误,如果你可以轻易地完成练习题,请忽略下面这一堆无技术含量的代码。
> use practice
switched to db practice
> db.createCollection("student")
{ "ok" : 1 }
> db.student.insert([
{
"name":"黄豆",
"age":21,
"gender":"female"
},
{
"name":"周大壮",
"age":20,
"gender":"male"
},
{
"name":"张小妹",
"age":17,
"gender":"female"
}])
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 3,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
> db.student.find({"gender":"male"})
{
"_id" : ObjectId("5c3593ef5b79be0e0e096e55"),
"name" : "周大壮",
"age" : 20,
"gender" : "male"
}
> db.student.find({"age":{$gt:20}})
{
"_id" : ObjectId("5c3593ef5b79be0e096e5587"),
"name" : "黄豆",
"age" : 21,
"gender" : "female"
}
> db.student.find({"name":/^张/})
{
"_id" : ObjectId("5c3593ef5b79be0e096e5588"),
"name" : "张小妹",
"age" : 17,
"gender" : "female"
}
参考链接
本实验课程参考以下文档:
文档的基本操作
虚拟机开机配置
启动 MongoDB 服务,因为 MongoDB 并不随系统一起启动,可能以下命令运行后会等一小段的时间才会启动完毕。
sudo service mongodb start
进入 MongoDB 命令行操作界面,在命令行中敲 exit
可以退出。
mongo
实验中的布尔类型的 true 用 1 代替,false 用 0 代替。
创建数据库
范例:
进入 MongoDB 命令行操作界面之后,进行如下操作:
> use Chenshi
switched to db Chenshi
您可以使用 db
命令查看您当前选择的数据库:
> db
Chenshi
您也可以通过使用 show dbs
命令查看所有的数据库:
> show dbs
在这条命令之后您刚才创建的 Chenshi 不会显示,它只有在向数据库中插入了数据之后才能显示。
删除数据库
语法:db.dropDatabase()
假设刚才我们使用语句 db.computer.insert({"name":"shiyanlou"})
对数据库 Chenshi 进行了数据插入操作。
> show dbs # 显示当前系统中的所有数据库
> use Chenshi # 转到Chenshi数据库
switched to db Chenshi
> db.dropDatabase()
{"dropped":"Chenshi","ok":1}
> show dbs
散出集合
语法:db.COLLECTION.drop()
操作实例:
> use Chenshi
switched to db Chenshi
> show collections
shiyanlou
shiyanlou2
system.indexes
> db.shiyanlou.drop()
true
> show collections
shiyanlou2
system.indexes
删除成功。
注意:当您要删除指定的集合时,用您想要删除的集合名称替代 COLLECTION 即可。
插入文档
语法:db.COLLECTION_NAME.insert(document)
操作实例:
> userdoc1=({"user_id":1,"name":"cloud","state":"active","actor":"user","e-mail":"test@qq.com","VM_num":2,"time":[{"date":"2014-08-12","hour":"10:53 PM"}] })
> userdoc2=({"user_id":2,"name":"testadmin","state":"active","actor":"admin","e-mail":"test@qq.com","VM_num":2,"time":[{"date":"2014-08-11","hour":"06:34 AM"}] })
> doc1=({"name":"peter","position":"teacher"}) #先定义文档
> use Chenshi
switched to db Chenshi
> db.shiyanlou.insert(userdoc1)
WriteResult({"nInserted":1})
> db.shiyanlou.insert(userdoc2)
WriteResult({"nInserted":1})
> db.shiyanlou.insert(doc1)
WriteResult({"nInserted":1})
插入文档成功,当然也可以直接将文档的内容作为函数的参数直接替代 document。
更新文档
语法:db.COLLECTION_NAME.update(SELECTION_CRITERIA,UPDATED_DATA)
操作实例:
> db.shiyanlou.update({"user_id":2,"e-mail":"test@qq.com"},{$set:{"e-mail":"group@qq.com"}})
WriteResult({"nMatched":1,"nUpserted":1,"nModified":1})
> db.shiyanlou.find()
- 将 user_id=2 的文档的 e-mail 改为 group@qq.com
- 第一个大括号内容标示查找条件,第二个大括号内容则表示更新后的数据
- 默认的 update 函数只对一个文档更新,如果想作用所有文档,则需要加入 multi:true
操作实例:
db.shiyanlou.update({"e-mail":"test@qq.com"},{$set:{"e-mail":"group@qq.com"}},{multi:true})
替换已存在的文档
语法:db.COLLECTION_NAME.save({_id:ObjectId(),NEW_DATA})
操作实例:
> db.shiyanlou.save({"_id":ObjectId("53ea174ccb4c62646d9544f4"),"name":"Bob","position":"techer"})
WriteResult({"nMatched":1,"nUpserted":1,"nModified":1})
这里的 _id 对应的是要替换文档的 _id。
跟 insert 差不多,但是 save 更好用。
删除文档
语法:db.COLLECTION_NAME.remove(DELECTION_CRITERIA)
操作实例:
> db.shiyanlou.remove({"name":"Bob"})
WriteResult({"nRemoved":1})
其实 remove 函数的参数跟 update 函数的第一个参数一样,相当于查找条件,注意,不要误删!
删除后可以用查找命令确认数据:
> db.shiyanlou.find()
查询、索引与聚合
开发准备
初始化环境
启动 MongoDB 服务,因为 MongoDB 并不随系统一起启动,可能以下命令运行后会等一小段的时间才会启动完毕。
sudo service mongodb start
进入 MongoDB 命令行操作界面,在命令行中敲exit可以退出。
mongo
实验中的布尔类型的 ture 用 1 代替,false 用 0 代替。
初始化 MongoDB 数据库
> use Chenshi
switched to db Chenshi
> db.createCollection("shiyanlou") #无参数
{"ok":1}
> show collections
shiyanlou
system.indexes
> userdoc1=({"user_id":1,"name":"cloud","state":"active","actor":"user","e-mail":"test@qq.com","VM_num":2,"time":[{"date":"2014-08-12","hour":"10:53 PM"}] })
> userdoc2=({"user_id":2,"name":"testadmin","state":"active","actor":"admin","e-mail":"test@qq.com","VM_num":2,"time":[{"date":"2014-08-11","hour":"06:34 AM"}] })
> doc=({"name":"peter","position":"teacher"})
> db.shiyanlou.insert(userdoc1)
WriteResult({"nInserted":1})
> db.shiyanlou.insert(userdoc2)
WriteResult({"nInserted":1})
> db.shiyanlou.insert(doc)
WriteResult({"nInserted":1})
查询语句
find()
查询语句:db.COLLECTION_NAME.find(Parameter)
范例:
> db.shiyanlou.find()
这条指令用于查询 shiyanlou 集合中的全部文档信息,相当于 sqlite 中的 SELECT * FROM TABLE_NAME
。
2.1.1 条件操作符 1
MongoDB 中的条件操作符有:
$gt
:大于$lt
:小于$gte
:大于等于$lte
:小于等于
范例:
> db.shiyanlou.find({user_id:{$gt:1}})
> db.shiyanlou.find({user_id:{$lte:2,$gt:1}})
2.1.2 条件操作符 2
语法:$type:[key]
可选的 key 值如下:
- 1: 双精度型(Double)
- 2: 字符串(String)
- 3: 对象(Object)
- 4: 数组(Array)
- 5: 二进制数据(Binary data)
- 7: 对象 ID(Object id)
- 8: 布尔类型(Boolean)
- 9: 日期(Date)
- 10: 空(Null)
- 11: 正则表达式(Regular Expression)
- 13: JS 代码(Javascript)
- 14: 符号(Symbol)
- 15: 有作用域的 JS 代码(JavaScript with scope)
- 16: 32 位整型数(32-bit integer)
- 17: 时间戳(Timestamp)
- 18: 64 位整型数(64-bit integer)
- -1: 最小值(Min key)
- 127: 最大值(Max key)
范例:
> db.shiyanlou.find({"name":{$type:2}})
上面的命令是用于查找 name 是字符串的文档记录,它等同于下面的命令:
> db.shiyanlou.find({"name":{$type:'string'}})
2.1.3 limit() 与 skip()
读取指定数量的数据记录 limit()
。
范例:
> db.shiyanlou.find().limit(1)
读取一条记录,默认是排在最前面的那一条被读取。
读取时跳过指定数量的数据记录 skip()
。
范例:
> db.shiyanlou.find().limit(1).skip(1)
当然,还可以添加 find 的查找条件的参数,以便进行更精确的查找。
2.1.4 MongoDB 排序 sort()
与 sqlite 中的排序一样有升序和降序,其中升序用 1 表示,降序用 -1 表示。
语法:db.COLLECTION_NAME.find().sort({KEY:1|-1})
范例:
> db.shiyanlou.find().sort({"time":1})
索引ensureIndex()
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可能要花费几十秒甚至几分钟,这无疑对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库集合中一个文档或多个文档的值进行排序的一种结构。
语法:db.COLLECTION_NAME.ensureIndex({KEY:1|-1})
同样 1 代表升序,-1 代表降序。
范例:
> db.shiyanlou.ensureIndex({"name":1})
ensureIndex()
的可选参数:
参数 | 类型 | 描述 |
---|---|---|
background | Boolean | 建立索引要不要阻塞其他数据库操作,默认为 false |
unique | Boolean | 建立的索引是否唯一,默认 false |
name | string | 索引的名称,若未指定,系统自动生成 |
dropDups | Boolean | 建立唯一索引时,是否删除重复记录,默认 flase |
sparse | Boolean | 对文档不存在的字段数据不启用索引,默认 false |
expireAfterSeconds | integer | 设置集合的生存时间,单位为秒 |
v | index version | 索引的版本号 |
weights | document | 索引权重值,范围为 1 到 99999 |
default-language | string | 默认为英语 |
language_override | string | 默认值为 language |
范例:
>db.shiyanlou.ensureIndex({"user_id":1,"name":1},{background:1})
聚合aggregate()
语法:
db.COLLECTION_NAME.aggregate({
$match:{x:1},
{limit:NUM},
$group:{_id:$age}
})
这些参数都可选:
$match
:查询,跟 find 一样;$limit
:限制显示结果数量;$skip
:忽略结果数量;$sort
:排序;$group
:按照给定表达式组合结果。
范例:
> db.shiyanlou.aggregate([{$group:{_id:"$name", user:{$sum:"$user_id"}}}])
$name
意为取得 name 的值。
3.3.1 聚合表达式
名称 | 描述 |
---|---|
$sum | 计算总和 |
$avg | 计算平均值 |
min 和min和max | 计算最小值和最大值 |
$push | 在结果文档中插入值到一个数组 |
$addToSet | 在结果文档中插入值到一个数组,但不创建副本 |
$first | 根据资源文档的排序获取第一个文档数据 |
$last | 根据资源文档的排序获取最后一个文档数据 |
3.3.2 管道
MongoDB 的聚合管道将 MongoDB 文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。
聚合框架中常用的几个操作:
$project
:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。$match
:用于过滤数据,只输出符合条件的文档。$match
使用 MongoDB 的标准查询操作。$limit
:用来限制 MongoDB 聚合管道返回的文档数。$skip
:在聚合管道中跳过指定数量的文档,并返回余下的文档。$unwind
:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。$group
:将集合中的文档分组,可用于统计结果。$sort
:将输入文档排序后输出。$geoNear
:输出接近某一地理位置的有序文档。
范例:
> db.shiyanlou.aggregate([{$match:{user_id:{$gt:0,$lte:2}}},{$group:{_id:"user",count:{$sum:1}}}])
{"_id":"user","count":2}
高级查询与索引
初始化环境
启动 MongoDB 服务,因为 MongoDB 并不随系统一起启动,可能以下命令运行后会等一小段的时间才会启动完毕。
sudo service mongodb start
进入 MongoDB 命令行操作界面,在命令行中敲exit可以退出。
mongo
实验中的布尔类型的 ture 用 1 代替,false 用 0 代替。
覆盖索引查询
所有的查询字段都是索引的一部分;所有的查询返回字段在同一个索引中。
由于索引存在于 RAM 中,因而从索引中获取数据要比扫描文档更快。
范例:
创建如下 users 集合(使用前面所学的方法创建该集合):
{
"contact": "987654321",
"dob": "01-01-1991",
"gender": "M",
"name": "Tom Benzamin",
"user_name": "tombenzamin"
}
在 users 中创建一个联合索引:
> db.users.ensureIndex({gender:1,user_name:1})
该索引会覆盖下面的查询:
> db.users.find({gender:"M"},{user_name:1,_id:0})
对于上述查询,MongoDB 不会在数据库文件中查找,而会从索引中提取数据。因为索引中不包含 _id 字段,所以 _id 在查询中会默认返回,可以在查询结果中将其排除。而 db.users.find({gender:"M"},{user_name:1})
就不会被索引覆盖。
高级索引
创建如下 users 集合(使用前面所学的方法创建该集合):
{
"address": {
"city": "chengdu",
"province": "sichuan",
"pincode": "123"
},
"tags": [
"music",
"cricket",
"blogs"
],
"name": "clound"
}
索引数组字段,在数组中创建索引,需要对数组中的每个字段依次建立索引。所以在我们为数组 tags 创建索引时,会为 music、cricket、blogs 三个值建立单独的索引。
范例:
> db.users.ensureIndex({"tags":1})
创建索引后,我们可以这样检索集合的 tags 字段:
> db.users.find({tags:"cricket"})
为了验证我们使用了索引,可以使用 explain 命令:
> db.users.find({tags:"cricket"}).explain()
索引子文档字段
假设我们需要通过 city、province、pincode 字段来检索文档,由于这些字段是子文档的字段,所以我们需要对子文档建立索引。
范例:
为子文档的三个字段创建索引,命令如下:
> db.users.ensureIndex({"address.city":1,"address.province":1,"address.pincode":1})
一旦创建索引,我们可以使用子文档的字段来检索数据:
> db.users.find({"address.city":"chengdu"})
记住查询表达式必须遵循指定的索引的顺序。所以上面创建的索引将支持以下查询:
> db.users.find({"address.city":"chengdu","address.province":"sichuan"})
同样支持以下查询:
> db.users.find({"address.city":"chengdu","address.province":"sichuan","address.pincode":"123"})
原子操作
所谓原子操作,就是要么执行成功,要么执行失败,执行成功完成既定任务,执行失败还原执行前的状态。
常用原子操作命令:
4.4.1 $set
用来指定一个键并更新键值,若键不存在则创建。
{ $set : { field : value } }
4.4.2 $unset
用来删除一个键。
{ $unset : { field : 1} }
4.4.3 $inc
$inc 可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。
{ $inc : { field : value } }
4.4.4 $push
把 value 追加到 field 里面去,field 一定要是数组类型才行,如果 field 不存在,会新增一个数组类型加进去。
{ $push : { field : value } }
4.4.5 $pushAll
同 $push ,只是一次可以追加多个值到一个数组字段内。
{ $pushAll : { field : value_array } }
4.4.6 $pull
从数组 field 内删除一个等于 value 值。
{ $pull : { field : _value } }
4.4.7 $addToSet
增加一个值到数组内,而且只有当这个值不在数组内才增加。
4.4.8 $pop
删除数组的第一个或最后一个元素。
{ $pop : { field : 1 } }
4.4.9 $rename
修改字段名称:
{ $rename : { old_field_name : new_field_name } }
4.4.10 $bit
位操作,integer 类型
{$bit : { field : {and : 5}}}
查询分析
explain()
explain()
操作提供了查询信息,使用索引及查询统计等。有利于我们对索引的优化。接下来我们在 users 集合中创建 gender 和 user_name 的索引:
> db.users.ensureIndex({gender:1,user_name:1})
> db.users.find({gender:"M"},{user_name:1,_id:0}).explain()
hint()
虽然 MongoDB 查询优化器一般工作的很不错,但是也可以使用 hints()
来强迫 MongoDB 使用一个指定的索引。通过这种方法在某些情形下会提升性能。
范例:
指定使用 gender 和 user_name 索引字段来查询:
> db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})
可以使用 explain()
函数来分析以上查询:
> db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1}).explain()