Могу ли я избежать использования одних и тех же критериев соответствия $ дважды при использовании $unwind?

#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"] }
      }
    }
  }}
]