MongoDB索引类型的介绍及使用

1,135 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情

一、索引类型

1-1、单键索引(Single Field Indexes)

在某一个特定的字段上建立索引 mongoDB在ID上建立了唯一的单键索引,所以经常会使用id来进行查询; 在索引字段上进行精确匹配、排序以及范围查找都会使用此索引

image.png

db.books.createIndex({title:1})

image.png

对内嵌文档字段创建索引:

image.png

1-2、复合索引(Compound Index)

复合索引是多个字段组合而成的索引,其性质和单字段索引类似。但不同的是,复合索引中字段的顺序、字段的升降序对查询性能有直接的影响,因此在设计复合索引时则需要考虑不同的查询场景。

image.png

db.books.createIndex({type:1,favCount:1})

image.png

1-3、多键索引(Multikey Index)

在数组的属性上建立索引。针对这个数组的任意值的查询都会定位到这个文档,既多个索引入口或者键值引用同一个文档

image.png 准备inventory集合:

db.inventory.insertMany([
    { _id: 5, type: "food", item: "aaa", ratings: [ 5, 8, 9 ] },
    { _id: 6, type: "food", item: "bbb", ratings: [ 5, 9 ] },
    { _id: 7, type: "food", item: "ccc", ratings: [ 9, 5, 8 ] },
    { _id: 8, type: "food", item: "ddd", ratings: [ 9, 5 ] },
    { _id: 9, type: "food", item: "eee", ratings: [ 5, 9, 5 ] }
])

创建多键索引

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

image.png

通过索引进行查询:

image.png

多键索引很容易与复合索引产生混淆,复合索引是多个字段的组合,而多键索引则仅仅是在一个字段上出现了多键(multi key)。而实质上,多键索引也可以出现在复合字段上

# 创建复合多值索引
db.inventory.createIndex( { item:1,ratings: 1 } )

注意: MongoDB并不支持一个复合索引中同时出现多个数组字段

嵌入文档的索引数组

db.inventory.insertMany([
    {
        _id: 1,
        item: "abc",
        stock: [
            { size: "S", color: "red", quantity: 25 },
            { size: "S", color: "blue", quantity: 10 },
            { size: "M", color: "blue", quantity: 50 }
        ]
    },
    {
        _id: 2,
        item: "def",
        stock: [
            { size: "S", color: "blue", quantity: 20 },
            { size: "M", color: "blue", quantity: 5 },
            { size: "M", color: "black", quantity: 10 },
            { size: "L", color: "red", quantity: 2 }
        ]
    },
    {
        _id: 3,
        item: "ijk",
        stock: [
            { size: "M", color: "blue", quantity: 15 },
            { size: "L", color: "blue", quantity: 100 },
            { size: "L", color: "red", quantity: 25 }
        ]
    }
])

在包含嵌套对象的数组字段上创建多键索引

db.inventory.createIndex( { "stock.size": 1, "stock.quantity": 1 } ) 
db.inventory.find({"stock.size":"S","stock.quantity":{$gt:20}})

image.png

1-4、地理空间索引(Geospatial Index)

在移动互联网时代,基于地理位置的检索(LBS)功能几乎是所有应用系统的标配。MongoDB为地理空间检索提供了非常方便的功能。地理空间索引(2dsphereindex)就是专门用于实现位置检索的一种特殊索引。

案例:MongoDB如何实现“查询附近商家"?

假设商家的数据模型如下

db.restaurant.insert({
    restaurantId: 0,
    restaurantName:"兰州牛肉面",
    location : {
        type: "Point",
        coordinates: [ -73.97, 40.77 ]
    }
})    

创建一个2dsphere索引

db.restaurant.createIndex({location : "2dsphere"})

查询附近10000米商家信息

db.restaurant.find( {
    location:{
        $near :{
            $geometry :{
                type : "Point" ,
                coordinates : [  -73.88, 40.78 ]
            } ,
            $maxDistance:10000
        }
    }
} )   
  • $near查询操作符,用于实现附近商家的检索,返回数据结果会按距离排序。
  • $geometry操作符用于指定一个GeoJSON格式的地理空间对象,type=Point表示地理坐标点,coordinates则是用户当前所在的经纬度位置;$maxDistance限定了最大距离,单位是米。

