Docs 菜单
Docs 主页
/
MongoDB Manual
/

通配符索引 (Wildcard Indexes)

在此页面上

  • 创建通配符索引
  • Considerations
  • 行为
  • 限制
  • 通配符索引查询/排序支持

MongoDB 支持在一个字段或一组字段上创建索引以支持查询。 由于 MongoDB 支持动态模式,因此应用程序可以查询事先无法知道名称或具有任意名称的字段。

MongoDB 版本中的新增功能:4.2

MongoDB 4.2 引入通配符索引,用于支持对未知或任意字段的查询。

考虑一个应用程序,它捕获 userMetadata字段下的用户定义数据并支持对该数据进行查询:

{ "userMetadata" : { "likes" : [ "dogs", "cats" ] } }
{ "userMetadata" : { "dislikes" : "pickles" } }
{ "userMetadata" : { "age" : 45 } }
{ "userMetadata" : "inactive" }

管理员希望创建索引以支持对userMetadata的任何子字段进行查询。

userMetadata上的通配符索引可以支持对userMetadatauserMetadata.likesuserMetadata.dislikesuserMetadata.age进行单字段查询:

db.userData.createIndex( { "userMetadata.$**" : 1 } )

该索引可以支持以下查询:

db.userData.find({ "userMetadata.likes" : "dogs" })
db.userData.find({ "userMetadata.dislikes" : "pickles" })
db.userData.find({ "userMetadata.age" : { $gt : 30 } })
db.userData.find({ "userMetadata" : "inactive" })

userMetadata上的非通配符索引只能支持对userMetadata值的查询。

重要

通配符索引并不是为了取代基于工作负载的索引规划而设计的。有关创建索引以支持查询的详细信息,请参阅创建索引以支持查询。有关通配符索引限制的完整文档,请参阅通配符索引限制。

重要

mongod featureCompatibilityVersion必须为4.2才能创建通配符索引。 有关设置 FCV 的说明,请参阅在 MongoDB 6.0 部署上设置特征兼容性版本。

您可以使用createIndexes数据库命令或其 Shell 助手createIndex()createIndexes()创建通配符索引。

要索引特定字段的值,请执行以下操作:

db.collection.createIndex( { "fieldA.$**" : 1 } )

使用此通配符索引,MongoDB 可为fieldA的所有值编制索引。 如果字段是嵌套文档或数组,则通配符索引将递归到文档/数组中,并将所有字段的值存储在文档/数组中。

product_catalog例如,product_attributes collection中的文档可能包含字段。product_attributes字段可以包含任意嵌套字段,包括嵌入式文档和数组:

