首页>软件资讯>常见问题

常见问题

MongoDB 功能详解之通配符索引(Wildcard Indexes)

发布时间:2023-04-03 15:08:16人气:324

通配符索引(Wildcard Indexes):MongoDB 4.2 版本中的新功能。


为了更高效地查询,MongoDB 支持在单字段或多字段上创建索引。由于 MongoDB 支持动态模式,应用程序可以查询那些无法事先知道名称的字段。

MongoDB 4.2 引入了通配符索引,以支撑针对未知或任意字段的查询。

设计一个应用程序,它在 userMetadata 字段下捕获用户定义的数据,并支持对这些数据进行查询:

{ "userMetadata" : { "likes" : [ "dogs", "cats" ] } }

{ "userMetadata" : { "dislikes" : "pickles" } }

{ "userMetadata" : { "age" : 45 } }

{ "userMetadata" : "inactive" }

管理员(Administrators)希望创建索引来支持对 userMetadata 的任何子字段的查询。

UserMetadata 上的通配符索引可以支持 userMetadata,userMetadata.likes,userMetadata.dislikes 和 userMetadata.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 的非通配符索引(non-wildcard index)只能支持对 userMetadata 值的查询。

⚠️ 重要:

通配符索引(Wildcard indexes)不是用来替代传统索引的。有关创建索引的详细信息,请参阅创建支持查询的索引(Create Indexes to Support Your Queries)。有关通配符索引限制的完整文档,请参见通配符索引限制(Wildcard Index Restrictions)。

创建通配符索引

⚠️ 重要:

mongod 的功能兼容性版本(featureCompatibilityVersion)必须是 v4.2 才支撑创建通配符索引。有关设置 fCV 的说明,请参照如何在 MongoDB 6.0 部署中设置特性兼容版本(Set Feature Compatibility Version on MongoDB 6.0 Deployments.)


创建通配符索引,您可以使用 createIndex 数据库命令或它的 mongosh 方法: createIndex() 或  createIndexes()。

在字段上创建通配符索引


为指定字段的值创建索引:


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

使用这个通配符索引,MongoDB 对 fieldA 的所有值进行索引。如果字段是嵌套的文档或数组,则通配符索引递归到文档/数组中,并存储文档/数组中所有字段的值。

例如,product_catalog 集合中的文档可能包含 product_attributes 字段。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" } )

📒 注意:

特殊路径的通配符索引语法与通配符用法不兼容时,参照 Options for wildcard  indexes 获取更多的帮助。

有关示例,参见文档内容:在单个字段路径上创建通配符索引(Create a Wildcard Index on a Single Field Path.)

在所有字段上创建通配符索引

以  "$**" 作为索引的关键字,对文档中所有字段( _id 除外)的值进行索引:

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

使用此通配符索引,MongoDB 可以对集合中每个文档的所有字段进行索引。

如果给定的字段是嵌套的文档或数组,则通配符索引递归到文档/数组中,并存储文档/数组中所有字段的值。

有关示例,参见文档内容:在所有字段路径上创建通配符索引(Create a Wildcard Index on All Field Paths)。

📒 注意:

默认情况下,通配符索引忽略 _ id字段。要在通配符索引中包含 _ id 字段,必须在通配符投影文档中显式地包含它。更多有关信息,参见通配符索引(wildcard indexes)的选项(Options for wildcard indexes)。


在多个特定字段上创建通配符索引

对文档中指定多个字段的值创建索引:

db.collection.createIndex(

  { "$**" : 1 },

  { "wildcardProjection" :

    { "fieldA" : 1, "fieldB.fieldC" : 1 }

  }

)

使用此通配符索引,MongoDB 对集合中每个文档的指定字段的所有值进行索引。

如果给定的字段是嵌套的文档或数组,则通配符索引递归到文档/数组中,并存储文档/数组中所有字段的值。

📒 注意:

通配符索引不支持在通配符投影文档中混合包含和写排除语句,想对 _ id 字段创建索引,只能显式包含。有关通配符投影(wildcardProjection)的更多信息,参见通配符索引的选项(Options for wildcard indexes)。


有关示例,参见在通配符索引中覆盖指定字段(Include Specific Fields in Wildcard Index Coverage)。

创建排除多个特定字段的通配符索引

对文档中不包括特定字段路径的所有字段创建索引:

db.collection.createIndex(

  { "$**" : 1 },

  { "wildcardProjection" :

    { "fieldA" : 0, "fieldB.fieldC" : 0 }

  }

)

