MongoDB允许深入文档内部,对嵌套字段和数组建立索引;
嵌套对象和数组字段可以和复合索引中的顶级字段一起使用,多数情况下与“正常”索引字段的行为也是一致的。
考虑以下文档集合(user ):
db.user.insertMany(
[
{
"address": {
"province": "HeNan",
"city": "ZhengZhou",
"pincode": "123"
},
"tags": [
"music",
"cricket",
"blogs"
],
"name": "fly"
},
{
"address": {
"province": "HeBei",
"city": "HanDan",
"pincode": "234"
},
"tags": [
"music",
"basket",
"blogs"
],
"name": "chen"
},
{
"address": {
"province": "ChongQing",
"city": "ChongQing",
"pincode": "456"
},
"tags": [
"music",
"writing",
"running"
],
"name": "wang"
}
]
)
以上文档包含了 address 子文档和 tags 数组。
索引数组字段
- 假设我们基于标签来检索用户,为此我们需要对集合中的数组 tags 建立索引。
- 在数组中创建索引,需要对数组中的每个字段依次建立索引。所以在我们为数组 tags 创建索引时,会为 music、cricket、blogs三个值建立单独的索引。
- 使用以下命令创建数组索引:
db.user.ensureIndex({"tags":1})
- 创建索引后,我们可以这样检索集合的 tags 字段:
db.user.find({tags:"music"})
- 为了验证我们使用使用了索引,可以使用 explain 命令:
db.user.find({tags:"music"}).explain()
- 执行结果
/* 1 */
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "mongotest.user",
"indexFilterSet" : false,
"parsedQuery" : {
"tags" : {
"$eq" : "music"
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"tags" : 1.0
},
"indexName" : "tags_1",
"isMultiKey" : true,
"multiKeyPaths" : {
"tags" : [
"tags"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"tags" : [
"[\"music\", \"music\"]"
]
}
}
},
"rejectedPlans" : []
},
"serverInfo" : {
"host" : "kf-PC",
"port" : 27017,
"version" : "3.4.9",
"gitVersion" : "876ebee8c7dd0e2d992f36a848ff4dc50ee6603e"
},
"ok" : 1.0
}
- 以上命令执行结果中会显示 "stage":"FETCH",,则表示已经使用了索引。
stage的类型的意义
mongodb的文档中列出了前4种类型,还有一些没有列出来,但是会比较常见,这里一并解释一下。
COLLSCAN :全表扫描
IXSCAN:索引扫描
FETCH::根据索引去检索指定document
SHARD_MERGE:各个分片返回数据进行merge
SORT:表明在内存中进行了排序(与前期版本的scanAndOrder:true一致)
SORT_MERGE:表明在内存中进行了排序后再合并
LIMIT:使用limit限制返回数
SKIP:使用skip进行跳过
IDHACK:针对_id进行查询
SHARDING_FILTER:通过mongos对分片数据进行查询
COUNT:利用db.coll.count()之类进行count运算
COUNTSCAN:count不使用用Index进行count时的stage返回
COUNT_SCAN:count使用了Index进行count时的stage返回
SUBPLA:未使用到索引的$or查询的stage返回
TEXT:使用全文索引进行查询时候的stage返回
附:explain查询结果解析官方文档:
https://docs.mongodb.org/v3.0/reference/explain-results/
数组上的索引
(1)可以看得出在数组字段上建立索引的代价比较大,因为每次的删除,更新都会对每一个索引进行刷新,太消耗服务器的资源;
(2)可以针对数组字段中的某一个元素做具体的单独索引,减少索引的数量;
- 例如,在数组字段tags中的第1个元素中的music上建立索引:
db.user.ensureIndex({"tags.0.music":1})
- 同样,只有精确匹配tags.0.music查询,上述索引才会起到索引的作用。
多键索引
如果在数组字段上创建索引,那么这个索引称为多键索引( multikey)。
多键索引用explain函数中可以看到“isMultikey”字段的值为true,多键索引比非多键索引要慢一些;
索引子文档字段
- 假设我们需要通过city、state、pincode字段来检索文档,由于这些字段是子文档的字段,所以我们需要对子文档建立索引。
- 为子文档的三个字段创建索引,命令如下:
db.user.ensureIndex({"address.province":1,"address.city":1,"address.pincode":1})
利用这种方式可以建立任意深度的索引,例如可以在X.Y.Z.A.B.C上建立索引。
但是,针对子文档“address”上建立的索引,和建立在子文档的某个字段“address.provincey”上的索引是不同的:
(1)对整个子文档上建立的索引,只会提高整个子文档的的查询速度;
- 也就是说只有在完全匹配子文档的查询(包括字段顺序),子文档索引才会起作用;
(2)只有查询address.province字段,索引address.province才会起作用,
其他情况索引address.province不起作用;
- 一旦创建索引,我们可以使用子文档的字段来检索数据:
db.user.find({"address.province":"HeNan"})
- 记住查询表达式必须遵循指定的索引的顺序。所以上面创建的索引将支持以下查询:
db.user.find({"address.province":"HeNan","address.city":"ZhengZhou"})
- 同样支持以下查询:
db.user.find({"address.province":"HeNan","address.city":"ZhengZhou","address.pincode":"123"})
查询分析
/* 1 */
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "mongotest.user",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"address.city" : {
"$eq" : "ZhengZhou"
}
},
{
"address.pincode" : {
"$eq" : "123"
}
},
{
"address.province" : {
"$eq" : "HeNan"
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"address.province" : 1.0,
"address.city" : 1.0,
"address.pincode" : 1.0
},
"indexName" : "address.province_1_address.city_1_address.pincode_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"address.province" : [],
"address.city" : [],
"address.pincode" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"address.province" : [
"[\"HeNan\", \"HeNan\"]"
],
"address.city" : [
"[\"ZhengZhou\", \"ZhengZhou\"]"
],
"address.pincode" : [
"[\"123\", \"123\"]"
]
}
}
},
"rejectedPlans" : []
},
"serverInfo" : {
"host" : "kf-PC",
"port" : 27017,
"version" : "3.4.9",
"gitVersion" : "876ebee8c7dd0e2d992f36a848ff4dc50ee6603e"
},
"ok" : 1.0
}
参考来源: http://www.runoob.com/mongodb/mongodb-advanced-indexing.html
参考来源:http://281816327.blog.51cto.com/907015/1601473