Как использовать $ mergeObjects для объединения массивов, соответствующих существующим группам? - PullRequest
1 голос
/ 09 июля 2020

Я пытаюсь объединить два массива в моем конвейере агрегации. После выполнения $ facet мой документ MongoDB имеет следующий формат:

{
  "final": [
    {
      "key": "TP-1",
      "status_map": [
        {  "status": "Closed", "final": [ "a", "b"]},
        { "status": "Done", "final": ["c","d" ] }
      ]
    },
    {
      "key": "TP-2",
      "status_map": [
        { "status": "Closed", "final": [  "x","y"] }
      ]
    }
  ],
  "start": [
    {
      "key": "TP-1",
      "status_map": [
        { "status": "Closed", "start": [ "h"]},
        { "status": "Done", "start": ["a"]}
      ]
    },
    {
      "key": "TP-2",
      "status_map": [{ "status": "Done", "start": ["l","m"]}
      ]
    }
  ]
}

Ожидаемый результат:

Мне нужно объединить массив final и start, соответствующий двум группам:

  1. На основе key, а затем
  2. На основе status
{
  "data": [
    {
      "key": "TP-1",
      "status_map": [
        { "status": "Closed","final": ["a","b"],"start":["h"]},
        { "status": "Done","final": ["c","d"],"start":["a"]}
        ]
    },
    {
      "key": "TP-2",
      "status_map": [
        { "status": "Closed",  "final":[ "x","y"],"start": []},
        { "status": "Done",  "final": [ ],"start": [ "l","m"]}
      ]
    }
  ]
}

Как достичь этого варианта использования?

Ответы [ 3 ]

1 голос
/ 10 июля 2020

Есть несколько способов приблизиться к этому, не обязательно с помощью $mergeObjects. Но поскольку вы упомянули $mergeObjects, это тот, который его использует:

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

db.collection.aggregate([
  {
    $project: {
      all: { $concatArrays: ["$final","$start"] }
    }
  },
  {
    $unwind: "$all"
  },
  {
    $unwind: "$all.status_map"
  },
  {
    $group: {
      _id: {
        _id: "$_id",  // keep _id in $group to apply the group for each document, otherwise if you want to apply group on all documents, omit this
        key: "$all.key",
        status: "$all.status_map.status"
      },
      status_map: { $mergeObjects: "$$ROOT.all.status_map" }
    }
  },
  { // some data don't have start or end at all, we have to set a default empty array
    $addFields: { // you can skip this stage if you allow data without start and final keys
      "status_map.start": { $ifNull: ["$status_map.start", []] },
      "status_map.final": { $ifNull: ["$status_map.final", []] }
    }
  },
  {
    $group: {
      _id: { _id: "$_id._id", key: "$_id.key" },
      key: { $first: "$_id.key" },
      status_map: { $push: "$status_map" }
    }
  }
])

Пн go Площадка

1 голос
/ 09 июля 2020

Без каких-либо предположений (например, чтобы всегда появлялись оба ключа) моя стратегия заключалась в объединении обоих массивов, раскручивании и, наконец, группировке по ключу. 1005 *

0 голосов
/ 10 июля 2020

Добавление к ответу @Tom Slabbaert,

Mon go Playground

Здесь финальный и начальный массивы имеют формат array of array. Но это должно быть просто array.

Это может быть достигнуто с помощью $unwind на status_map и $reduce на status_map.final и status_map.start массивах.

Последний запрос:

db.collection.aggregate([
  {
    $project: {
      concat: {
        $concatArrays: [
          "$final",
          "$start"
        ]
      }
    }
  },
  {
    $unwind: "$concat"
  },
  {
    $unwind: "$concat.status_map"
  },
  {
    $group: {
      _id: {
        k: "$concat.key",
        status: "$concat.status_map.status"
      },
      final: {
        $push: "$concat.status_map.final"
      },
      start: {
        $push: "$concat.status_map.start"
      }
    }
  },
  {
    $group: {
      _id: "$_id.k",
      status_map: {
        $push: {
          status: "$_id.status",
          final: "$final",
          start: "$start"
        }
      }
    }
  },
  {
    $project: {
      key: "$_id",
      status_map: 1,
      _id: 0
    }
  },
  {
    $unwind: "$status_map"
  },
  {
    $project: {
      key: 1,
      "status_map.status": 1,
      final: {
        $reduce: {
          input: "$status_map.final",
          initialValue: [],
          in: {
            $concatArrays: [
              "$$value",
              "$$this"
            ]
          }
        }
      },
      start: {
        $reduce: {
          input: "$status_map.start",
          initialValue: [],
          in: {
            $concatArrays: [
              "$$value",
              "$$this"
            ]
          }
        }
      }
    }
  },
  {
    $group: {
      _id: "$key",
      status_map: {
        $push: {
          status: "$status_map.status",
          final: "$final",
          start: "$start"
        }
      }
    }
  }
])

Пн go Детская площадка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...