#mongodb #mongodb-query #aggregation-framework
#mongodb #mongodb-запрос #агрегация-фреймворк
Вопрос:
В качестве примера возьмем следующие данные:
{
_id: 1,
item: "abc",
stock: [
{ size: "S", color: "red", quantity: 25 },
{ size: "S", color: "blue", quantity: 10 },
{ size: "M", color: "blue", quantity: 50 }
]
}
{
_id: 2,
item: "def",
stock: [
{ size: "S", color: "blue", quantity: 20 },
{ size: "M", color: "blue", quantity: 5 },
{ size: "M", color: "black", quantity: 10 },
{ size: "L", color: "red", quantity: 2 }
]
}
{
_id: 3,
item: "ijk",
stock: [
{ size: "M", color: "blue", quantity: 15 },
{ size: "L", color: "blue", quantity: 100 },
{ size: "L", color: "red", quantity: 25 }
]
}
Допустим, я собираюсь отфильтровать stock
s, соответствующие критериям size = 'L'
. У меня уже есть индекс с несколькими ключами в stock.size
поле.
В конвейере агрегации, если я использую следующие две операции:
[{$unwind: {path: "$stock"}},
{$match: {"stock.size": "L"}}]
Я получу желаемые результаты, но когда БД станет очень большой, на $unwind
этапе придется сканировать всю коллекцию, не используя существующий индекс, что очень неэффективно.
Если я изменю порядок операций $unwind
and $match
на обратный, то $match
будет использоваться индекс для применения ранней фильтрации, но конечный результат будет не таким, как хотелось бы: он будет извлекать дополнительные stock
s, которые не имеют размера L, но имеют родственные stock
s размером L, которые принадлежат одному и тому же item
.
Должен ли я использовать одну и ту же $match
операцию дважды, т. Е. Как До, так и после $unwind
, чтобы она использовала индекс и возвращала правильные результаты?
Ответ №1:
Да, вы можете использовать $match
этап дважды в конвейере агрегации, но здесь только первый $match
этап будет использовать индекс, второй будет выполнять collscan.
[
{ "$match": { "stock.size": "L" }},
{ "$unwind": { "path": "$stock" }},
{ "$match": { "stock.size": "L" }}
]
Если вы хотите избежать $match
двойного использования, используйте $filter
агрегацию
[
{ "$match": { "stock.size": "L" } },
{ "$addFields": {
"stock": {
"$filter": {
"input": "$stock",
"as": "st",
"cond": { "$eq": ["$stock.size", "L"] }
}
}
}}
]