Mongodb数组字段索引之多键索引

学习mongodb,体会mongodb的每一个使用细节,欢迎阅读威赞的文章。这是威赞发布的第92篇mongodb技术文章,欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题,欢迎在文章下面点个赞,或者关注威赞。谢谢。

Mongodb字段允许包含字符,文档,数组等各种各样的类型。同样Mongodb索引也可以支持字符,文档,数组等类型。本文结合Mongodb官方文档,介绍Mongodb数组类型数据的索引——多键索引。如果应用经常查询数组字段,为该字段添加多键索引,能够提高查询效率,增加索引查询覆盖率,优化数据库查询性能。

如在学生集合当中,包含了存储学生测验成绩的test_scores字段,这个学期的每一次测验成绩都会在这个数组当中。老师需要查询出至少5次测验成绩超过90分的学生 。这样就可以在字段test_scores上添加索引来提高查询效率。因为test_scores是数组类型,Mongodb自动为数组类型创建多键索引。

概述

多键索引,包含并排序了字段中的数组数据。多键索引,能够改善数组字段的查询性能。用户不需要显示的定义多键索引类型。当Mongodb构建索引时,看到该字段是数组字段,就会自动的创建多键索引。Mongodb可以为普通类型数据数组(如字符串数组,数字数组)和嵌入式文档数据来构建多键索引。如果一个数组包含相同值的多个元素,则Mongodb只会选择这些元素中的一个来放入索引当中。

下面的图中描述了多键索引的结构。有一个collection集合,字段addr是文档类型的数组。现在为addr数组中的zip字段建立索引。在索引当中,数组元素的数值从小到大排列。

语法

使用下面的语句来创建多键索引

db.<collection>.createIndex({<arrayField>: <sortOrder>})

使用和限制

索引边界

在查询中,边界定义了索引扫描的各个部分。Mongodb在多键索引边界计算上有特殊的规则,详细查看文档《Mongodb多键索引边界》。

唯一多键索引

在唯一多键索引当中,文档的数组元素,只能包含集合中其他文档数组中不存在的元素。

复合多键索引

在复合多键索引中,每一个文档最多只能包含一个被索引的数组字段。

用户不可以为多个数组创建索引。如在集合中包含了下面一个文档数据

{_id: 1, scores_spring:[8, 6], scores_fall:[5,9]}

其中字段scores_spring和字段scores_fall是数组索引,用户不能够使用{scores_spring: 1, scores_fall:1}来创建索引。

如果一个复合索引已经存在,用户也不能够插入还是索引定义相违背的文档数据。

如集合中包含文档

{_id: 1, scores_spring:[8, 6], scores_fall:9}
{_id: 2, scores_spring:6, scores_fall:[5, 7]}

用户可以创建一个复合多键索引{scores_spring: 1, scores_fall:1},因为每一个文档当中,只有一个字段是数组索引,不包含这两个字段同时是数组字段的文档数据。该索引创建后,Mongodb不允许用户插入两个字段都是数据元素的文档。

排序

基于数组字段的索引进行排序时,满足下面两个条件,才会使用索引排序,而不会在查询中包含一个内存排序。

  • 所有排序字段的值包含在索引边界最大最小值内
  • 任何一个与排序模版带有相同前缀的多键索引都不能有边界限制。这句话在文档中很绕口。尝试去理解一下。如前面提到的索引{scores_spring: 1, scores_fall:1}。当某个查询排序,使用{sort:{scores_spring:1}},这该排序字段是索引{scores_spring: 1, scores_fall:1}的索引前缀。在查询当中,不能对scores_spring做边界限制,否者将使用内存排序。

分片集合

多键索引的字段,不能作为分片键。但是,当分片键是复合索引的前缀时,后续索引字段包含数组时,这个复合索引就会成为一个符合多键索引。

如下文档, 集合中带有索引{field2:1, field1: 1},当使用field2字段作为分片集的关键字时,则field2既是分片关键字,也是复合索引的前缀。外国人写的这些英语,还是很绕的,要理解一下。

