《MongoDB 权威指南(第2版)》阅读笔记
第 1 章 MongoDB 简介
MongoDB 是一款强大、灵活,且易于扩展的通用型数据库。它能扩展出非常多的功能,如二级索引(Secondary index)、范围查询(range query)、排序、聚合(aggregation),以及地理空间索引(geospatial inde)。
1.1 易于使用
MongoDB 是一个面向文档(document-oriented)的数据库,而不是关系型数据库。
与关系型数据库相比,面向文档的数据库不再有“行”(row)的概念,取而代之的是更为灵活的“文档”(document)模型。通过在文档中嵌入文档和数组,面向文档的方法能够仅使用一条记录来表现复杂的层次关系。
另外,不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小。根据需要添加或删除字段变得更容易了。通常,由于开发者能够进行快速迭代,所以开发进程得以加快。而且,试验更容易进行。开发者能尝试大量的数据模型,从中选择一个最好的。 ## 1.2 易于扩展 MongoDB 的设计采用横向扩展。面向文档的数据模型使它能很容易的在多台服务器之间进行数据分割。MongoDB 能自动处理跨集群的数据和负载,自动重新分配文档,以及将用户请求路由到正确的机器上。开发者只需要集中精力编写应用程序,而不需要考虑如何扩展的问题。如果一个集群需要更大的容量,只需要向集群添加新服务器,MongoDB 就会自动将现有数据向新服务传送。
1.3 丰富的功能
- 索引 MongoDB 支持通用二级索引,允许多种快速查询,且提供唯一索引、复合索引、地理空间索引,以及全文索引。
- 聚合 MongoDB 支持“聚合管道”(aggregation pipeline)。用户能通过简单地片段创建复杂的聚合,并通过数据库自动优化。
- 特殊的集合类型 MongoDB 支持存在时间有限的集合,适用于那些将在某个时刻过期的数据,如会话(session)。类似地,MongoDB 也支持固定大小的集合,用于保存近期数据,如日志。
- 文件存储(file storage) MongoDB 支持一种非常易用的协议,用于存储大文件和文件元数据。
第 2 章 MongoDB 基础知识
- 文档是 MongoDB 中数据库基本单元,非常类似于关系数据库管理系统中的行。
- 集合(collection)可以看作是一个拥有动态模式(dynamic schema)的表。
- MongoDB 的一个实例可以拥有多个相互独立的数据库(database),每一个数据库都有自己的集合。
- 每一个文档都有一个特殊的键“_id“,这个键在文档所属的集合中是惟一的。
- MongoDB 自带了一个简单但功能强大的 JavaScript shell,可用于管理 MongoDB 的实例或数据操作。
2.1 文档
文档是 MongoDB 的核心概念。文档就是键值对的一个有序集。文档中的值可以是多种不同的数据类型(甚至可以是一个完整的内嵌文档)。
文档的键是字符串,除了少数例外情况,键可以使用任意 UTF-8 字符。 - 键不能含有 \0 (空字符)。这个字符用于表示键的结尾。 - . 和 $ 具有特殊意义,只能在特定环境下使用。通常这两个字符是被保留的。
MongoDB 不但区分类型,还区分大小写。
MongoDB 的文档不能有重复的键。
2.2 集合
集合就是一组文档。
2.2.1 动态模式
集合是动态模式的。在一个集合里面的文档可以是各式各样的。例如,下面两个文档可以存储在同一个集合里面:
1
2{"greeting":"hello,world!"}
{"foo":5}
既然没有必要区分不同类型文档的模式,为什么还要使用多个集合呢?这里有几个重要的原因:
- 如果把各种各样的文档不加区分地放在同一个集合里,无论对开发者还是管理员来说都是噩梦。开发者要么确保每次查询只返回固定类型的文档,要么让执行查询的应用程序来处理所有不同类型的文档。
- 在一个集合里查询特定类型的文档在速度上也不划算,分开查询多个集合要快得多。
- 把同种类型的文档放在一个集合里,数据会更加集中。
- 创建索引时,需要使用文档的附加结构(特别是创建唯一索引时)。索引时按照集合来定义的。在一个集合中只放入一种类型的文档,可以更有效地对集合进行索引。
2.2.2 命名
集合名可以是满足下列条件的任意 UTF-8 字符串。 - 集合名不能是空字符串(“”)。 - 集合名不能包含 \0 字符(空字符),这个字符表示集合名的结束。 - 集合名不能以 ”system.“ 开头,这是为系统集合保留的前缀。如,system.user 这个集合保存着数据库的用户信息。 - 用户创建的集合不能在集合名中包含保留字符 ‘$’,。因为某些系统生成的集合中包含 $ ,很多驱动程序确实支持在集合名里包含该字符。除非你要访问这种系统创建的集合,否则不应该在集合名中包含 $ 。
** 子集合 ** 组织集合的一种惯例是使用 “.” 分隔不同命名空间的子集合。例如,一个具有博客功能的应用可能包含两个集合,分别是 blog.posts 和 blog.authors。这是为了使组织结构更清晰。
2.3 数据库
在 MongoDB 中,多个文档组成集合,而多个集合组成可以组成数据库。一个 MongoDB 实例可以承载多个数据库,每个数据库拥有 0 个或者多个集合。每个数据库都有独立的权限,在磁盘上,不同的数据库也放置在不同的文件中。一般将一个应用程序的所有数据都存储在同一个数据库中。要存储多个应用程序,就需要使用不同的数据库。
数据库可以是满足以下条件的任意 UTF-8 字符串。 - 不能是空字符串 (““). - 不能含有 /、、.、”、*、<、>、:、|、?、$、\0. 基本上,只能使用 ASCII 中的字母和数字。 - 数据库名区分大小写。简单起见,数据库名应全部小写。 - 数据库名最多为 64 字节。
保留的数据库名: - admin - local - config
把数据库名添加到集合名前,得到集合的完全限定名,即命名空间(namdspace)。命名空间的长度不得超过 121 字节,且在实际使用中应小于 100 字节。
2.4 启动 MongoDB
MongoDB 在没有参数的情况下会使用默认目录 /data/db . 如果参数目录不存在或不可写,服务器会启动失败。因此,在启动前创建数据目录非常重要。
MongoDB 默认监听 27017 端口。如果端口被占用,则启动失败。
2.5 MongoDB shell 简介
2.5.1 运行 shell
MongoDB shell是一个功能完备的 JavaScript 解释器,可运行任意 JavaScript 程序。如进行简单的数学运算、利用 JavaScript 的标准库还可以定义和调用 JavaScript 函数。
2.5.2 MongoDB 客户端
查看 db 当前指向哪个数据库 1
db
1
use test
2.5.3 shell 中的基本操作
创建、读取、更新和删除
1.创建 1
2
3post = {"title":"My Blog Post",
"content":"Here's my blog post.",
"date":new Date()}1
db.blog.insert(post)
1
db.blog.find()
2.读取 find 和 findOne
方法可以用于查询集合里的文档。若只想查看一个文档,可用 findOne:
1
db.blog.findOne()
3.更新 update 接受(至少)两个参数:第一个是限定条件(用于匹配待更新的文档),第二个是新的文档。假设我们要为先前写的文章增加评论功能,就需要增加一个新的键,用于保存评论数组。
首先,修改变量 post,增加 “comments” 键: 1
post.comments = []
然后执行 update 操作: 1
db.blog.update({title:"My Blog Post"},post)
4.删除 使用 remove
方法可将文档从数据库中永久删除。它可以接受一个作为限定条件的文档作为参数。
1
db.blog.remove({title:"My Blog Post"})
2.6 数据类型
null null 用于表示空值或者不存在的字段:
1
{"x":null}
布尔型 有两个值 true 或 false:
1
{"x":"false"}
数值 shell 默认使用 64 位浮点型数值。
或:1
{"x":3.14}
1
{"x":3}
对于整型值,可使用 NuberInt 类(4字节带符号整数)或 NumberLong
类(8字符带符号整数): 1
2{"x":NumberInt("3")}
{"x":NumberLong("3")}
字符串 UTF-8 字符串都可表示为字符串类型的数据:
1
{"x":"foobar"}
日期 日期被存储为自新纪元以来经过的毫秒数,不存储时区:
1
{"x":new Date()}
正则表达式 查询时,使用正则表达式作为限定条件,语法也与 JavaScript 的正则表达式语法相同:
1
{"x":/foobar/i}
数组 数据列表或数据集可以表示为数组;
1
{"x":["a","b","c"]}
内嵌文档 文档可嵌套其他文档,被嵌套的文档作为父文档的值:
1
{"x":{"foo":"bar"}}
对象 id 对象 id 是一个 12 字节的 ID,是文档的唯一标识
1
{"x":ObjectId()}
二进制数据 二进制数据时一个任意字节的字符串。它不能直接在 shell 中使用。如果要将非 UTF-8 字符保存到数据库中,二进制数据是唯一的方式。
代码 查询和文档中可以包括任意 JavaScript 代码:
1
{"x":function(){/*...*/}}
2.6.2 日期
在 JavaScript 中, Date 类可以用作 MongoDB 的日期类型。创建日期对象时,应使用 new Date(…).
2.6.5 _id 和 ObjectId
MongoDB 中存储的文档必须包含一个 “_id” 键。这个键可以是任何类型的,默认是 ObjectId 对象。在一个集合里面,每个文档都有唯一的 “_id”, 确保集合里面每个文档都能被唯一标识。
1.ObjectId ObjectId 是 “_id” 的默认类型。它被设计成轻量型的,不同的机器都能用全局唯一的同种方法方便地生成它。
ObjectId 使用 12 字节的存储空间,是一个由 24 个十六进制数字组成的字符串(每个字节可以存储两个十六进制数组)。
ObjectId 的前 4 个字节是从标准纪元开始的时间戳,单位为秒。接下来的 3 字节是所在主机的唯一标示符。通常是机器主机名的散列值(hash)。这样就可以确保不同主机生成不同的 ObjectId 而不产生冲突。接下来的两字节来自产生 ObjectId 的进程的进程标识符(PID),这样可以确保在同一台机器上并发的多个进程产生的 ObjectId 是惟一的。
前 9 字节保证了同一秒钟不同机器不同进程产生的 ObjectId 是惟一的。最后 3 字节是一个自动增加的计数器,确保相同进程同一秒产生的 ObjectId 也是不一样的。一秒钟最多允许每个进程拥有 2563(16 777 216) 个不同的 ObjectId。
2.7 使用 MongoDB Shell
连接 MongoDB 实例: 1
mongo some-host:27017/myDB
1
mongo --nodb
1
2conn = new Mongo("localhost:27017")
db = conn.getDB("test")
2.7.1 shell 小贴士
由于 mongo 是一个简化的 JavaScript shell,可以通过查看 JavaScript
的在线文档得到大量帮助。可以使用 help 命令查看 MongoDB 特有的功能。
1
2
3help
db.help() //查看数据库级别的帮助
db.foo.help() //查看集合级别的帮助1
db.foo.update
2.7.2 使用 shell 执行脚本
1 | mongo script1.js script2.js script3.js |
mongo shell 会依次执行传入的脚本,然后退出。
1 | mongo --quiet server-1:27017/foo script1.js script2.js script3.js |
这样可以将 db 指向 server-1:27017 上的 foo 数据库,然后执行这三个脚本。运行 shell 时指定的命令行选项要出现在地址之前。
可以使用 load() 函数,从交互式 shell 中运行脚本: 1
load("script1.js")
1
2
3
4
5
6
7
8
9
10
11
12var connectTo = function(port,dbname){
if(!port){
port = 27017;
}
if(!dbname){
dbname = "test"
}
db = connect("localhost:" + port + "/" + dbname);
return db
};1
2load('defineConnectTo.js')
typeof connectTo1
run("ls","-l","/")
2.7.3 创建 .mongorc.js 文件
如果某些脚本被频繁加载,可以将它们添加到 .mongorc.js
文件中,这个文件在启动 shell
时自动运行。将该文件放在当前用户的主目录下,如 /root 目录下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 定制欢迎内容
var compliment = ["attractive","intelligent","like Batman"];
var index = Math.floor(Math.random()*3);
print("Hello, you're looking particularly " + compliment[index] + " today!");
var no = function(){
print("Not on my watch.");
};
// 禁止删除数据库
db.dropDatabase = DB.prototype.dropDatabase = no;
// 禁止删除集合
DBCollection.prototype.drop = no;
// 禁止删除索引
DBCollection.prototype.dropIndex = no;
// 定制 shell 提示
prompt = function(){
if(typeof db == 'undefined'){
return '(nodb)>';
}
try{
db.runCommand({getLastError:1});
}
catch(e){
print(e);
}
return db+">";
};1
2
3EDITOR="/usr/bin/vim"
var wap = db.col.findOne({x:1})
edit wap1
2
3db.inventory.insertOne(
{ item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
)1
2
3
4
5
6db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
{ item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
{ item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])
db.inventory.find()
3.2 删除文档
删除集合中的所有元素,但不会删除集合本身。 1
db.foo.remove({})
1
db.mailing.list.remove({"opt-out":true})
删除集合中的所有元素,同时删除集合本身。 1
db.foo.drop()
1
2
3
4
5
6{
"_id":ObjectId("5cb6e441040c0c1ce40033a7"),
"name":"joe",
"friends":32,
"enimes":2
}1
2
3
4
5
6
7var joe=db.users.findOne({"name":"joe"})
joe.relationships={"friendd":joe.friends,"enemies":joe.enemies}
joe.username=joe.name
delete joe.friends
delete joe.enemies
delete joe.name
db.users.update({"name":"joe"},joe)
例如,用户资料存储在下面的文档里: 1
2
3
4
5
6
7{
"_id": ObjectId("5cb6ee4a040c0c1ce40033a9"),
"name": "joe",
"age": 30,
"sex": "male",
"location": "Wisconsin"
}1
db.users.update({"_id":ObjectId("5cb6ee4a040c0c1ce40033a9")},{"$set":{"favorite book":"War and Peace"}})
1
db.users.update({"_id":ObjectId("5cb6ee4a040c0c1ce40033a9")},{"$set":{"favorite book":"Green Eggs and Ham"}})
1
db.users.update({"_id":ObjectId("5cb6ee4a040c0c1ce40033a9")},{"$set":{"favorite book":["Green Eggs and Ham","Cat's Cradle","Foundation Trilogy"]}})
1
db.users.update({"_id":ObjectId("5cb6ee4a040c0c1ce40033a9")},{"$unset":{"favorite book":1}})
2.增加或减少 “$inc"
修改器用来增加已有键的值,没有该键则直接创建一个。例如,更新集合
inventory 中 item 为 canvas 的文档,使其 qty 值增加,可以这么写:
1
2db.inventory.findOne()
db.inventory.update({"item":"canvas"},{"$inc":{"qty":2}})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20> db.blog.posts.findOne()
{
"_id": ObjectId("5cb81579040c0c1ce40033aa"),
"title": "A blog post",
"content": "bla bla bla"
}
> db.blog.posts.update({"title":"A blog post"}, {"$push":{"comments":{"name":"joe","email":"joe@example.com","content":"nice post."}}})
> db.blog.posts.findOne()
{
"_id": ObjectId("5cb81579040c0c1ce40033aa"),
"title": "A blog post",
"content": "bla bla bla",
"comments": [
{
"name": "joe",
"email": "joe@example.com",
"content": "nice post."
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19> db.blog.posts.update({"title":"A blog post"}, {"$push":{"comments":{"name":"bob","email":"bob@example.com","content":"good post."}}})
> db.blog.posts.findOne()
{
"_id": ObjectId("5cb81579040c0c1ce40033aa"),
"title": "A blog post",
"content": "bla bla bla",
"comments": [
{
"name": "joe",
"email": "joe@example.com",
"content": "nice post."
},
{
"name": "bob",
"email": "bob@example.com",
"content": "good post."
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24> db.blog.posts.update({"title":"A blog post"}, {"$push":{"tickets":{"$each":[10,20,15]}}})
> db.blog.posts.findOne()
{
"_id": ObjectId("5cb81579040c0c1ce40033aa"),
"title": "A blog post",
"content": "bla bla bla",
"comments": [
{
"name": "joe",
"email": "joe@example.com",
"content": "nice post."
},
{
"name": "bob",
"email": "bob@example.com",
"content": "good post."
}
],
"tickets": [
10,
20,
15
]
}1
> db.blog.posts.update({"title":"A blog post"}, {"$push":{"top10":{"$each":["Nightmare on Elm Street","Saw"],$slice:-10}}})
如果数组的元素数量小于 10,那么所有元素都会保留。如果数组的元素数量大于 10,那么只要最后 10 个元素会保留。
类似的用法如 $ne 和 $addToSet 不一一举例。用到的时候再查。
删除元素 {$pop:{"key:":1} 从数组末尾删除一个元素, {$pop:{“key:”:-1} 则从头部删除。 删除待办列表中的 laundry:
1
db.lists.update({},{$pull:{"todo:":"laundry"}})
基于位置的数组修改器 有两种方法操作数组中的值:通过位置或者定位操作符 “$" 。 数组下标都是从 0 开头的,可以将下标直接作为键来选择元素。 定位操作符 "$” 标识数组中要更新的元素,而不显式指定元素在数组中的位置。
例如,将 _id 为 1 的学生的成绩 80 改为
82,则可以这么做,而无需知道数组的下标: 1
2
3
4
5
6
7
8
9> db.students.insert([
{ "_id" : 1, "grades" : [ 85, 80, 80 ] },
{ "_id" : 2, "grades" : [ 88, 90, 92 ] },
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }
])
> db.students.updateOne(
{ _id: 1, grades: 80 },
{ $set: { "grades.$" : 82 } }
)
以上更新操作符的具体用法请参考 mongoDB 官方文档 Update Operators 。
3.3.3 update 函数参数
update 函数格式如下: 1
2
3
4
5
6
7
8
9
10
11db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
)
3.3.5 返回被更新的文档
db.collection.findAndModify()
方法可以修改并返回单个文档。其格式为: 1
2
3
4
5
6
7
8
9
10
11
12
13db.collection.findAndModify({
query:<document>,
sort:<document>,
remove:<boolean>,
update:<document>,
new:<boolean>,
fields:<document>,
upsert:<boolean>,
bypassDocumentValidation :<boolean>,
writeConcern:<document>,
collation:<document>,
arrayFilters:[<filterdocument1>,...]
});
第 4 章 查询
4.1 find 简介
find 方法格式如下: 1
db.collection.find(query, projection)
Parameter | Type | Description |
---|---|---|
query | document | 可选的。指定使用选择过滤查询操作。要返回集合中的所有文档,请省略此参数或传递空文档({})。 |
projection | document | 可选的。指定要在与查询过滤器匹配的文档中返回的字段。要返回匹配文档中的所有字段,请省略此参数。 |
例如:想要查找 “age” 值为 27 的所有文档,可以这样写: 1
db.user.find({"age":27})
1
db.user.find({"age":27,"username":"joe"})
例如,如果只对用户集合的 “username” 和 “email”
键感兴趣,可以使用如下查询返回这些键: 1
db.users.find({},{"username":1,"email":1})
##4.2 查询条件 ### 4.2.1 查询条件 比较操作符 “$lt"、"$lte”、“$gt"
、 "$gte” 和 “$ne" 分别对应
<、<=、> 、 >= 和 != 。例如,查询 18~30
岁(含)的用户,可以向下面这样:
1
db.users.find({"age":{"$gte":18,"$lte":30}})
1
2> start = new Date("01/01/2007")
> db.users.find({"registered":{"$lt":start}})1
db.raffle.find({"ticket_no":{"$in":[725,542,390]}})
4.3.1 null
null
不仅可以匹配自身,而且还会匹配不包含这个键的文档。。如果既要检查该键的值是否为
null,还要通过 “$exists” 条件判定键值已存在: 1
db.c.find({"z":{"$in":[null],"$exists":true}})
MongoDB 可以为前缀型正则表达式(比如 /^joey/)查询创建索引,所以这种类型的查询会非常高效。
4.3.3 查询数组
1.$all 使用 $all 通过多个元素来匹配数组。例如,有以下数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// 1
{
"_id": 1,
"fruit": [
"apple",
"banana",
"peach"
]
}
// 2
{
"_id": 2,
"fruit": [
"apple",
"kumquat",
"orange"
]
}
// 3
{
"_id": 3,
"fruit": [
"cherry",
"banana",
"apple"
]
}1
db.food.find({fruit:{$all:["apple","banana"]}})
1
db.food.find({"fruit.2":"peach"})
2.$size 可以使用 $size 查询特定长度的数组。 1
db.food.find({"fruit":{"$size":3}})
2.$size 可以使用 $size 查询特定长度的数组。例如: 1
db.food.find({"fruit":{"$size":3})
3.slice操作符slice
操作符可以返回某个键匹配的数组元素的一个子集。例如,返回博客文章的文档的前
10 条评论: 1
db.blog.posts.findOne(criteria,{"comments":{$slice:10}})
1
db.blog.posts.findOne(criteria,{"comments":{$slice:[23,10]}})
返回一个匹配的数组元素: 1
db.blog.posts.find({"comments.name":"bob",{"comments.$":1}})
第 5 章 索引
5.1 索引简介
5.1.5 索引基数
基数(cardinality)就是集合中某个字段拥有不同值的数量。通常,一个字段的基数越高,这个键上的索引就越有用。一般来说,应该在基数比较高的键上建立索引,或者至少应该把基数较高的键放在复合索引的前面(低基数的键之前)。
5.4 索引类型
5.4.1 唯一索引
唯一索引可以确保集合的每一个文档的指定键都有唯一值。唯一索引会把 null
看做值,所以无法将多个缺少唯一索引中的键的文档插入到集合中。例如,如果想保证不同文档的“username”键拥有不同的值,创建一个唯一索引就好了:
1
db.users.ensureIndex({"username":1},{"unique":true})
2.去除重复
创建索引时使用“dropDups”选项,如果遇到重复的值,第一个会被保留,之后的重复文档都会被删除。
1
db.people.ensureIndex({"username":1},{"unique":true,"dropDups":true})
5.4.2 稀疏索引
创建稀疏索引时,只对键有值的情况进行唯一性约束。 1
db.ensureIndex({"email":1},{"unique":true,"sparse":true})
5.5.1 标识索引
创建索引时指定索引的名称: 1
db.foo.ensureIndex({"a":1,"b":1,"c":1,...,"z":1},{"name":"alphabet"})
5.5.2 删除索引
使用 dropIndex 命令删除不再需要的索引: 1
db.people.dropIndex("x_1_y_1")
第 6 章 特殊的索引和集合
6.1 固定集合
固定集合需要事先创建好,且大小是固定的。固定集合类似于循环队列,当固定集合被沾满时,如果再插入新文档,固定集合会自动将最老的文档从集合中删除。
固定集合不能被分片。可以用于记录日志。
6.1.1 创建固定集合
size 参数可以指定集合的固定大小,以字节为单位: 1
db.createCollection("my_collection",{"capped":true,"size":10000})
可以使用 convertToCapped 命令将常规集合转换为固定集合:
1
db.runCommand({"convertToCapped":"test","size":10000})
6.1.2 自然排序
对固定集合可以进行 自然排序(natural
sort)。自然排序返回结果集中文档的顺序就是文档在磁盘上的顺序。
1
db.my_collection.find().sort({"$natural":1})
6.1.3 循环游标
循环游标(tailable cursor):当循环游标的结果集被取光后,游标不会被关闭。如果超过 10 分钟没有新的结果,循环游标就会被释放。
6.2 TTL 索引
TTL 索引是特殊的单字段索引,MongoDB
可以使用它在一定时间后或在特定时钟时间自动从集合中删除文档。数据到期对于某些类型的信息非常有用,例如机器生成的事件数据,日志和会话信息,这些信息只需要在数据库中持续有限的时间。
1
db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )
6.4 地理空间索引
MongoDB 支持 2dsphere 索引(用于地球表面类型的地图)和 2d 索引(用于平面地图和时间连续的数据)。
2dsphere 允许使用 GeoJSON 格式指定点、线和多边形。 1
2
3
4
5
6
7{
"name":"New York City",
"loc":{
"type":"Point",
"coordinates":[50,2]
}
}1
2
3
4
5
6
7{
"name":"Hudson River",
"loc":{
"type":"Line",
"coordinates":[[0,1],[0,2],[1,2]]
}
}1
2
3
4
5
6
7{
"name":"New England",
"loc":{
"type":"Polygon",
"coordinates":[[0,1],[0,2],[1,2]]
}
}1
db.world.ensureIndex({"loc":"2dsphere"})
1
2
3
4
5
6
7
8
9
10
11
12
13
14db.places.find(
{
loc: {
$geoIntersects: {
$geometry: {
type: "Polygon" ,
coordinates: [
[ [ 0, 0 ], [ 3, 6 ], [ 6, 1 ], [ 0, 0 ] ]
]
}
}
}
}
)1
db.hyrule.ensureIndex({title:"2d"})
6.5 使用 GridFS 存储文件
6.5.1 GridFS 入门
在下面这个会话中,首先用 mongofiles 从文件系统中上传一个文件到 GridFS
,然后列出 GridFS 中的所有文件,最后再将之前上传过的文件从 GridFS
中下载下来: 1
2
3
4echo "Hello,world" > foo.txt
mongofiles put foo.txt
mongofiles list
mongofiles get foo.txt
第 7 章 聚合
7.1 聚合框架
例如,有一个保存着杂志文章的集合,假设每篇文章被保存为 MongoDB
中的一个文档,则可以构建如下查询: 1.将 “author” 从每个文档中投射出来
1
{"$project":{"author":1}}
1
{$group:{"_id":"$author","count":{"$sum":1}}}
1
{"$sort":{"count":-1}}
1
{"$limit":5}
1
2
3
4db.aticles.aggregate({"$project":{"author":1}},
{$group:{"_id":"$author","count":{"$sum":1}}},
{"$sort":{"count":-1}},
{"$limit":5})