如此使用通配符索引,MongoDB 将对集合中的每个文档的所有字段(不包括指定的字段路径)进行索引。

如果给定的字段是嵌套的文档或数组,则通配符索引递归到文档/数组中,并存储文档/数组中所有字段的值。

请参照:从通配符索引覆盖中省略特定字段(Omit Specific Fields from Wildcard Index Coverage)。

📒 注意:

通配符索引不支持在通配符投影(wildcardProjection)文档中混合包含和排除语句,除非显式包含 _ id 字段。有关通配符投影(wildcardProjection)的更多信息,参见通配符索引的选项(Options for wildcard indexes)。


注意事项


通配符索引在任何指定的查询条件中最多只能支持一个字段。有关通配符索引查询的更多信息,参见 Wildcard Index Query/Sort Support。


mongd 的功能兼容版本(featureCompatibilityVersion)必须是4.2才能创建通配符索引。有关设置 fCV 的说明,参见 Set Feature Compatibility Version on MongoDB 6.0 Deployments。

默认情况下,通配符索引省略 _id 字段。要在通配符索引中包含 _id 字段,必须在通配符投影文档 wildcardProjection(即{“ _ id”: 1})中显式包含它。


可以在集合(collection)中创建多个通配符索引。


通配符索引可以覆盖与集合(collection)中的其他索引相同的字段。


通配符索引是稀疏索引(Sparse Indexes),只包含具有索引字段的文档的条目,允许索引字段包含空值。


特性


通配符索引在索引对象(即嵌入式文档 embedded document)或数组字段时具有递归遍历的特性:


如果字段是一个对象(object),则通配符索引将递归到该对象的最后一层嵌套,并对其内容进行索引。

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


如果数组中的某个元素是一个对象,则通配符索引将深入到该对象中,以按照上面所述对其内容进行索引。

如果元素是一个数组——即直接嵌入在父数组中的数组——那么通配符索引不会遍历嵌入的数组,而是将整个数组作为单个值进行索引。

对于所有其他字段,将基础原语 primitive(非对象/数组)的值记录到索引中。

通配符索引持续遍历任何其他嵌套对象或数组,直到达到一个基本值(即不是对象或数组的字段)。然后对这个基本值以及该字段的完整路径进行索引。


例如:


