Получить соответствующий документ из вложенного массива

Я пытаюсь собрать все объекты во вложенный массив, где поле spec равно unknown.

Структура документа аналогична этой:

{
    "_id" :"5b1e73786f11e421956023c3",
    "subs" : [ 
        {
            "name" : "subrepo1",
            "files" : [ 
                {
                    "name" : ".....",
                    "spec" : "Unknown"
                }, 
                {
                    "name" : ".....",
                    "spec" : "Unknown"
                }
            ]
        },
        {
            "name" : "subrepo2",
            "files" : [ 
                {
                    "name" : "file2",
                    "spec" : "Unknown"
                }, 
                {
                    "name" : ".....",
                    "spec" : "1234"
                }
            ]
        }
    ]
}

Я пробовал следующее, но это не работает. Я думаю, что это в правильном направлении, но я, вероятно, упускаю что-то важное.

db.col.aggregate([
    {$match: {'subs.files.spec': 'Unknown'}},
    {$project: {
        'subs.files': {$filter: {
            input: '$subs.files',
            //as: 'subs.files',
            cond: {$eq: ['this.spec', 'FunSuite']}
        }},
        //_id: 0
    }}
])

Ожидаемый результат будет следующим: (поэтому ТОЛЬКО файлы, у которых спецификация равна Unknown (НЕ другие)

{
    "_id" : "5b1e73786f11e421956023c3",
    "subs" : [ 
        {
            "name" : "subrepo1",
            "files" : [ 
                {
                    "name" : ".....",
                    "spec" : "Unknown"
                }, 
                {
                    "name" : ".....",
                    "spec" : "Unknown"
                }
            ]
        },
        {
            "name" : "subrepo2",
            "files" : [ 
                {
                    "name" : "file2",
                    "spec" : "Unknown"
                }
            ]
        }
    ]
}

person Captain Obvious    schedule 12.06.2018    source источник
comment
Каков ваш ожидаемый результат?   -  person Mạnh Quyết Nguyễn    schedule 12.06.2018
comment
@MạnhQuyếtNguyễn Добавлен вывод, чтобы было понятно.   -  person Captain Obvious    schedule 12.06.2018


Ответы (2)


Вам нужно использовать оператор $filter aggregation, который дает только соответствующий элемент из массива и экранирует другие элементы

db.collection.aggregate([
  {
    $unwind: "$subs"
  },
  {
    $project: {
      "subs.name": "$subs.name",
      "subs.files": {
        $filter: {
          input: "$subs.files",
          as: "file",
          cond: {
            $eq: [
              "$$file.spec",
              "Unknown"
            ]
          }
        }
      }
    }
  },
  {
    $group: {
      _id: "$_id",
      subs: {
        $push: "$subs"
      }
    }
  }
])

Выше будет получен следующий вывод

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "subs": [
      {
        "files": [
          {
            "name": ".....",
            "spec": "Unknown"
          },
          {
            "name": ".....",
            "spec": "Unknown"
          }
        ],
        "name": "subrepo1"
      },
      {
        "files": [
          {
            "name": "file2",
            "spec": "Unknown"
          }
        ],
        "name": "subrepo2"
      }
    ]
  }
]

Вы можете проверить результат здесь

И если вы хотите получить поля как в массиве, удалите $unwind и этап $replaceRoot из конвейера

db.collection.aggregate([
  {
    $unwind: "$subs"
  },
  {
    $project: {
      "subs.name": "$subs.name",
      "subs.files": {
        $filter: {
          input: "$subs.files",
          as: "file",
          cond: {
            $eq: [
              "$$file.spec",
              "Unknown"
            ]
          }
        }
      }
    }
  },
  {
    $unwind: "$subs.files"
  },
  {
    $replaceRoot: {
      newRoot: "$subs.files"
    }
  }
])

Выше будет получен следующий вывод

[
  {
    "name": ".....",
    "spec": "Unknown"
  },
  {
    "name": ".....",
    "spec": "Unknown"
  },
  {
    "name": "file2",
    "spec": "Unknown"
  }
]
person Ashh    schedule 12.06.2018
comment
Это не те входные данные, которые приведены в примере, вы можете их изменить? (Выход в порядке) - person Captain Obvious; 12.06.2018
comment
@CaptainObvious У меня изменился ввод и вывод... Пожалуйста, проверьте еще раз... - person Ashh; 12.06.2018
comment
вроде работает; Благодарю. Можете ли вы также предоставить запрос для исходного вывода? Вывод содержит только файлы {name: ..., spec: ....}, потому что я тоже хотел бы иметь это. Я соглашусь, если вы включите это. - person Captain Obvious; 12.06.2018

Попробуйте так:

db.col.aggregate([
    {
        $unwind: '$subs'
    },
    {
        $unwind: '$subs.files'
    },
    {
        $match: {
            'subs.files.spec': 'Unknown'
        }
    }
]);
person karaxuna    schedule 12.06.2018
comment
Это также возвращает элементы в массиве файлов, где спецификация не равна неизвестной, чего я не хочу (см. вывод) - person Captain Obvious; 12.06.2018