1.什么是索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
假设这里有一个 commits 集合,我们想要查询其中的数据!
db.commits.find({"eId":"5913cd0ee727e70007a109f2"}).explain("executionStats")
从执行计划来看,我们从18284条数据中查询得到了80条查询结果集,而我们文档中的记录数就是18284条记录,说明这条查询语句是全表搜索,显然这是非常浪费效率的。
2.创建单索引
为了提高查询效率,我们可以使用创建索引的方式,mongodb使用createIndex方法来创建索引语法:
db.collection_name.createIndex(keys[,options])
这时我们给commits集合的eId这个字段创建索引
db.commits.createIndex({eId:1})
索引创建成功之后,这时再查询commits集合
db.commits.find({"eId":"5913cd0ee727e70007a109f2"}).explain("executionStats")
我们查询得到的数据是80条,而我们一共只在80条数据里查询,从执行记划来看,查询的记录数,查询的时间明显都缩小。
从上图,如果我们想给sinfo下面的name加索引,可以这样写
db.commits.createIndex({"sinfo.name":1})
我们不在要sinfo上面加索引,如果这样做了,我们必须查询整个子方档。
比如说:创建唯一索引,方法就是指定unique键为true,如:
db.users.createIndex({name:1},{“unique”:true})
3. 复合索引
当我们的查询条件不只有一个时,就需要建立复合索引。复合索引是两个或更多字段的索引,并且它可以支持基于这些字段的查询。当我们查询时有多个过滤条件时,为了提高查询效率,可以在这多个条件上添加索引,语法
db.collection_name.createIndex(key1:1,key2:1)
当我们查询条件为两个时,如:
db.commits.find({"eId":"5913cd0ee727e70007a109f2","sId":"5913c700434a8b00077256fe"}).explain("executionStats")
因前面已经给eId创建了索引,所以本次查询范围从18248条记录,降到了80条记录,而sId没有创建索引,所有我们从80条记录里得到了符合eId,sId这两个条件的38条记录,那么我们现在给sId也创建索引
db.commits.createIndex({sId:1})
当查询的两个条件sId,eId都创建了索引后,再执行
db.commits.find({"eId":"5913cd0ee727e70007a109f2","sId":"5913c700434a8b00077256fe"}).explain("executionStats")
4. 多键索引
如果文档中含有array类型字段,可以直接对其名称建立索引,这样MongoDB就会为内嵌数组中的每个元素建立一个独立的索引
注意:多键索引不等于在多列字段上创建索引(复合索引),多键索引与单键索引创建形式相同,差别在于字段的类型.
语法:
db.COLLECTION_NAME.createIndex({key:< 1 or -1 >})
如:
有一个paper集合,数据表结构如下,其中structures字段是数组类型。
例如:这时我们给papers集合的structures这个字段创建索引
db.papers.createIndex({structures:1})
因为这个structures字段是数组,所有这个索引称之为多键索引。
5.复合多键索引
对于一个复合多键索引,每个索引最多可以包含一个数组。在多于一个数组的情形下来创建复合多键索引不被支持。
假定存在如下集合
{ _id: 1, a: [ 1, 2 ], b: [ 1, 2 ], category: "AB - both arrays" }
你可能会这样创建索引db.COLLECTION_NAME.createIndex({a:1,b:1}),但是这样是不允许的,因为a和b都是数组。
如果{a:1,b:1}的索引已经创建了,则a和b当中必定有一个是非array,此时插入一个a和b都是array的文档就会失败。
假定存在如下集合
{ _id: 1, a: [1, 2], b: 1, category: "A array" } { _id: 2, a: 1, b: [1, 2], category: "B array" }
则可以基于每一个文档创建一个基于{ a: 1, b: 1 }的复合多键索引,原因是每一个索引的索引字段只有一个数组
类似的,如下的内嵌文档也可以建立索引。例如:
db.test.createIndex({“stock.size”:1,”stock.quantity”:1})
6.部分索引
MongoDB部分索引只为那些在一个集合中,满足指定的筛选条件的文档创建索引。由于部分索引是一个集合文档的一个子集,因此部分索引具有较低的存储需求,并降低了索引创建和维护的性能成本。
部分索引通过指定过滤条件来创建,可以为MongoDB支持的所有索引类型使用部分索引。
简单点说:部分索引就是带有过滤条件的索引,即索引只存在与某些文档之上
创建部分索引语法:
db.collection.createIndex(keys, options)
options可以使用partialFilterExpression,即部分过滤表达式,其类型为文档类型
过滤表达式通常包括:$exists, $gt, $gte, $lt, $lte,$type,$and
过滤表达式使用示例:
db.persons.createIndex({name:1},{partialFilterExpression:{age: {$gt:25}}})
此句的意思是:基于age列创建大于25岁的部分索引。
创建的部分索引过滤条件是age大于25,
当查询的条件是country等于china,age大于25。 条件满足,从执行计划里可以看出此次查询采用索引扫描。
当查询的条件是country等于china,age大于等于25。 条件不满足,从执行计划里可以看出此次查询采用的是全表扫描方式。
创建部分唯一索引的一些限制:
部分索引只为集合中那些满足指定的筛选条件的文档创建索引。 如果你指定的partialfilterexpression和唯一约束、那么唯一性约束只适用于满足筛选条件的文档。 具有唯一约束的部分索引不会阻止不符合唯一约束且不符合过滤条件的文档的插入。
示例文档
db.users.insertMany([ { "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 }, { "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 }, { "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }])
为集合添加索引
db.users.createIndex({ username: 1 }, { partialFilterExpression: { age: { $gte: 21 } } })
在集合users上插入用户名相同的文档,收到了重复键的错误提示
db.users.insert( { username: "david", age: 27 } ) WriteResult({ "nInserted" : 0, "writeError" : { "code" : 11000, "errmsg" : "E11000 duplicate key error collection: test.users index: username_1 dup key: { : "david" }"} })
下面插入年龄小于部分索引值或者age键为空的同用户名文档,可以成功插入。
也就是说对于不在部分索引限制之类的其他键值重复是允许的。
db.users.insert( { username: "david", age: 20 } ) WriteResult({ "nInserted" : 1 }) db.users.insert( { username: "amanda" } ) WriteResult({ "nInserted" : 1 }) db.users.insert( { username: "rajiv", age: null } ) WriteResult({ "nInserted" : 1 })
7.文本索引
MongoDB提供文本索引以支持对字符串内容的文本搜索查询。text索引可以包括其值为字符串或字符串元素数组的任何字段。
文本索引,顾名思义就是用于搜索文本的,可以用于搜索所有的value,也可以搜索指定的field对应的value。只要field对应value是string,或者对应的value是array且array中的元素是string,那么文本索引都可以索引该field
注意:一个集合最多只能有一个文本索引。
要创建text索引,请使用该 db.collection.createIndex()方法。要索引包含字符串或字符串元素数组的字段,请包含该字段并"text"在索引文档中指定字符串文字。
如以下示例所示:
db.collection.createIndex({keys:”text”})
也可以创建多个字段text,例如
db.collection.createIndex({subject:”text”,comments:”text”})
举例,有两条记录
{_id:5908df789dfd1fd5884fd84f7df4,statement:MongoDB is the worst} {_id:5908dfgfh587hgf15f4hf54hf418,statement:MongoDB is the best}
给statement字段创建文本索引:
db.collection.createIndex({statement:”text”})
查询
db.collection.find({$text:{$search:”MongoDB best”}})
查询出来的结果集是:
{_id:5908df789dfd1fd5884fd84f7df4,statement:MongoDB is the worst} {_id:5908dfgfh587hgf15f4hf54hf418,statement:MongoDB is the best}
这是因为文本查询时,每个单词之间的分隔是”或者”,所以上面的查询语句的意思是:查询包含MongoDB或者best的记录数。
在多个字段上创建文本索引时,还可以使用通配符说明符($**)。使用通配符文本索引,MongoDB会为包含集合中每个文档的字符串数据的每个字段编制索引。
例如:
db.collection.createIndex({“$**”,”text”})
此索引允许在具有字符串内容的所有字段上进行文本搜索。如果不清楚要包含在文本索引中的哪些字段或用于临时查询
通配符文本索引可以是复合索引的一部分。例如,以下内容在字段nane和通配符说明符上创建复合索引,例如:
db.collection.createIndex({name:1,“$**”,”text”})
排序操作无法从text索引获取排序顺序,即使是复合文本索引也是如此 ; 即排序操作不能使用文本索引中的排序。
8.唯一索引
只要指定了某个field是唯一的,那么在同一个集合中就不允许存在相同的field值,MongoDB默认创建的唯一索引就是_id。
唯一索引一般是这样创建的:
db.members.createIndex( { "user_id": 1 }, { unique: true } )
单索引创建唯一索引,如:
db.persons.createIndex({name:1},{unique:true})
复合键创建唯一索引,如:
db.persons.createIndex({name:1,email:1},{unique:true})
9.查看索引
getIndexes()方法可以用来查看集合的所有索引,
getIndexKeys()方法查看索引键。
totalIndexSize()查看集合索引的总大小,
getIndexSpecs()方法查看集合各索引的详细信息db
MongoDB默认会为插入的文档生成_id字段(如果应用本身没有指定该字段),_id是文档唯一的标识,为了保证能根据文档id快递查询文档,MongoDB默认会为集合创建_id字段的索引。
10.删除索引
不再需要的索引,我们可以将其删除,mongodb提供两种删除索引的方法:
dropIndex()方法用于删除指定的索引
dropIndexes()方法用于删除全部的索引