Mongodb 索引,partialfilters 是否也应包含在索引中?

Mongodb index, should partialfilters also be included in the index?

我有这样的查询:

collection.find({
  type: "person",
  image: {
    $exists: true
  }
}, {
  sort: [
    ["age", -1]
  ],
  limit: 9,
)

如果我已经过滤了这些字段,还需要将它们包含在索引中吗?

collection.createIndex(
  {type: 1, image: 1, age: -1}, 
  { 
    partialFilterExpression: {
      type: 'person',
      image: {
        $exists: true
      }
    },
    background: true
  }
)

或者它是否已经通过部分过滤器表达式知道,而我只是通过未修复的内容进行查询?

collection.createIndex(
  {age: -1}, 
  { 
    partialFilterExpression: {
      type: 'person',
      image: {
        $exists: true
      }
    },
    background: true,
    name: "ageIndex"
  }
)

想一想,也许正确的做法是不索引所有字段,而是使用提示强制数据库使用索引?

collection.find({
  type: "person",
  image: {
    $exists: true
  }
}, {
  sort: [
    ["age", -1]
  ],
  limit: 9,
).hint("ageIndex")

这实际上会正确使用索引并忽略集合中的其他文档吗?

根据 MongoDB Partial Index documentation,如果在该表达式中执行查询,则如果字段已经在 partialFilterExpression 中,则无需将这些字段包含在索引中。

下面的索引将完美地用于提供的查询。

collection.createIndex(
  {age: -1}, 
  {partialFilterExpression: { type: 'person', image: { $exists: true }}}
)

查询:

collection.find({type: "person", image: { $exists: true }}, {sort: [["age", -1]])

提示都不需要。

重要提示:如果类型不是'person',或者不提供图像过滤器——索引将不起作用。

如果查询结果 100% 在索引内,索引将起作用。

文档中的另一个示例:

索引:

db.restaurants.createIndex(
   { cuisine: 1, name: 1 },
   { partialFilterExpression: { rating: { $gt: 5 } } }
)

查询:

db.restaurants.find( { cuisine: "Italian", rating: { $lt: 8 } } )

此查询不属于上述索引,因为它包含低于 5 的评分。

考虑正在查询的 示例文档 的集合,如问题:

{ _id: 1, type: "person", image: "i-1", age: 19, "fld": 12 },
{ _id: 2, type: "person", image: "i-2", age: 22, "fld": 121 },
{ _id: 3, type: "thing", image: "i-99", age: 29, "fld": 1212 },    // 'type' not 'person'
{ _id: 4, type: "person", age: 31, "fld": 12121 },                 // 'image' missing
{ _id: 5, type: "person", image: "i-3", age: 13, "fld": 121212 },
{ _id: 6, type: "person", age: 43, "fld": 1212121 },               // 'image' missing
{ _id: 7, type: "person", image: "i-4", age: 20, "fld": 1 }

Do I need to include the fields in the index if I already have them filtered? Or would it already know by the partial filter expression and I'm only querying by what's not fixed?

创建一个索引:

db.collection.createIndex( { type: 1, age: -1 },
  {
     partialFilterExpression: {
       type: 'person',
       image: { $exists: true }
    }
  }
)

请注意,索引字段仅在 typeage 上。为什么?这在下面的索引使用验证中进行了解释。

查询:

db.collection.find( { type: "person", image: { $exists: true } } ).sort( { age: -1 } )

结果:

查询 returns 预期的筛选文档 的排序顺序。

{ "_id" : 2, "type" : "person", "image" : "i-2", "age" : 22, "fld" : 121 }
{ "_id" : 7, "type" : "person", "image" : "i-4", "age" : 20, "fld" : 1 }
{ "_id" : 1, "type" : "person", "image" : "i-1", "age" : 19, "fld" : 12 }
{ "_id" : 5, "type" : "person", "image" : "i-3", "age" : 13, "fld" : 121212 }


索引使用验证:

可以通过使用explain方法生成查询计划来验证索引使用情况:

db.collection.find( { type: "person", image: { $exists: true } } ).sort( { age: -1 } ).explain()

计划输出显示过滤器 排序操作的索引使用情况。这在计划中被标记为 IXSCAN(索引扫描)和 SORT 阶段的缺失。这是此查询索引的正确用法。

在索引定义中,两个字段type + age指定一个复合索引。这要求索引应用于 type(用于过滤)和 age(用于排序)。字段 image 无法在索引定义中指定,因为它 未与相等 条件一起使用(使用 $exists);如果指定,索引将不会用于以下排序字段(来自 documentation):

An index can support sort operations on a non-prefix subset of the index key pattern. To do so, the query must include equality conditions on all the prefix keys that precede the sort keys.

查询计划(部分):

{
    "queryPlanner" : {
            "plannerVersion" : 1,
            "namespace" : "test.persons",
            "indexFilterSet" : false,
            "parsedQuery" : {
                    "$and" : [
                            {
                                    "type" : {
                                            "$eq" : "person"
                                    }
                            },
                            {
                                    "image" : {
                                            "$exists" : true
                                    }
                            }
                    ]
            },
            "queryHash" : "25E877F5",
            "planCacheKey" : "C9D745BE",
            "winningPlan" : {
                    "stage" : "FETCH",
                    "filter" : {
                            "image" : {
                                    "$exists" : true
                            }
                    },
                    "inputStage" : {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                    "type" : 1,
                                    "age" : -1
                            },
                            "indexName" : "type_1_age_-1",
                            "isMultiKey" : false,
                            "multiKeyPaths" : {
                                    "type" : [ ],
                                    "age" : [ ]
                            },
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : true,
                            "indexVersion" : 2,
                            "direction" : "forward",
                            "indexBounds" : {
                                    "type" : [
                                            "[\"person\", \"person\"]"
                                    ],
                                    "age" : [
                                            "[MaxKey, MinKey]"
                                    ]
                            }
                    }
            }, ...