Размер документа массива проекта MongoDB с условием - PullRequest
1 голос
/ 31 марта 2020

Допустим, у меня есть следующая структура документа:

{
  "_id": "ID",
  "array": [
    {
      "a": "A",
      "b": "B",
      "c": {
        "x": true,
        "y": true,
        "z": false
      }
    },
    {
      "a": "A",
      "b": "B"
    },
    {
      "a": "A",
      "b": "B"
      "c": {
        "s": true
      }
    }
  ]
}

Я пытаюсь выполнить агрегацию, которая дает мне такой тип вывода:

{
  "_id": "ID",
  "array": [
    {
      "a": "A",
      "b": "B",
      "c": 2
    },
    {
      "a": "A",
      "b": "B",
      "c": 0
    },
    {
      "a": "A",
      "b": "B"
      "c": 1
    }
  ]
}

Итак, что я хочу do вместо документа c возвращает количество элементов со значением true. И, как в примере, поле c не обязательно существует (в этом случае я хочу вернуть 0), а когда это происходит, его подполя не обязательно совпадают с другими подполями c в массиве. Допустим, я делаю агрегацию:

db.collection.aggregate([
  {"$match": {<conditions>}},
  {"$project": {
    "array.a": 1,
    "array.b": 1,
    "array.c": <?>,
  }}
])

Как мне настроить "array.c" в проекции для достижения sh того, что мне нужно?

1 Ответ

2 голосов
/ 31 марта 2020

Вам нужно иметь как минимум MongoDB v3.4.4

Проецирование "array.a": 1 вернет ["A", "A", "A"]. Поскольку ваше поле array является массивом, нам нужно использовать оператор $map для итерации.

Чтобы перебрать ключи объекта, нам нужно преобразовать его в массив с помощью оператора $ objectToArray .

"c": {                   "c": [
    "x": true,             {k: "x", v: true},
    "y": true,       ->    {k: "y", v: true},
    "z": false             {k: "z", v: false},
  }                      ]

Затем мы применяем оператор $filter, чтобы получить только k:v пар, у которых v равно true.


db.collection.aggregate([
  {
    $match: {}
  },
  {
    $project: {
      array: {
        $map: {
          input: "$array",
          as: "arr",
          in: {
            a: "$$arr.a",
            b: "$$arr.b",
            c: {
              $size: {
                $filter: {
                  input: {
                    $objectToArray: {
                      $ifNull: [
                        "$$arr.c",
                        {}
                      ]
                    }
                  },
                  cond: {
                    $eq: [
                      "$$this.v",
                      true
                    ]
                  }
                }
              }
            }
          }
        }
      }
    }
  }
])

MongoPlayground

...