不与 elemMatch 一起使用的多键部分索引

Multikey partial index not used with elemMatch

考虑以下文档格式,它有一个数组字段 tasks 保存嵌入式文档

{
    "foo": "bar",
    "tasks": [
        {
            "status": "sleep",
            "id": "1"
        },
        {
            "status": "active",
            "id": "2"
        }
    ]
}

tasks.id

上存在部分索引
{
    "v": 2,
    "unique": true,
    "key": {
        "tasks.id": 1
    },
    "name": "tasks.id_1",
    "partialFilterExpression": {
        "tasks.id": {
            "$exists": true
        }
    },
    "ns": "zardb.quxcollection"
}

以下 $elemMatch 对同一数组元素进行多个条件查询

db.quxcollection.find(
{
    "tasks": {
        "$elemMatch": {
            "id": {
                "$eq": "1"
            },
            "status": {
                "$nin": ["active"]
            }
        }
    }
}).explain()

好像没有使用索引

 "winningPlan": {
    "stage": "COLLSCAN",
    "filter": {
        "tasks": {
            "$elemMatch": {
                "$and": [{
                        "id": {
                            "$eq": "1"
                        }
                    },
                    {
                        "status": {
                            "$not": {
                                "$eq": "active"
                            }
                        }
                    }
                ]
            }
        }
    },
    "direction": "forward"
 }

如何让上面的查询使用索引?该索引似乎是通过点符号使用的

db.quxcollection.find({"tasks.id": "1"})

但是我需要相同的数组元素来匹配多个条件,其中包括 status 字段,并且以下内容似乎不等同于上述基于 $elemMatch 的查询

db.quxcollection.find({
  "tasks.id": "1",
  "tasks.status": { "$nin": ["active"] }
})

部分索引的工作方式是使用路径作为键。使用 $elemMatch,您在查询中没有明确的路径。如果您使用 .explain("allPlansExecution") 检查它,查询计划器甚至不会考虑它。

要从索引中获益,您可以在查询中指定路径:

db.quxcollection.find(
{
    "tasks.id": "1",
    "tasks": {
        "$elemMatch": {
            "id": {
                "$eq": "1"
            },
            "status": {
                "$nin": ["active"]
            }
        }
    }
}).explain()

它重复了elemMatch条件的一部分,因此索引将用于获取包含特定id任务的所有文档,然后在获取阶段过滤掉具有“活动”任务的文档。我必须承认查询看起来不太好,所以可以在代码中添加一些注释和解释。