Запрос поиска агрегации Mongodb - вернуть все поддокументы - PullRequest
2 голосов
/ 21 мая 2019

Исходя из этого вопроса, я надеюсь отфильтровать список родительских документов на основе значений в связанных дочерних документах.

Учитывая следующие коллекции mongoDB ....

    // 'job' collection
    {
      "id"      : j1,
      "mediaID" : "ABC1234"
    },
    {
      "id"      : j2,
      "mediaID" : "DEF1234"
    }

.. и ..

    // 'task' collection

    // j1 tasks
    {
      "id"      : "t1",
      "job"     : "j1",
      "taskName": "MOVE",
      "status"  : "COMPLETE"
    },
    {
      "id"      : "t2",
      "job"     : "j1",
      "taskName": "PUBLISH",
      "status"  : "FAILED"
    },
    // j2 tasks
    {
      "id"      : "t3",
      "job"     : "j2",
      "taskName": "MOVE",
      "status"  : "COMPLETE"
    },
    {
      "id"      : "t4",
      "job"     : "j2",
      "taskName": "PUBLISH",
      "status"  : "COMPLETE"
    }

.. где коллекция задач связана с коллекцией заданий через job.id -> task.job

У меня есть сводный запрос, который отфильтрует коллекцию job по критериям в дочерней коллекции. Я использую конвейерный синтаксис Mongo's $lookup и у меня есть запрос примерно такой ...

db.getCollection("job").aggregate(
  [
    {

      "$lookup": {
        "from"    : "task",
        "let"     : {
          "job_id": "$_id"
        },
        "pipeline": [
          {
            "$match": {
              "$expr": {
                "$and": [
                  {
                    // link job.id to task.job
                    "$eq": ["$job", "$$job_id"]
                  },
                  {
                    // Filter taskName
                    "$eq": ["$taskName", "PUBLISH"]
                  },
                  {
                    // Filter by status
                    "$eq": ["$status", "FAILED"]
                  }
                ]
              }
            }
          }
        ],
        "as"      : "tasks"
      }
    },
    {
      // Remove ROOT docs that do not meet 'task' criteria
      "$match": {
        "tasks": {"$ne": []}
      }
    }
  ]
);

Это работает просто отлично, за исключением того, что мне бы хотелось, чтобы все поддокументы родителя возвращались в результатах, а не только тот, который соответствовал.

Например, приведенный выше запрос дает мне это ...

  {
     "id"      : j1,
     "mediaID" : "ABC1234"
     "tasks" : [
         {
           "id"      : "t2",
           "job"     : "j1",
           "taskName": "PUBLISH",
           "status"  : "FAILED"
      },
     ]
   },

.. но я бы хотел это ...

  {
     "id"      : j1,
     "mediaID" : "ABC1234"
     "tasks" : [
         {
           "id"      : "t1",
           "job"     : "j1",
           "taskName": "MOVE",
           "status"  : "COMPLETE"
         },
         {
           "id"      : "t2",
           "job"     : "j1",
           "taskName": "PUBLISH",
           "status"  : "FAILED"
      },
     ]
   },

У меня есть чернила, которые мне нужно было бы использовать $push в какой-то момент, но я в тупике! Любая помощь приветствуется.

Ответы [ 2 ]

1 голос
/ 21 мая 2019

Вы должны убрать логику фильтрации из $lookup и запустить $ match в качестве следующего этапа конвейера.Там вы можете использовать оператор $ anyElementTrue , чтобы проверить, существует ли какой-либо поддокумент с необходимыми значениями:

db.job.aggregate([
    {
        $lookup: {
            from: "task",
            localField: "id",
            foreignField: "job",
            as: "tasks"
        }
    },
    {
        $match: {
            $expr: {
                $anyElementTrue: {
                    $map: {
                        input: "$tasks",
                        in: {
                            $and: [
                                { $eq: [ "$$this.taskName", "PUBLISH" ] },
                                { $eq: [ "$$this.status", "FAILED" ] }
                            ]
                        }
                    }
                }
            }
        }
    }
])

Отказ от ответственности: у вас есть два разных имени поля: taskName и taskCode вваши данные / агрегация.Я решил использовать первый.

0 голосов
/ 22 мая 2019

Вы можете использовать $ addFields.

db.job.aggregate([
    {$lookup: {from: 'task', localField: 'id', foreignField: 'job', as: 'taskList'}},
    {$addFields: {
      tasks: {
        $filter: {
          input: '$taskList',
          as: 't',
          cond: { $and: [
              {$eq: ['$$t.taskCode', 'PUBLISH']},
              {$eq: ['$$t.status', 'FAILED']}
          ]}
        }
      }
    }},
    {$project: {
      _id: 1,
      mediaID: 1,
      tasks: 1
    }}
])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...