Как сгруппировать массив вложенных документов по нескольким полям?

Я потратил на это большую часть дня, и теперь у меня нет идей. Вот моя коллекция:

[
  {
    "_id": "ID_XXX",
    "logs": [
      {
        "lead_id": 123,
        "list_id": "list_44",
        "order_id": "order_1"
      },
      {
        "lead_id": 124,
        "list_id": "list_44",
        "order_id": "order_2"
      }
    ]
  },
  {
    "_id": "ID_YYY",
    "logs": [
      {
        "lead_id": 125,
        "list_id": "list_44",
        "order_id": "order_2"
      },
      {
        "lead_id": 126,
        "list_id": "list_44",
        "order_id": "order_2"
      },
      {
        "lead_id": 127,
        "list_id": "list_44",
        "order_id": "order_3"
      },
      {
        "lead_id": 128,
        "list_id": "list_66",
        "order_id": "order_3"
      }
    ]
  }
]

Я просто пытаюсь получить подсчет для list_id и order_id, сохраняя при этом _id документа, в котором они находятся. Вот мой желаемый результат:

[
  {
    "_id": "ID_XXX",
    "counts": [
      {
        "lists": {"list_44": 2},
      },
      {
        "orders": {"order_1": 1, "order_2": 1}
      }
    ]
  },
  {
    "_id": "ID_YYY",
    "counts": [
      {
        "lists": {"list_44": 3, "list_66": 1},
      },
      {
        "orders": {"order_2": 2, "order_3": 2}
      }
    ]
  }
]

Я перепробовал слишком много агрегатных вариантов, чтобы перечислять их здесь, но последний из них таков:

db.collection.aggregate([
  {
    $unwind: "$logs"
  },
  {
    $group: {
      _id: "$_id",
      lists: {
        $push: "$logs.list_id"
      },
      orders: {
        $push: "$logs.order_id"
      }
    }
  }
])

Что не дает мне того, что я хочу. Может кто-то указать мне верное направление? Вот ссылка на игровую площадку: https://mongoplayground.net/p/f-jk7lbSrJ0

Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
1
0
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  • $reduce для итерации цикла logs, преобразовать объект logs в массив в формате k (ключ) v (значение), используя $objectToArray, $concatArrays с начальным значением в $reduce,
  • $filter выше уменьшить результат в качестве ввода и отфильтровать обязательные поля из logs
  • $unwind деконструировать logs массив
db.collection.aggregate([
  {
    $addFields: {
      logs: {
        $filter: {
          input: {
            $reduce: {
              input: "$logs",
              initialValue: [],
              in: { $concatArrays: ["$$value", { $objectToArray: "$$this" }] }
            }
          },
          cond: { $in: ["$$this.k", ["list_id", "order_id"]] }
        }
      }
    }
  },
  { $unwind: "$logs" },
  • $group с помощью _id и logs объекта и получить общее количество с помощью $sum
  {
    $group: {
      _id: {
        _id: "$_id",
        logs: "$logs"
      },
      counts: { $sum: 1 }
    }
  },
  • $group на _id и создать массив lists, если logs.k равно list_id, и вернуть в формате k и v, иначе $$REMOVE, так же, как для orders создать массив порядка на основе order_id
  • $addFields для преобразования массива lists из формата k и v в формат объекта с использованием массива $arrayToObjectand same as fororders`
  {
    $group: {
      _id: "$_id._id",
      lists: {
        $push: {
          $cond: [
            { $eq: ["$_id.logs.k", "list_id"] },
            {
              k: "$_id.logs.v",
              v: "$counts"
            },
            "$$REMOVE"
          ]
        }
      },
      orders: {
        $push: {
          $cond: [
            { $eq: ["$_id.logs.k", "order_id"] },
            {
              k: "$_id.logs.v",
              v: "$counts"
            },
            "$$REMOVE"
          ]
        }
      }
    }
  },
  {
    $addFields: {
      lists: { $arrayToObject: "$lists" },
      orders: { $arrayToObject: "$orders" }
    }
  }
])

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

Это работает, спасибо. Но, учитывая относительную сложность этой агрегации, не могли бы вы порекомендовать мне вместо этого извлекать необработанные данные в свой код и делать это в JS вместо их агрегирования? Мне просто интересно, что, по вашему мнению, будет более эффективным.

codemonkey 23.12.2020 07:34

это просто логика, которую вы можете сделать на своем клиентском языке, и это будет эффективнее, чем запрос, и если вы действительно хотите работать в агрегации, попробуйте оператор $function, вы можете сделать JS-код внутри этой функции.

turivishal 23.12.2020 07:47

Понятно. Я беспокоюсь, делая это на стороне клиента, что эти журналы могут содержать сотни тысяч записей, и, поскольку моя база данных не находится на том же сервере, что и мое приложение узла, я не хотел загружать все эти данные в свое приложение, чтобы делать что-то с этим. В любом случае, очень ценю ваше понимание.

codemonkey 23.12.2020 07:56

Другие вопросы по теме