Я потратил на это большую часть дня, и теперь у меня нет идей. Вот моя коллекция:
[
{
"_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
$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 for
orders` {
$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" }
}
}
])
это просто логика, которую вы можете сделать на своем клиентском языке, и это будет эффективнее, чем запрос, и если вы действительно хотите работать в агрегации, попробуйте оператор $function, вы можете сделать JS-код внутри этой функции.
Понятно. Я беспокоюсь, делая это на стороне клиента, что эти журналы могут содержать сотни тысяч записей, и, поскольку моя база данных не находится на том же сервере, что и мое приложение узла, я не хотел загружать все эти данные в свое приложение, чтобы делать что-то с этим. В любом случае, очень ценю ваше понимание.
Это работает, спасибо. Но, учитывая относительную сложность этой агрегации, не могли бы вы порекомендовать мне вместо этого извлекать необработанные данные в свой код и делать это в JS вместо их агрегирования? Мне просто интересно, что, по вашему мнению, будет более эффективным.