{_id:1, field1: [2,8],field2: 'A'}

哈希索引

哈希索引,不能是多键索引

索引覆盖查询

多键索引不能覆盖数组字段的查询。但是,多键索引,能够使用索引前缀,覆盖非数组字段的查询。如下面的一个使用案例。

在集合matches中插入文档。

db.matches.insertMany([
    { name: "joe", event: ["open", "tournament"]},
    { name: "bill", event: ["match", "championship"]}
])

在name字段和event字段建立索引

db.matches.createIndex({
    name: 1,
    event: 1
})

该索引是复合多键索引,但是能够覆盖在name字段的查询

db.matches.find({
    name: "joe"
}).explain()

使用数组作为查询条件

当查询过滤器中,使用整个数组做为查询条件时,Mongodb能够使用多键索引,查询数组过滤条件中的第一个数组元素,但不能使用多键索引去查询整个数组。当Mongodb使用多键索引查询过滤数组中的第一个元素以后所查询出来的文档,Mongodb在内存中会对这部分文档进一步过滤,过滤出复合查询条件中整个数组的文档。

举例说明一下这个过程。创建集合inventory并插入数据

db.inventory.insertMany([
    { _id:5, type: "food", item:"apple", ratings: [ 5, 8, 9 ] },
    { _id:6, type: "food", item:"banana", ratings: [ 5, 9 ] },
    { _id:7, type: "food", item:"grapes", ratings: [ 9, 5, 8 ] },
    { _id:8, type: "food", item:"orange", ratings: [ 5, 9, 5 ] },
    { _id:9, type: "food", item:"pear", ratings: [ 9, 5 ] }
])

在数组ratings建立多键索引

db.inventory.createIndex({ratings: 1})

构建一个使用数组作为过滤器的查询语句

db.inventory.find({
    ratings: [5, 9]
})

在查询计划中,能够看出,mongodb先使用5通过多键索引,查询出所有包含元素5的文档,然后在内存中过滤出包含整个数组[5,9]的文档数据

$expr

$expr表达式,不支持多键索引

应用

为数值数组添加索引

创建students集合,并插入数据。其中 test_scores是数值类型的数组。

db.students.insertMany([
    {name: 'Andre Robinson', test_scores: [88, 97]},
    {name: 'Alice Martin', test_scores: [62, 73]},
    {name: 'Bob Smith', test_scores: [92, 89]}
])

用户经常需要查询出至少有一次测验分数大于90的同学,这可以向数组字段添加索引来提高性能

db.students.createIndex({test_scores: 1})

因为字段test_scores是数组类型,所以Mongodb自动为该字段创建了多键索引。该索引中包含了字段test_scores的所有值,并按照从小到大排列,[62, 73, 88, 89, 92, 97].该索引支持在字段test_scores上的查询

db.students.find({
    test_scores: { $elemMatch: { $gte: 90 } }
})

为文档数组添加索引

构建inventory集合并插入数据

db.inventory.insertMany([
    {
        item: "t-shirt",
        stock: [
            { size: "S", quantity: 8 },
            { size: "L", quantity: 10 }
        ]
    },
    {
        item: "sweater",
        stock: [
            { size: "S", quantity: 4 },
            { size: "M", quantity: 7 }
        ]
    },
    {
        item: "vest",
        stock: [
            { size: "S", quantity: 6 },
            { size: "L", quantity: 1 }
        ]
    }
])

用户需要在库存低于5的时候,下订单来补货。为了查找出哪些需要补货,需要构建语句,查出来stock数组中,数量quantity少于5的记录。为了提高性能,用户需要在字段stock.quantity上添加索引。

db.inventory.createIndex({'stock.quantity': 1})

因为stock是包含文档的数组,索引Mongodb将这个索引存储为多键索引。该索引将字段stock.quantity所有值按照从小到大排列[1,4,6,7,8,10]

构建语句,查询出少于5的数据

db.inventory.find({'stock.quantity': { $lt: 5 }})

查询数据,按照库存的倒序排列

db.inventory.find().sort({'stock.quantity': -1})

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

威赞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值