Mongodb grouping statement by custom file - mongodb

I want to retrieve data from mongodb, grouping and summing for a custom field based in a db field which can exist or not but I don't have the result I expect because I there is no data ggrupation (see attached file)enter image description here. The mongo statement is:
aggregate({
$match: {
owner: 'W99999',
creation_date: {
$gte: 1530748800,
$lte: 1531292133
}
},
$project: {
isWarm: {
$cond: [{
$not: ["$referral"] }, 1, 0 ]
},
isCold: {
$cond: [{
$not: ["$referral"] }, 0, 1 ]
},
daysBefore: {
$subtract: [6, {
$trunc: {
$divide: [{
$subtract: ['$creation_date', 1530748800]
}, 86400]
}
}]
}
},
$group: {
_id: {
isWarm: { $sum: "$isWarm" },
isCold: { $sum: "$isCold" },
daysBefore: '$daysBefore'
}
})
I think the problem is the "isWarm" and "isCold" condition for creating them. Thank you in advance.
UPDATE 05/07/2018.
Schema (trunked for security reasons):
{
"_id": "1",
"creation_date":"1515780901",
"referral: //This field is optional.
{
some_data: { }
},
more_data: { }
}
Result expected:
{ [
{ isCold: 3, isWarm: 2, daysBefore: 0 },
{ isCold: 2, isWarm: 5, daysBefore: 1 },
{ isCold: 5, isWarm: 0, daysBefore: 2 },
{ isCold: 1, isWarm: 2, daysBefore: 3 },
{ isCold: 1, isWarm: 1, daysBefore: 4 },
{ isCold: 1, isWarm: 0, daysBefore: 5 },
{ isCold: 0, isWarm: 0, daysBefore: 6 }
] }
I would like to have the object even if there is no documents to count (e.g. last line of the result).
RESOLVED: I need to test with real data.
I think I have a solution:
[{ $project: {
_id: 0,
daysBefore: {
$subtract: [6, {
$trunc: {
$divide: [{
$subtract: ['$creation_date', 1530748800] }, 86400]
}
}]
},
isWarm: {$cond: [{ $gte: ['$referral', null]}, 1, 0]},
isCold: {$cond: [{ $gte: ['$referral', null]}, 0, 1]}} },
{
$group:
{
_id: { creation_date: '$daysBefore' },
isWarm: { $sum: '$isWarm' },
isCold: { $sum: '$isCold' }
}
}]

Related

mongoDb groupby with array object field

I have searched a lot to use groupby based on the array field value, but I didn't get proper results in google, so I'm posting here.
I have tried my best, it works 50% need to correct my query can anyone help me with this
I have a database value like
{"_id": "62b0bec8922dc767f8b933b4",
"seatSeletion": [{
"rowNo": 0,
"columnNo": 0,
"seatNo": 3
}, {
"rowNo": 0,
"columnNo": 1,
"seatNo": 4
}],
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T18:14:38.133+00:00",
"movieTiming": "1:30 p.m",
},
{"_id": "62b0b91560f57e0cb220db02","seatSeletion": [{
"rowNo": 0,
"columnNo": 0,
"seatNo": 1
}, {
"rowNo": 0,
"columnNo": 1,
"seatNo": 2
}],
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T18:14:38.133+00:00",
"movieTiming": "1:30 p.m",
}
expected output
{
"seatSeletion": [
{
"rowNo": 0,
"columnNo": 0,
"seatNo": 1
},
{
"rowNo": 0,
"columnNo": 1,
"seatNo": 2
},
{
"_id": "62b0b90e60f57e0cb220db00",
"rowNo": 0,
"columnNo": 0,
"seatNo": 3
},
{
"_id": "62b0b90e60f57e0cb220db01",
"rowNo": 0,
"columnNo": 1,
"seatNo": 4
}
],
"movieTiming": "1:30 p.m",
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T11:03:37.000Z"
},
this is how I tried in my query
Bookings.aggregate([
{
$match: {
$and: [{ movieId: ObjectId(bookingParam.movieId) },
{ movieTiming: bookingParam.movieTiming },
{ movieDate: dateQuery },
]
}
},
{
$group: {
_id: {
seatSeletion: '$seatSeletion', movieTiming: '$movieTiming',
movieId: '$movieId', movieDate: '$movieDate', createdBy: "$createdBy", updatedBy: "$updatedBy", movies: "$movies"
}
}
},
{
$project: {
seatSeletion: '$_id.seatSeletion', movieTiming: '$_id.movieTiming',
movieId: '$_id.movieId', movieDate: '$_id.movieDate', movies: "$_id.movies",
_id: 0
}
}
])
but i got it like this
{
"seatSeletion": [
{
"_id": "62b0b91560f57e0cb220db03",
"rowNo": 0,
"columnNo": 0,
"seatNo": 1
},
{
"_id": "62b0b91560f57e0cb220db04",
"rowNo": 0,
"columnNo": 1,
"seatNo": 2
}
],
"movieTiming": "1:30 p.m",
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T11:03:37.000Z"
},
{
"seatSeletion": [
{
"_id": "62b0b90e60f57e0cb220db00",
"rowNo": 0,
"columnNo": 0,
"seatNo": 3
},
{
"_id": "62b0b90e60f57e0cb220db01",
"rowNo": 0,
"columnNo": 1,
"seatNo": 4
}
],
"movieTiming": "1:30 p.m",
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T11:03:37.000Z"
}
can anyone help me to fix this issue.
One option is using $reduce after the $group. It is important NOT to group by the seatSeletion as the value of this field is not common to these movies:
db.collection.aggregate([
{
$match: {
$and: [
{movieId: "62af1ff6cb38656a4ffe36aa"},
{movieTiming: "1:30 p.m"},
{movieDate: "2022-06-20T18:14:38.133+00:00"},
]
}
},
{
$group: {
_id: {movieTiming: "$movieTiming", movieId: "$movieId", movieDate: "$movieDate"},
seatSeletion: {$push: "$seatSeletion"}
}
},
{
$project: {
seatSeletion: {
$reduce: {
input: "$seatSeletion",
initialValue: [],
in: {$concatArrays: ["$$value", "$$this"]}
}
},
movieTiming: "$_id.movieTiming",
movieId: "$_id.movieId",
movieDate: "$_id.movieDate",
_id: 0
}
}
])
See how it works on the playground example
Another option is using $unwind instead of $reduce, but it is generally considered slower:
db.collection.aggregate([
{
$match: {
$and: [
{movieId: "62af1ff6cb38656a4ffe36aa"},
{movieTiming: "1:30 p.m"},
{movieDate: "2022-06-20T18:14:38.133+00:00"},
]
}
},
{$unwind: "$seatSeletion"},
{
$group: {
_id: {movieTiming: "$movieTiming", movieId: "$movieId", movieDate: "$movieDate"},
seatSeletion: {$push: "$seatSeletion"}
}
},
{
$project: {
seatSeletion: 1,
movieTiming: "$_id.movieTiming",
movieId: "$_id.movieId",
movieDate: "$_id.movieDate",
_id: 0
}
}
])
See how it works on the playground example - unwind
more output nearly you expect
{
"_id": {
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T18:14:38.133+00:00",
"movieTiming": "1:30 p.m"
},
"seatSeletion": [
{ "rowNo": 0,"columnNo": 0,"seatNo": 3
},
{ "rowNo": 0,"columnNo": 1,"seatNo": 4
},
{ "rowNo": 0,"columnNo": 0,"seatNo": 1
},
{ "rowNo": 0,"columnNo": 1,"seatNo": 2
}
]
}
query
db.collection.aggregate(
{
$match: {}
},
{
$unwind: {
path: '$seatSeletion'
}
},
{
$group: {
_id:
{
movieId: '$movieId',
movieDate: '$movieDate',
movieTiming: '$movieTiming'
},
seatSeletion:
{ $push: '$seatSeletion' }
}
}
)

Mongodb aggregation and nested grouping

I have the following MongoDB data model:
{
"_id": {
"$oid": "5ffd62eedf2075dfc5a5b0b8"
},
"portfolio": "4086_ClearCreek",
"ruleDescription": "Maximum Moody's Rating Factor Test",
"failureLevel": 3,
"failureCategory": "",
"hasHoldings": true,
"summaryStatus": 0,
"summaryStatusLabel": "Failure",
"ruleType": 1,
"ruleSource": 0,
"ruleValueType": 0,
"testValue": "3673",
"limitValue": "2400",
"limitOperator": "<=",
"testRoom": "-1273",
"numeratorValue": "185278281.19",
"denominatorValue": "680407923070.46",
"ruleCategory": "Collateral Quality",
"topLevelFilter": {
"id": "5fd1bd7868d7ac4e211a7642",
"type": "WSO CMP Dataset",
"name": "Clear Creek CLO, LTD._Clear Creek Test Data Set_Initial"
},
"executionDateTime": "2021-01-12T08:50:54.103"
}
I want to see results like this:-
{
"_id" : "Concentration Limitations",
"pass" : 1,
"warn" : 0,
"fail" : 0,
"portfolio" : [
{
"id" : "5fd1bd7868d7ac4e211a7642",
"name" : "Clear Creek CLO, LTD._Clear Creek Test Data Set_Initial",
"pass" : 1,
"warn" : 0,
"fail" : 0
}
]
}
This is my code that i am trying to achive the above result set:
db.rule_execution_result.aggregate([{
$group: {
_id: '$ruleCategory',
pass: {
$sum: {
$cond: [{
$eq: ["$summaryStatus", 1]
}, 1, 0]
}
},
warn: {
$sum: {
$cond: [{
$eq: ["$summaryStatus", 2]
}, 1, 0]
}
},
fail: {
$sum: {
$cond: [{
$eq: ['$summaryStatus', 0]
}, 1, 0]
}
},
portfolio: {
$push: {
id: '$$ROOT.topLevelFilter.id',
name: '$$ROOT.topLevelFilter.name',
category: '$$ROOT.ruleCategory',
summary:'$$ROOT.summaryStatus',
pass: '',
warn: '',
fail: ''
}
}
}
}
]).pretty()
I want to make one another nested group for portfolio on the basis of topLevelFilter.id and calculate sum of summaryStatus, but unable to do so.
So Can anyone please help me to achive the result set?.
first $group by both ruleCategory and topLevelFilter.id, get required count and fields
second $group by ruleCategory and sum counts and make array of portfolio
db.collection.aggregate([
{
$group: {
_id: {
ruleCategory: "$ruleCategory",
topLevelFilter: "$topLevelFilter.id"
},
name: { $first: "$topLevelFilter.name" },
summaryStatus: { $first: "$summaryStatus" },
pass: { $sum: { $cond: [{ $eq: ["$summaryStatus", 1] }, 1, 0] } },
warn: { $sum: { $cond: [{ $eq: ["$summaryStatus", 2] }, 1, 0] } },
fail: { $sum: { $cond: [{ $eq: ["$summaryStatus", 0]}, 1, 0] } }
}
},
{
$group: {
_id: "$_id.ruleCategory",
pass: { $sum: "$pass" },
warn: { $sum: "$warn" },
fail: { $sum: "$fail" },
portfolio: {
$push: {
id: "$_id.topLevelFilter",
name: "$name",
summary: "$summaryStatus",
pass: "$pass",
warn: "$warn",
fail: "$fail"
}
}
}
}
])
Playground

How to get argmax/argmin of multiple fields simultaneously in mongodb?

Here's the data example I'm working with.
[
{
"uid": "111",
"a": 1,
"b": 3,
"c": 1,
},
{
"uid": "222",
"a": 2,
"b": 2,
"c": 2
},
{
"uid": "333",
"a": 3,
"b": 1,
"c": 3
}
]
Then I want to perform argmax on fields "a" and "b", and argmin on field "c" and return the "uid" as the result.
For example:
For "a", it's maximum value is 3, the corresponding "uid" is "333", so argmax of "a" should be "uid" : "333".
The question is what query should be executed so that I can get the result as below?
[
{
"argmax_of_a": "333",
"argmax_of_b": "111",
"argmin_of_c": "111",
}
]
Here's the code snipped I'm playing with https://mongoplayground.net/p/gEDuHd-aCiZ
I can find someway to get argmax/argmin of one specific field, but I have no idea how to work on multiple fields simultaneously.
Thanks in advance!
give this aggreation pipeline a try:
db.collection.aggregate(
[
{
$group: {
_id: null,
a: { $push: { uid: '$uid', val: '$a' } },
b: { $push: { uid: '$uid', val: '$b' } },
c: { $push: { uid: '$uid', val: '$c' } }
}
},
{
$project: {
_id: 0,
max_of_a: { $arrayElemAt: ["$a", { $indexOfArray: ["$a.val", { $max: '$a.val' }] }] },
max_of_b: { $arrayElemAt: ["$b", { $indexOfArray: ["$b.val", { $max: '$b.val' }] }] },
max_of_c: { $arrayElemAt: ["$c", { $indexOfArray: ["$c.val", { $max: '$c.val' }] }] }
}
},
{
$project: {
arg_max_of_a: '$max_of_a.uid',
arg_max_of_b: '$max_of_b.uid',
arg_max_of_c: '$max_of_c.uid'
}
}
])

How to group data by every hour

How do I get counts data grouped by every hour in 24 hours even if data is not present i.e. IF 0 will select 0
MonogDB 3.6
Input
[
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced4b"),
"date": "2019-05-03T10:39:53.108Z",
"id": 166,
"update_at": "2019-05-03T02:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced4c"),
"date": "2019-05-03T10:39:53.133Z",
"id": 166,
"update_at": "2019-05-03T02:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced4d"),
"date": "2019-05-03T10:39:53.180Z",
"id": 166,
"update_at": "2019-05-03T20:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced7a"),
"date": "2019-05-10T10:39:53.218Z",
"id": 166,
"update_at": "2019-12-04T10:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced7b"),
"date": "2019-05-03T10:39:53.108Z",
"id": 166,
"update_at": "2019-05-05T10:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2cedae"),
"date": "2019-05-03T10:39:53.133Z",
"id": 166,
"update_at": "2019-05-05T10:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2cedad"),
"date": "2019-05-03T10:39:53.180Z",
"id": 166,
"update_at": "2019-05-06T10:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2cedab"),
"date": "2019-05-10T10:39:53.218Z",
"id": 166,
"update_at": "2019-12-06T10:45:36.208Z",
"type": "image"
}
]
Implementation
db.collection.aggregate({
$match: {
update_at: {
"$gte": "2019-05-03T00:00:00.0Z",
"$lt": "2019-05-05T00:00:00.0Z"
},
id: {
"$in": [
166
]
}
}
},
{
$group: {
_id: {
$substr: [
"$update_at",
11,
2
]
},
count: {
"$sum": 1
}
},
},
{
$project: {
_id: 0,
hour: "$_id",
count: "$count"
}
},
{
$sort: {
hour: 1
}
})
Actual Output:
{
"count": 2,
"hour": "02"
},
{
"count": 1,
"hour": "20"
}
My expectation code show 24 hours event data is 0 or null and convert from example "02" as "02 AM" , "13" as "01 PM":
Expected Output
{
"count": 0,
"hour": "01" // 01 AM
},
{
"count": 2,
"hour": "02"
},
{
"count": 0,
"hour": "03"
},
{
"count": 0,
"hour": "04"
},
{
"count": 0,
"hour": "05"
},
{
"count": 1,
"hour": "20" // to 08 pm
}
Try this solution:
Explanation
We group by hour to count how many images are uploaded.
Then, we add extra field hour to create time interval (if you had v4.x, there is a better solution).
We flattern hour field (will create new documents) and split first 2 digits to match count and split last 2 digits to put AM / PM periods.
db.collection.aggregate([
{
$match: {
update_at: {
"$gte": "2019-05-03T00:00:00.0Z",
"$lt": "2019-05-05T00:00:00.0Z"
},
id: {
"$in": [
166
]
}
}
},
{
$group: {
_id: {
$substr: [
"$update_at",
11,
2
]
},
count: {
"$sum": 1
}
}
},
{
$addFields: {
hour: [
"0000",
"0101",
"0202",
"0303",
"0404",
"0505",
"0606",
"0707",
"0808",
"0909",
"1010",
"1111",
"1212",
"1301",
"1402",
"1503",
"1604",
"1705",
"1806",
"1907",
"2008",
"2109",
"2210",
"2311"
]
}
},
{
$unwind: "$hour"
},
{
$project: {
_id: 0,
hour: 1,
count: {
$cond: [
{
$eq: [
{
$substr: [
"$hour",
0,
2
]
},
"$_id"
]
},
"$count",
0
]
}
}
},
{
$group: {
_id: "$hour",
count: {
"$sum": "$count"
}
}
},
{
$sort: {
_id: 1
}
},
{
$project: {
_id: 0,
hour: {
$concat: [
{
$substr: [
"$_id",
2,
2
]
},
{
$cond: [
{
$gt: [
{
$substr: [
"$_id",
0,
2
]
},
"12"
]
},
" PM",
" AM"
]
}
]
},
count: "$count"
}
}
])
MongoPlayground
There's no "magic" solution, you'll have to hardcode it into your aggregation:
Heres an example using Mongo v3.2+ syntax with some $map and $filter magic:
db.collection.aggregate([
{
$match: {
update_at: {
"$gte": "2019-05-03T00:00:00.0Z",
"$lt": "2019-05-05T00:00:00.0Z"
},
id: {"$in": [166]}
}
},
{
$group: {
_id: {$substr: ["$update_at", 11, 2]},
count: {"$sum": 1}
}
},
{
$group: {
_id: null,
hours: {$push: {hour: "$_id", count: "$count"}}
}
},
{
$addFields: {
hours: {
$map: {
input: {
$concatArrays: [
"$hours",
{
$map: {
input: {
$filter: {
input: ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"],
as: "missingHour",
cond: {
$not: {
$in: [
"$$missingHour",
{
$map: {
input: "$hours",
as: "hourObj",
in: "$$hourObj.hour"
}
}
]
}
}
}
},
as: "missingHour",
in: {hour: "$$missingHour", count: 0}
}
}
]
},
as: "hourObject",
in: {
count: "$$hourObject.count",
hour: {
$cond: [
{$eq: [{$substr: ["$$hourObject.hour", 0, 1]}, "0"]},
{$concat: ["$$hourObject.hour", " AM"]},
{
$concat: [{
$switch: {
branches: [
{case: {$eq: ["$$hourObject.hour", "13"]}, then: "1"},
{case: {$eq: ["$$hourObject.hour", "14"]}, then: "2"},
{case: {$eq: ["$$hourObject.hour", "15"]}, then: "3"},
{case: {$eq: ["$$hourObject.hour", "16"]}, then: "4"},
{case: {$eq: ["$$hourObject.hour", "17"]}, then: "5"},
{case: {$eq: ["$$hourObject.hour", "18"]}, then: "6"},
{case: {$eq: ["$$hourObject.hour", "19"]}, then: "7"},
{case: {$eq: ["$$hourObject.hour", "20"]}, then: "8"},
{case: {$eq: ["$$hourObject.hour", "21"]}, then: "9"},
{case: {$eq: ["$$hourObject.hour", "22"]}, then: "10"},
{case: {$eq: ["$$hourObject.hour", "23"]}, then: "11"},
],
default: "None"
}
}, " PM"]
}
]
}
}
}
}
}
},
{
$unwind: "$hours"
},
{
$project: {
_id: 0,
hour: "$hours.hour",
count: "$hours.count"
}
},
{
$sort: {
hour: 1
}
}
]);
A short explanation of the $addFields stage: we first add hours that we're missing, we then merge the two arrays (of the original found hours and the "new" missing hours), finally we convert to the required output ("01" to "01 AM").
If you're using Mongo v4+ I recommend you change the $group _id stage to use $dateFromString as its more consistent.
_id: {$hour: {$dateFromString: {dateString: "$update_at"}}}
If you do do that, you'll have to update the $filter and $map section to use numbers and not strings and eventually using $toString to cast into the format you want, hence the v4+ requirement.
You should store date values as Date objects instead of strings. I would do the formatting like this:
db.collection.aggregate(
[
{ $match: { ... } },
{
$group: {
_id: { h: { $hour: "$update_at" } },
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
hour: {
$switch: {
branches: [
{ case: { $lt: ["$_id.h", 10] }, then: { $concat: ["0", { $toString: "$_id.h" }, " AM"] } },
{ case: { $lt: ["$_id.h", 13] }, then: { $concat: [{ $toString: "$_id.h" }, " AM"] } },
{ case: { $lt: ["$_id.h", 22] }, then: { $concat: ["0", { $toString: { $subtract: ["$_id.h", 12] } }, " PM"] } },
{ case: { $lt: ["$_id.h", 24] }, then: { $concat: [{ $toString: { $subtract: ["$_id.h", 12] } }, " PM"] } }
]
}
},
hour24: "$_id.h",
count: 1
}
},
{ $sort: { hour24: 1 } }
])
As non-American I am not familiar with AM/PM rules, esp. for midnight and midday but I guess you get the principle.
Here is the query you can test it out, for MongoDB 4.0+
i will be improving query and update
const query = [{
$match: {
update_at: {
"$gte": ISODate("2019-05-03T00:00:00.0Z"),
"$lt": ISODate("2019-05-05T00:00:00.0Z")
},
id: {
"$in": [
166
]
}
}
},
{
$group: {
_id: { $hour: "$update_at" },
count: {
"$sum": 1
}
},
},
{
$addFields: {
hourStr: { $toString: { $cond: { if: { $gte: ["$_id", 12] }, then: { $subtract: [12, { $mod: [24, '$_id'] }] }, else: "$_id" } } },
}
},
{
$project: {
formated: { $concat: ["$hourStr", { $cond: { if: { $gt: ["$_id", 12] }, then: " PM", else: " AM" } }] },
count: "$count",
hour: 1,
}
}]
If you want to output in Indian Time formate. then below code work!
const query = [
{
$match: {
update_at: {
"$gte": ISODate("2019-05-03T00:00:00.0Z"),
"$lt": ISODate("2019-05-05T00:00:00.0Z")
},
id: {
"$in": [
166
]
}
}
},
{
$project: {
"h": { "$hour": { date: "$update_at", timezone: "+0530" } },
}
},
{
$group:
{
_id: { $hour: "$h" },
count: { $sum: 1 }
}
}
];