{
"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_attributes 字段上创建一个通配符索引:

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

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

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

注意

特定于路径的通配符索引语法与wildcardProjection选项不兼容。 有关更多信息,请参阅wildcard索引的选项

有关示例,请参阅在单个字段路径(Field Path)上创建通配符索引。

要对文档中所有字段的值(不包括_id )进行索引,请将"$**"指定为索引键:

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

使用此通配符索引,MongoDB 可为collection中每个文档的所有字段建立索引。如果给定字段是嵌套文档或数组,则通配符索引将递归到文档/数组中,并将所有字段的值存储在文档/数组中。

有关示例,请参阅在所有字段路径(Field Path)上创建通配符索引。

注意

默认情况下,通配符索引会省略_id字段。要将_id字段包含在通配符索引中,您必须将其明确包含在wildcardProjection文档中。 有关更多信息,请参阅wildcard索引的选项

要对文档中多个特定字段的值进行索引,请执行以下操作:

db.collection.createIndex(
{ "$**" : 1 },
{ "wildcardProjection" :
{ "fieldA" : 1, "fieldB.fieldC" : 1 }
}
)

使用此通配符索引,MongoDB 可为collection中每个文档的指定字段的所有值建立索引。如果给定字段是嵌套文档或数组,则通配符索引将递归到文档/数组中,并将所有字段的值存储在文档/数组中。

注意

通配符索引不支持在wildcardProjection文档中混合包含和排除声明,除非显式包含_id字段。有关wildcardProjection的详细信息,请参阅wildcard索引的选项

有关示例,请参阅在通配符索引覆盖中包含特定字段。

要对文档中特定字段路径(Field Path)之外的所有字段进行索引:

db.collection.createIndex(
{ "$**" : 1 },
{ "wildcardProjection" :
{ "fieldA" : 0, "fieldB.fieldC" : 0 }
}
)

使用此通配符索引,MongoDB 可以对collection中每个文档的所有字段进行索引,但不包括指定的字段路径(Field Path)。如果给定字段是嵌套文档或数组,则通配符索引将递归到文档/数组中,并将所有字段的值存储在文档/数组中。

有关示例,请参阅从通配符索引覆盖中省略特定字段。

注意

通配符索引不支持在wildcardProjection文档中混合包含和排除声明,除非显式包含_id字段。有关wildcardProjection的详细信息,请参阅wildcard索引的选项

  • 通配符索引在任何给定查询谓词中最多支持一个字段。 有关通配符索引查询支持的详细信息,请参阅通配符索引查询/排序支持。

  • mongod featureCompatibilityVersion必须为4.2才能创建通配符索引。 有关设置 FCV 的说明,请参阅在 MongoDB 6.0 部署上设置特征兼容性版本。

  • 默认情况下,通配符索引会省略 _id 字段。要将_id字段包含在通配符索引中,您必须将其显式包含在wildcardProjection 文档中(即 { "_id" : 1 } )。

  • 您可以在一个集合中创建多个通配符索引。

  • 通配符索引可能涵盖与collection中其他索引相同的字段。

  • 通配符索引是稀疏索引,仅包含具有索引字段的文档的条目,即使索引字段包含空值也是如此。

对对象(即嵌入式文档)或数组的字段进行索引时,通配符索引具有特定行为:

  • 如果字段是对象,则通配符索引会下降到对象中并对其内容进行索引。通配符索引会继续下降到遇到的任何其他嵌入式文档。

  • 如果字段是数组,则通配符索引将遍历数组并对每个元素进行索引:

    • 如果数组中的元素是对象,则通配符索引会下降到对象以索引其内容,如上所述。

    • 如果元素是数组(即直接嵌入到父数组中的数组),则通配符索引不会遍历嵌入式数组,而是将整个数组作为单个值进行索引。

  • 对于所有其他字段,将基元(非对象/数组)值记录到索引中。

通配符索引继续遍历任何其他嵌套对象或数组,直到到达原始值(即不是对象或数组的字段)。 然后,它会为这个原始值以及该字段的完整路径编制索引。

例如,考虑以下文档:

{
"parentField" : {
"nestedField" : "nestedValue",
"nestedObject" : {
"deeplyNestedField" : "deeplyNestedValue"
},
"nestedArray" : [
"nestedArrayElementOne",
[ "nestedArrayElementTwo" ]
]
}
}

包含parentField的通配符索引记录以下条目:

  • "parentField.nestedField" : "nestedValue"

  • "parentField.nestedObject.deeplyNestedField" : "deeplyNestedValue"

  • "parentField.nestedArray" : "nestedArrayElementOne"

  • "parentField.nestedArray" : ["nestedArrayElementTwo"]

请注意, parentField.nestedArray的记录不包括每个元素的数组位置。 将元素记录到索引时,通配符索引会忽略数组元素的位置。 通配符索引仍可支持包含显式数组索引的查询。 有关更多信息,请参阅使用显式数组索引的查询

有关嵌套对象的通配符索引行为的更多信息,请参阅嵌套对象。

有关嵌套数组的通配符索引行为的更多信息,请参阅嵌套数组。

当通配符索引遇到嵌套对象时,它会深入到该对象并对其内容进行索引。 例如:

{
"parentField" : {
"nestedField" : "nestedValue",
"nestedArray" : ["nestedElement"]
"nestedObject" : {
"deeplyNestedField" : "deeplyNestedValue"
}
}
}

包含parentField的通配符索引深入到对象中,以遍历其内容并对其内容进行索引:

  • 对于本身就是对象(即嵌入式文档)的每个字段,深入到该对象以对其内容进行索引。

  • 对于每个字段(如果是数组),遍历该数组并对其内容进行索引。

  • 对于所有其他字段,将基元(非对象/数组)值记录到索引中。

通配符索引继续遍历任何其他嵌套对象或数组,直到到达原始值(即不是对象或数组的字段)。 然后,它会为这个原始值以及该字段的完整路径编制索引。

根据示例文档,通配符索引会将以下记录添加到索引中:

  • "parentField.nestedField" : "nestedValue"

  • "parentField.nestedObject.deeplyNestedField" : "deeplyNestedValue"

  • "parentField.nestedArray" : "nestedElement"

有关嵌套数组的通配符索引行为的更多信息,请参阅嵌套数组。

当通配符索引遇到嵌套数组时,它会尝试遍历该数组以对其元素进行索引。 如果数组本身是父数组中的一个元素(即嵌入式数组),则通配符索引会将整个数组记录为一个值,而不是遍历其内容。 例如:

{
"parentArray" : [
"arrayElementOne",
[ "embeddedArrayElement" ],
"nestedObject" : {
"nestedArray" : [
"nestedArrayElementOne",
"nestedArrayElementTwo"
]
}
]
}

包含parentArray的通配符索引下降到数组中以遍历其内容并对其内容进行索引:

  • 对于每个数组元素(即嵌入式数组),将整个数组索引为一个值。

  • 对于每个属于对象的元素,深入到对象中以遍历并索引其内容。

  • 对于所有其他字段,将基元(非对象/数组)值记录到索引中。

通配符索引继续遍历任何其他嵌套对象或数组,直到到达原始值(即不是对象或数组的字段)。 然后,它会为这个原始值以及该字段的完整路径编制索引。

根据示例文档,通配符索引会将以下记录添加到索引中:

  • "parentArray" : "arrayElementOne"

  • "parentArray" : ["embeddedArrayElement"]

  • "parentArray.nestedObject.nestedArray" : "nestedArrayElementOne"

  • "parentArray.nestedObject.nestedArray" : "nestedArrayElementTwo"

请注意, parentField.nestedArray的记录不包括每个元素的数组位置。 将元素记录到索引时,通配符索引会忽略数组元素的位置。 通配符索引仍可支持包含显式数组索引的查询。 有关更多信息,请参阅使用显式数组索引的查询

提示

另请参阅:

从 MongoDB 6.3、6.0.5 和 5.0.16 开始,wildcardProjection 字段以其提交的形式存储索引投影。早期版本的服务器可能已将投影以标准化形式存储。

服务器以相同的方式使用索引,但您可能会注意到listIndexesdb.collection.getIndexes()命令的输出有所不同。

重要

通配符索引不同于通配符文本索引,并且与通配符文本索引不兼容。通配符索引不支持使用 $text 操作符的查询。

有关通配符索引创建限制的完整文档,请参阅不兼容的索引类型或属性。

仅当 满足以下所有条件时,通配符索引才能支持 涵盖的查询

  • 查询规划器选择通配符索引来满足查询谓词。

  • 查询谓词可精确指定通配符索引所涵盖的一个字段。

  • 投影显式排除_id包含查询字段。

  • 指定的查询字段绝不是数组。

请考虑 employees 集合的以下通配符索引:

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

以下操作查询单个字段 lastName 并从结果文档中投影出所有其他字段:

db.employees.find(
{ "lastName" : "Doe" },
{ "_id" : 0, "lastName" : 1 }
)

假设指定的lastName绝不是数组,则 MongoDB 可以使用$**通配符索引来支持覆盖的查询。

通配符索引最多支持一个查询谓词字段。也就是说:

  • MongoDB 不能使用非通配符索引来满足查询谓词的一个部分,也不能使用通配符索引来满足另一部分。

  • MongoDB 不能使用一个通配符索引来满足查询谓词的一个部分,而使用另一个通配符索引来满足另一部分。

  • 即使单个通配符索引可以支持多个查询字段,MongoDB 也可以使用通配符索引仅支持其中一个查询字段。 所有剩余字段都在没有索引的情况下解析。

但是,MongoDB 可能会使用相同的通配符索引来满足查询$or或聚合$or操作符的每个独立参数。

仅当 以下所有条件为sort() true 时,MongoDB 才能使用通配符索引来满足 :

  • 查询规划器选择通配符索引来满足查询谓词。

  • sort()指定查询谓词字段。

  • 指定的字段绝不是数组。

如果不满足上述条件,MongoDB 则无法使用通配符索引进行排序。 MongoDB 不支持需要与查询谓词不同的索引的sort()操作。 有关详细信息,请参阅索引交集和排序。

请考虑 products 集合的以下通配符索引:

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

以下操作查询单个字段product_attributes.price并对该字段进行排序:

db.products.find(
{ "product_attributes.price" : { $gt : 10.00 } },
).sort(
{ "product_attributes.price" : 1 }
)

假设指定的price绝不是数组,则 MongoDB 可以使用product_attributes.$**通配符索引来同时满足find()sort()

通配符索引支持以下查询模式:

  • 检查字段是否存在的查询

  • 检查字段是否等于文档或数组的查询

  • 检查字段是否等于 null 的查询

有关详细信息,请参阅不支持的查询和聚合模式。

MongoDB 通配符索引在索引期间不会记录任何给定元素在数组中的数组位置。 但是,MongoDB 仍可能选择通配符索引来回答包含具有一个或多个显式数组索引(例如parentArray.0.nestedArray.0 )的字段路径(Field Path)的查询。由于为每个连续嵌套数组定义索引边界的复杂性越来越高,因此,如果查询中的给定字段路径(Field Path)包含超过8显式数组索引,则 MongoDB 不会考虑使用通配符索引来回答该路径。MongoDB 仍可考虑使用通配符索引来回答查询中的其他字段路径(Field Path)。

例如:

{
"parentObject" : {
"nestedArray" : [
"elementOne",
{
"deeplyNestedArray" : [ "elementTwo" ]
}
]
}
}

MongoDB 可以选择包含parentObject的通配符索引来满足以下查询:

  • "parentObject.nestedArray.0" : "elementOne"

  • "parentObject.nestedArray.1.deeplyNestedArray.0" : "elementTwo"

如果查询中的给定字段路径(Field Path)指定了超过 8 个显式数组索引,则 MongoDB 不会考虑使用通配符索引来回答该字段路径(Field Path)。MongoDB 会选择另一个符合条件的索引来回答查询,或者执行collection扫描。

请注意,通配符索引本身在索引文档时对其遍历文档的深度没有任何限制。该限制仅适用于显式指定精确数组索引的查询。 通过在没有显式数组索引的情况下发出相同的查询,MongoDB 可能会选择通配符索引来回答查询:

  • "parentObject.nestedArray" : "elementOne"

  • "parentObject.nestedArray.deeplyNestedArray" : "elementTwo"

提示

另请参阅:

后退

多键索引边界