暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

技术干货 | MongoDB 功能详解之通配符索引(Wildcard Indexes)

Mongoing中文社区 2023-04-26
625

点击下方公众号关注并分享获取 MongoDB 最新资讯


通配符索引(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 上的通配符索引可以支持 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 的非通配符索引(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)。

            • 不能创建复合索引。
            • 不能为通配符索引指定下列属性:

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

          ⚠️ 重要:

          通配符索引与通配符文本索引(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"

            📒 注意:

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

          原文:Wildcard Indexes

          关于译者:

          赵瑞航,MongoDB 中文社区成员,现就职于中国联通软件研究院,目前专注于 MongoDB 数据库运维与技术支持。

          【推荐阅读】


          社区招募

          为了让社区组委会成员和志愿者朋友们灵活参与,同时我们为想要深度参与社区建设的伙伴们开设了“招募通道”,如果您想要在社区里面结交志同道合的技术伙伴,想要通过在社区沉淀有价值的干货内容,想要一个展示自己的舞台,提升自身的技术影响力,即刻加入社区贡献队伍~ 点击提交申请

          社区合作

          接下来我们会在诸多城市举办技术大会,如果您有优质的议题和 MongoDB 相关经验的嘉宾可以与我们分享,或者有赞助合作意向的小伙伴,包含但不限制于联合主办方、媒体合作等,都可以随时联系我们的社区助手小芒果(微信ID:mongoingcom)沟通具体事宜。

          重要资讯

          感谢大家一直以来对社区的关注与支持!社区在大家共同的努力下不断发展与壮大,为了给大家营造更便捷的交流环境,QQ 技术交流群将同步在“微信技术交流群”中。扫描下方二维码添加小芒果微信发送“mongo”即可进入技术交流群。




          获取更多精彩内容点击社区网站www.mongoing.com


          扫描上方二维码添加小芒果微信

          (ID:mongoingcom)

          进入中文用户组技术交流群


          长按二维码加入我们

          文章转载自Mongoing中文社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论