MongoDB aggregating multiple arrays of objects based on shared key

I'm writing a query to calculate multiple metrics for each user in my DB.
I've calculated all of the metrics, and have a structure like this
{
"metric1": [{"user_id": 1, "val": 13},{"user_id": 2, "val": 100}],
"metric2": [{"user_id": 2, "val": 29},{"user_id": 1, "val": 123}],
"metric3": [{"user_id": 1, "val": 46},{"user_id": 2, "val": 111]
}
I'm trying to convert the above into this structure
{
"user_id": [1,2],
"metric1": [13, 100],
"metric2": [29,123],
"metric3": [46,111]
}
So that I can display a table showing each user and the three metrics (one metric per column, and one user per row).
considering that your data is what you've said:
{
"metric1": [
{"id1": 1}, {"id2": 2}
],
"metric2": [
{"id2": 22}, {"id1": 11}
],
"metric3": [
{"id2": 222}, {"id1": 111}
]
}
all you've to do is using $unwind to be able to break the array and then $objectToArray to have access to keys
db.blah.aggregate([
{ $unwind: '$metric1' },
{ $unwind: '$metric2' },
{ $unwind: '$metric3' },
{ $project: {'metric1': { $objectToArray: '$metric1' }, 'metric2': { $objectToArray: '$metric2' }, 'metric3': { $objectToArray: '$metric3' }} },
{ $sort: { 'metric1.k' : -1} },
{ $sort: { 'metric2.k' : -1} },
{ $sort: { 'metric3.k' : -1} },
{ $unwind: '$metric1' },
{ $unwind: '$metric2' },
{ $unwind: '$metric3' },
{ $group: {
_id: null,
user_id: { $addToSet: '$metric1.k' },
metric1: { $addToSet: '$metric1.v' },
metric2: { $addToSet: '$metric2.v' },
metric3: { $addToSet: '$metric3.v' },
} },
{ $project: { _id: 0 } }
]).pretty()
which results
{
"user_id" : [
"id1",
"id2"
],
"metric1" : [
1,
2
],
"metric2" : [
11,
22
],
"metric3" : [
111,
222
]
}