{

  "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 的记录是无序的。通配符索引在将元素写到索引中时忽略数组元素的位置。通配符索引仍然可以支持含有显式数组索引的查询。参照 Queries with Explicit Array Indices 获取详情。


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


有关嵌套数组的通配符索引行为的详细信息,请参阅:Nested Arrays。


嵌套对象(Nested Objects)


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


{

  "parentField" : {

    "nestedField" : "nestedValue",

    "nestedArray" : ["nestedElement"]

    "nestedObject" : {

      "deeplyNestedField" : "deeplyNestedValue"

    }

  }

}

一个通配符索引,其中包含 parentField,它深入到对象中以遍历和索引其内容:

字段本身是一个对象(例如一个嵌入式文档) ,下降到该对象以索引其内容。

作为数组的字段,遍历数组并索引其内容。

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


通配符索引继续遍历任何其他嵌套对象或数组,直到达到一个基本值(即不是对象或数组的字段)。然后对该基元值以及该字段的完整路径进行索引。


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


"parentField.nestedField" : "nestedValue"


"parentField.nestedObject.deeplyNestedField" : "deeplyNestedValue"


"parentField.nestedArray" : "nestedElement"

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


嵌套数组(Nested Arrays)


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


{

  "parentArray" : [

    "arrayElementOne",

    [ "embeddedArrayElement" ],

    "nestedObject" : {

      "nestedArray" : [

        "nestedArrayElementOne",

        "nestedArrayElementTwo"

      ]

    }

  ]

}

一个通配符索引,其中包含 parentField ,它深入到对象中以遍历和索引其内容:

字段本身是一个对象(例如一个嵌入式文档) ,下降到该对象以索引其内容。


作为数组的字段,遍历数组并索引其内容。

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


通配符索引继续遍历任何其他嵌套对象或数组,直到达到一个基本值(即不是对象或数组的字段)。然后对该基元值以及该字段的完整路径进行索引。

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

"parentArray" : "arrayElementOne"


"parentArray" : ["embeddedArrayElement"]


"parentArray.nestedObject.nestedArray" : "nestedArrayElementOne"


"parentArray.nestedObject.nestedArray" : "nestedArrayElementTwo"

注意,parentField.nestedArray 的记录不包括每个元素的数组位置。通配符索引在将元素记录到索引中时忽略数组元素的位置。通配符索引仍然可以支持包含显式数组索引的查询。请参阅:Queries with Explicit Array Indices


📒 TIP:

另见:BSON 文档的嵌套深度 Nested Depth for BSON Documents


限制条件


不能使用通配符索引分片集合。在要分片的一个或多个字段上创建非通配符索引。有关选择碎片键的详细信息,请参阅碎片键(Shard Keys)。


不能创建复合索引。

不能为通配符索引指定下列属性:


TTL

Unique

不能使用通配符语法创建下列索引类型:


2d(地理空间)


2dsphere(地理空间)


Hashed


⚠️ 重要:


通配符索引与通配符文本索引(Wildcard Text Indexes)不同且不兼容。通配符索引不支持使用 $Text 运算符的查询。


有关通配符索引创建限制的完整文档,参见不兼容索引类型或属性(Incompatible Index Types or Properties)。


通配符索引查询/排序支持


覆盖查询


通配符索引支持覆盖查询(covered query),必须满足以下所有条件:

满足查询条件的查询计划,可以利用通配符索引。


查询条件正好命中通配符索引所覆盖的一个字段。


排除了 _ id 字段,只包括查询字段。


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

例如 employees 集合上的以下通配符索引:

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

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

db.employees.find(

  { "lastName" : "Doe" },

  { "_id" : 0, "lastName" : 1 }

)

如果 lastName 字段不是一个数组,则 MongoDB 可以使用 $* * 通配符索引来实现覆盖查询。

多字段查询语句

通配符索引最多只能支持一个查询谓词字段,即:

MongoDB 的查询条件不可以在使用通配符索引时还覆盖其他索引。


MongoDB 的查询条件不可以同时覆盖两个通配符索引。


一个通配符索引可以支持多个字段联合查询,也可以只使用通配符索引包含的其中一个字段查询。

但是,MongoDB 可以使用相同的通配符索引来满足 $or 聚合函数查询 或 $or 操作符查询。

查询排序

满足以下所有条件时,MongoDB 可以使用通配符索引来满足排序 sort ():

查询条件对应的查询计划命中通配符索引。


只能针对查询条件中的字段 sort () 排序。


排序指定的字段不能是数组。

如果不满足上述条件,MongoDB 就不能使用通配符索引进行排序。有关详细信息,请参阅索引交集和排序(Index Intersection and 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()。

不支持的查询模式

通配符索引不支持使用文档中不存在字段的查询。


通配符索引不支持检查字段是否等于文档或数组的查询条件。


通配符索引不支持检查字段是否为空的查询条件。

更多详细信息,参见不支持的查询和聚合模式(Unsupported Query and Aggregation Patterns)

使用显式数组索引的查询

MongoDB 通配符索引在索引期间不记录数组中任何元素的数组位置。

但是,MongoDB 仍然可以选择通配符索引来响应一个查询,该查询包含一个或多个具有显式数组索引的字段路径(例如,parentArray.0.nestedArray.0)。

由于为多层嵌套数组定义索引边界的复杂性日益增加,如果查询中的给定字段路径包含超过 8 个显式数组索引,MongoDB 不会利用通配符索引来响应该路径。

MongoDB 仍然可以使用通配符索引来响应查询中的其他字段路径。

例如:

{

  "parentObject" : {

    "nestedArray" : [

       "elementOne",

       {

         "deeplyNestedArray" : [ "elementTwo" ]

       }

     ]

  }

}

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

"parentObject.nestedArray.0" : "elementOne"


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


如果查询条件中的给定字段路径使用了超过 8 个显式数组索引,MongoDB 不会利用通配符索引来响应该字段路径。


MongoDB 要么选择另一个符合条件的索引来响应查询,要么执行全表扫描。

注意,通配符索引本身并不限制它们遍历文档的深度,这种限制只适用于显式指定精确数组索引的查询。

在没有显式数组索引的情况下,发出相同的查询,MongoDB 可以选择通配符索引来响应查询:

"parentObject.nestedArray" : "elementOne

"parentObject.nestedArray.deeplyNestedArray" : "elementTwo"


上一条:Zemax OpticStudio2022安装教程

下一条:Zemax光学设计优化方法