Многоключевой частичный индекс, не используемый с сопоставлением элементов

#arrays #mongodb #mongodb-query #mongodb-indexes

Вопрос:

Рассмотрим следующий формат документа, в котором есть поле массива 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"] }
})
 

Комментарии:

1. Откуда вы знаете, что elemmatch использует какие-либо индексы?

2. @D.SM .explain() Не подтверждает ли это?

3. В этом вопросе чего-то не хватает. Я попытался воспроизвести это здесь mongoplayground.net/p/McHKOlujolW Не могли бы вы взглянуть. Он использует там индекс, и мне интересно, сможете ли вы определить, чем он отличается от вашей настройки.

4. @AlexBlex Одно отличие, которое я могу заметить, заключается в частичном индексе. Если я добавлю "partialFilterExpression": { "tasks.id": { "$exists": true } } , это приведет к повторному сканированию коллекции mongoplayground.net/p/NLAHfYangIs

5. Конечно. это «частичное».

Ответ №1:

Способ работы частичных индексов заключается в том, что он использует путь в качестве ключа. С помощью $elemMatch у вас нет явного пути в запросе. Если вы проверите его, .explain("allPlansExecution") он даже не будет рассмотрен планировщиком запросов.

Чтобы воспользоваться индексом, вы можете указать путь в запросе:

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

Он дублирует часть условия соответствия элементов, поэтому индекс будет использоваться для получения всех документов, содержащих задачи с определенным идентификатором, затем он будет отфильтровывать документы с «активными» задачами на этапе выборки. Я должен признать, что запрос выглядит не очень хорошо, поэтому можно добавить некоторые комментарии к коду с пояснениями.