Docs 菜单

Multikey Indexes

为了对保存数组值的字段进行索引,MongoDB 会为数组中的每个唯一元素创建一个索引键。 这些多键索引支持对数组字段进行高效查询。 可以在保存两个标量值[1]的数组上构造多键索引(例如 字符串、数字)嵌套文档。 如果数组包含同一值的多个实例,则索引仅包含这些值的一个条目。

针对“addr.zip”的多键索引图示字段。“addr”字段包含地址文档的数组。地址文档包含“zip”字段。
[1] 标量值是指既不是嵌入式文档也不是数组的值。

您可以对MongoDB Atlas中托管的部署使用多键索引。

要了解有关为 MongoDB Atlas 中托管的部署管理索引的更多信息,请参阅创建、查看、删除和隐藏索引

要创建多键索引,请使用 db.collection.createIndex()方法:

db.coll.createIndex( { <field>: < 1 or -1 > } )

如果任何索引字段是数组,MongoDB 会自动创建多键索引;无需显式指定多键类型。

注意

仅适用于 WiredTiger 和内存存储引擎

对于多键索引,MongoDB 会追踪哪个或哪些索引字段导致索引成为多键索引。跟踪此信息允许 MongoDB 查询引擎使用更严格的索引边界。

如果索引是多键索引,则索引边界的计算遵循特殊规则。 有关多键索引边界的详细信息,请参阅多键索引边界。

对于唯一索引,唯一约束适用于集合中的各个文档,而不是单个文档。

由于唯一性约束适用于单独的文档,因此对于唯一性多键索引,只要一个文档的索引键值不与另一个文档的索引键值重复,该文档就可能包含导致索引键值重复的数组元素。

有关详细信息,请参阅跨独立文档的唯一性约束。

对于复合多键索引,每个已建立索引的文档最多可以有一个值为数组的已建立索引的字段。也就是说:

  • 如果文档的多个待索引字段是数组,则无法创建复合多键索引。 例如,考虑包含以下文档的collection:

    { _id: 1, a: [ 1, 2 ], b: [ 1, 2 ], category: "AB - both arrays" }

    您无法在collection上创建复合多键索引{ a: 1, b: 1 } ,因为ab字段都是数组。

  • 或者,如果复合多键索引已存在,则无法插入会违反此限制的文档。

    考虑一个包含以下文档的collection:

    { _id: 1, a: [1, 2], b: 1, category: "A array" }
    { _id: 2, a: 1, b: [1, 2], category: "B array" }

    复合多键索引{ a: 1, b: 1 }是允许的,因为对于每个文档,只有一个由复合多键索引索引的字段是数组;即没有文档同时包含ab字段的数组值。

    但是,在创建复合多键索引后,如果尝试插入ab字段均为数组的文档,MongoDB 将使插入失败。

如果字段是文档数组,则可以对嵌入式字段进行索引以创建复合索引。 例如,考虑包含以下文档的collection:

{ _id: 1, a: [ { x: 5, z: [ 1, 2 ] }, { z: [ 1, 2 ] } ] }
{ _id: 2, a: [ { x: 5 }, { z: 4 } ] }

您可以在{ "a.x": 1, "a.z": 1 }上创建复合索引。 最多一个索引字段可以是数组的限制也适用。

有关示例,请参阅具有嵌入式文档的索引数组。

由于 MongoDB 4.4中数组字段的排序行为发生变化,当您对使用多键索引进行索引的数组进行排序时,查询计划将包括阻塞排序阶段,除非:

  • 所有排序字段的索引边界[MinKey, MaxKey]并且

  • 任何多键已索引字段的边界均不得与排序模式的路径前缀相同。

不能将多键索引指定为分片键索引。

但是,如果分片键索引是复合索引的前缀,则允许该复合索引成为复合多键索引,前提是其他键之一(即 不属于分片键的键)对数组进行索引。 复合多键索引可能会对性能产生影响。

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

多键索引可以覆盖满足以下条件的查询:

  • 查询不返回数组字段(这意味着该数组不包含在查询投影中)。这意味着要覆盖查询,多键索引必须是复合索引

  • 查询不包括 $elemMatch

  • 该查询符合所有其他涵盖的查询要求

例如,考虑包含以下文档的 matches 集合:

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

matches 集合在 eventname 字段上具有复合多键索引:

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

前面的索引为多键索引,因为 event 字段包含数组值。

该索引涵盖以下查询:

db.matches.find(
{ event: 'championship' },
{ _id: 0, name: 1 }
)
db.matches.find(
{ name: 'Bill', event: 'championship' },
{ _id: 0, name: 1 }
)

索引不包括以下查询,因为投影包含 event 数组字段:

db.matches.find(
{ event: 'championship' },
{ _id: 0, event: 1 }
)

当查询筛选器指定整个数组的精确匹配项时,MongoDB 可以使用多键索引来查找查询数组的第一个元素,但不能使用多键索引扫描来查找整个数组。 相反,在使用多键索引查找查询数组的第一个元素后,MongoDB 会检索关联的文档并筛选其数组与查询中的数组匹配的文档。

inventory例如,考虑包含以下文档的collection集合:

{ _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 ] }

collection在ratings字段上具有多键索引:

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

以下查询查找 ratings 字段为数组 [ 5, 9 ] 的文档:

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

MongoDB 可以使用多键索引来查找在 ratings 数组中的任意位置包含 5 的文档。然后,MongoDB 检索这些文档并过滤出 ratings 数组等于查询数组 [ 5, 9 ] 的文档。

$expr 不支持多键索引。

使用以下文档创建surveycollection:

db.survey.insertOne(
{ _id: 1, item: "ABC", ratings: [ 2, 5, 9 ] }
)

在字段ratings上创建索引:

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

由于ratings字段包含一个数组,因此ratings上的索引是多键索引。 多键索引包含以下三个索引键,每个索引键都指向同一文档:

  • 2,

  • 5

  • 9.

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

inventory请考虑包含以下形式文档的collection集合:

{
_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 }
]
}
...

以下操作在stock.sizestock.quantity字段上创建多键索引:

db.inventory.createIndex( { "stock.size": 1, "stock.quantity": 1 } )

复合多键索引可以支持使用包含索引字段的谓词以及仅包含索引前缀"stock.size"的谓词的查询,如以下示例所示:

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

有关 MongoDB 如何组合多键索引边界的详细信息,请参阅多键索引边界。 有关复合索引和前缀行为的更多信息,请参阅复合索引和前缀。

复合多键索引还可以支持排序操作,例如以下示例:

db.inventory.find( ).sort( { "stock.size": 1, "stock.quantity": 1 } )
db.inventory.find( { "stock.size": "M" } ).sort( { "stock.quantity": 1 } )

有关复合索引和排序操作行为的更多信息,请参阅使用索引对查询结果进行排序。