1-5、全文索引(Text Indexes)

MongoDB支持全文检索功能,可通过建立文本索引来实现简易的分词检索。

db.reviews.createIndex( { comments: "text" } )

$text操作符可以在有text index的集合上执行文本检索。$text将会使用空格和标点符号作为分隔符对检索字符串进行分词, 并且对检索字符串中所有的分词结果进行一个逻辑上的 OR 操作。

全文索引能解决快速文本查找的需求,比如有一个博客文章集合,需要根据博客的内容来快速查找,则可以针对博客内容建立文本索引。

1-5-1、案例

数据准备

db.stores.insert(
    [
        { _id: 1, name: "Java Hut", description: "Coffee and cakes" },
        { _id: 2, name: "Burger Buns", description: "Gourmet hamburgers" },
        { _id: 3, name: "Coffee Shop", description: "Just coffee" },
        { _id: 4, name: "Clothes Clothes Clothes", description: "Discount clothing" },
        { _id: 5, name: "Java Shopping", description: "Indonesian goods" }
    ]
)

创建name和description的全文索引

db.stores.createIndex({name: "text", description: "text"})

测试

通过$text操作符来查寻数据中所有包含“coffee”,”shop”,“java”列表中任何词语的商店

db.stores.find({$text: {$search: "java coffee shop"}})

MongoDB的文本索引功能存在诸多限制,而官方并未提供中文分词的功能,这使得该功能的应用场景十分受限。

image.png

1-6、Hash索引(Hashed Indexes)

不同于传统的B-Tree索引,哈希索引使用hash函数来创建索引。在索引字段上进行精确匹配,但不支持范围查询,不支持多键hash; Hash索引上的入口是均匀分布的,在分片集合中非常有用;

db.users.createIndex({username : 'hashed'})

1-7、通配符索引(Wildcard Indexes)

MongoDB的文档模式是动态变化的,而通配符索引可以建立在一些不可预知的字段上,以此实现查询的加速。MongoDB 4.2 引入了通配符索引来支持对未知或任意字段的查询。

1-7-1、案例

准备商品数据,不同商品属性不一样

db.products.insert([    {        "product_name" : "Spy Coat",        "product_attributes" : {            "material" : [ "Tweed", "Wool", "Leather" ],
            "size" : {
                "length" : 72,
                "units" : "inches"
            }
        }
    },
    {
        "product_name" : "Spy Pen",
        "product_attributes" : {
            "colors" : [ "Blue", "Black" ],
            "secret_feature" : {
                "name" : "laser",
                "power" : "1000",
                "units" : "watts",
            }
        }
    },
    {
    "product_name" : "Spy Book"
    }
])    

创建通配符索引

db.products.createIndex( { "product_attributes.$**" : 1 } )

测试

通配符索引可以支持任意单字段查询 product_attributes或其嵌入字段:

db.products.find( { "product_attributes.size.length" : { $gt : 60 } } )
db.products.find( { "product_attributes.material" : "Leather" } )
db.products.find( { "product_attributes.secret_feature.name" : "laser" } )  

1-7-2、注意事项

  • 通配符索引不兼容的索引类型或属性

image.png

image.png

  • 通配符索引是稀疏的,不索引空字段。因此,通配符索引不能支持查询字段不存在的文档。
# 通配符索引不能支持以下查询
db.products.find( {"product_attributes" : { $exists : false } } )
db.products.aggregate([
    { $match : { "product_attributes" : { $exists : false } } }
])
  • 通配符索引为文档或数组的内容生成条目,而不是文档/数组本身。因此通配符索引不能支持精确的文档/数组相等匹配。通配符索引可以支持查询字段等于空文档{}的情况。
#通配符索引不能支持以下查询:
db.products.find({ "product_attributes.colors" : [ "Blue", "Black" ] } )

db.products.aggregate([{    $match : { "product_attributes.colors" : [ "Blue", "Black" ] }
}])