Можно ли избежать использования одного и того же критерия $ match дважды при использовании $ unwind? - PullRequest
2 голосов
/ 12 апреля 2019

В качестве примера возьмем следующие данные:

{
  _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 и $match, $match будет использовать индекс для применения ранней фильтрации, но конечный результат будет не таким, как хотелось бы: он получит дополнительные stock s, которые не имеют размер L, но имеют родного размера stock s, которые принадлежат тем же item.

Должен ли я использовать одну и ту же операцию $match дважды, т. Е. Как до, так и после $unwind, чтобы она использовала индекс и возвращала правильные результаты?

1 Ответ

2 голосов
/ 12 апреля 2019

Да, вы можете использовать $match ступень дважды в конвейере агрегации, но здесь будет только первая $match ступень используйте индекс, второй будет выполнять проверку.

[
    { "$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"] }
      }
    }
  }}
]
...