#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/NLAHfYangIs5. Конечно. это «частичное».
Ответ №1:
Способ работы частичных индексов заключается в том, что он использует путь в качестве ключа. С помощью $elemMatch у вас нет явного пути в запросе. Если вы проверите его, .explain("allPlansExecution")
он даже не будет рассмотрен планировщиком запросов.
Чтобы воспользоваться индексом, вы можете указать путь в запросе:
db.quxcollection.find(
{
"tasks.id": "1",
"tasks": {
"$elemMatch": {
"id": {
"$eq": "1"
},
"status": {
"$nin": ["active"]
}
}
}
}).explain()
Он дублирует часть условия соответствия элементов, поэтому индекс будет использоваться для получения всех документов, содержащих задачи с определенным идентификатором, затем он будет отфильтровывать документы с «активными» задачами на этапе выборки. Я должен признать, что запрос выглядит не очень хорошо, поэтому можно добавить некоторые комментарии к коду с пояснениями.