MongoDB 索引
索引通常能够极大的提高查询效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录
这种扫描全集合的查询效率是非常低的,特别是处理大量的数据时,查询可能要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
createIndex() 方法
MongoDB 使用 createIndex()
方法来创建索引。
注意在 3.0.0 版本前创建索引的方法为
db.collection.ensureIndex()
,之后的版本使用db.collection.createIndex()
方法,ensureIndex() 还能用,但只是 createIndex() 的别名。
语法
createIndex() 方法的基本语法格式如下:
1 | db.collection.createIndex(keys, options) |
语法中的 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。
实例
1 | db.col.createIndex({"title": 1}) |
createIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)
1 | db.col.createIndex({"title": 1, "description": -1}) |
createIndex() 接收可选参数,可选参数列表如下所示:
Parameter | Type | Description |
---|---|---|
background | Boolean | 建索引过程会阻塞其他数据库操作,background 可指定以后台方式创建索引,即增加 ”background“ 可选参数,”background“ 默认值为 false |
unique | Boolean | 建立的索引是否唯一。指定 true 创建唯一索引。默认值为 false |
name | string | 索引的名称。如果未指定,MongoDB 会通过连接索引的字段名和排序生成一个索引的名称 |
dropDups | Boolean | 3.0+ 版本已废弃 。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引,默认值为 false |
sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为 true 的话,在索引字段中不会查询出不包含对应字段的文档。默认值为 false |
expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL 设定,设定集合的生存时间 |
v | index version | 索引的版本号。默认的索引版本取决于 mongod 创建索引时运行的版本。 |
weights | document | 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重 |
default_language | string | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。默认为英语 |
language_override | string | 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的 language,默认值为 language |
实例
在后台创建索引
1 | db.values.createIndex({open: 1, close: 1}, {background: true}) |
通过在创建索引时加 background:true 的选项,让创建工作在后台执行
MongoDB 聚合
MongoDB 中聚合(aggregate) 主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似 sql 语句中的 count(*)
aggregate() 方法
MongoDB 中聚合的方法使用 aggregate()
语法
aggregate() 方法的基本语法如下所示:
1 | db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION) |
实例
集合中的数据如下:
1 | > db.col.find().pretty() |
现在我们通过以上集合计算每个作者所写的文章数,使用 aggregate() 计算结果如下:
1 | > db.col.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}]) |
以上实例类似 sql 语句:
1 | select by_user, count(*) from col group by by_user |
上面的例子中,我们通过 by_user 字段对数据进行分组,并计算 by_user 字段相同值的总和。
下表展示了一些聚合的表达式
表达式 | 描述 | 实例 |
---|---|---|
$sum | 计算总和 | db.col.aggregate([{$group: {_id: “$by_user”, num_tutorial:{$sum: “$likes”}}}]) |
$avg | 计算平均值 | db.col.aggregate([{$group: {_id: “$by_user”, num_tutorial: {$avg: “$likes”}}}]) |
$min | 获取集合中所有文档对应值的最小值 | db.col.aggregate([{$group: {_id: “$by_user”, num_tutorial: {$min: “$likes”}}}]) |
$max | 获取集合中所有文档对应值的最大值 | db.col.aggregate([{$group: {_id: “$by_user”, num_tutorial: {$max: “$likes”}}}]) |
$push | 在结果文档中插入值到一个数组中 | db.col.aggregate([{group: {_id:”$by_user”, url:{$push: “$url”}}}]) |
$addToSet | 在结果文档中插入值到一个数组中,但不创建副本 | db.col.aggregate([{group: {_id:”$by_user”, url:{$addToSet: “$url”}}}]) |
$first | 根据资源文档的排序获取第一个文档数据 | db.col.aggregate([{group: {_id:”$by_user”, first_url:{$first: “$url”}}}]) |
$last | 根据资源文档的排序获取最后一个文档数据 | db.col.aggregate([{group: {_id:”$by_user”, last_url:{$last: “$url”}}}]) |
管道的概念
管道在 Unix 和 Linux 中一般用于将当前命令的输出结果作为下一个命令的参数。
MongoDB 的聚合管道将 MongoDB 文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其他的文档。
这里我们介绍一下聚合框架中常用的几个操作:
- $project: 修改输入文档的结构。可以用来重命名,增加或删除域,也可以用来创建计算结果以及嵌套文档。
- $match: 用于过滤数据,只输出符合条件的文档。$match 使用 MongoDB 的标准查询操作。
- $limit: 用来限制 MongoDB 聚合管道返回的文档树
- $skip: 在聚合管道中跳过指定数量的文档,并返回剩余的文档;
- $unwind: 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值;
- $group: 将集合中的文档分组,可用于统计结果;
- $sort: 将输入文档排序后输出;
- $geoNear: 输出接近某一地理位置的排序文档。
实例
- $project 实例
1
2
3
4
5
6db.article.aggregate(
{ $project : {
title : 1 ,
author : 1 ,
}}
);
这样的话结果中就只还有 _id, title 和 author 三个字段了,默认情况下 _id 字段是被包含的,如果不想包含 _id,可以这样:
1 | db.article.aggregate( |
$match 实例
1
2
3
4db.articles.aggregate([
{$match: {score: {$gt: 70, $lte: 90 }}},
{$group: {_id: null, count: {$sum: 1}}}
]);$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。
$skip 实例
1
db.article.aggregate({$skip: 5});
经过$skip管道操作符处理后,前五个文档被”过滤”掉。