so i have document for users with this structure in JSON format:
[
{
"_id": {
"$oid": "6369aeb83ce0f8168520f42f"
},
"fullname": "Jokona",
"password": "$2b$10$MUAe7XIc/xtJTGVh/y1DeuShCARbwxCSejUbHaqIPZfjekNrn0.Yy",
"NIK": "MT220047",
"status": "active",
"department": "Logistic",
"position": "Management Trainee",
"Group_Shift": "Non Shift",
"role": "admin",
"createdAt": 1667870392,
"updatedAt": 1668564835,
"__v": 0
},
{
"_id": {
"$oid": "6369b17b11e02557349d8de5"
},
"fullname": "Warana",
"password": "$2b$10$0xaqz5V8bar/osWmsCiofet5bY10.ORn8Vme3QC7Dh0HwLHwYOm3a",
"NIK": "17000691",
"status": "active",
"department": "Production",
"position": "Foreman",
"Group_Shift": "R1",
"role": "user",
"__v": 0,
"createdAt": 1667871099,
"updatedAt": 1668496775
},
]
it try to lookitup using mongodb $lookup to get the fullname by joining using the NIK as the foreignnkey,here is what i have try:
const dataAnaylitics = await Answer.aggregate([
// $match stage
{
$group: {
_id: {
username: "$username",
title: "$title",
date: "$date",
},
count: {
$sum: 1,
},
position: {
$first: "$position",
},
department: {
$first: "$department",
},
},
},
{
$lookup: {
from: "users",
localField: "username",
foreignField: "NIK",
as: "fullname",
pipeline: [{ $project: { fullname: 0 } }],
},
},
{
$group: {
_id: {
username: "$_id.username",
title: "$_id.title",
},
dates: {
$push: {
k: "$_id.date",
v: "$count",
},
},
position: {
$first: "$position",
},
department: {
$first: "$department",
},
},
},
{
$project: {
_id: 0,
username: "$_id.username",
title: "$_id.title",
position: 1,
department: 1,
dates: 1,
},
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$$ROOT",
{
$arrayToObject: "$dates",
},
],
},
},
},
{
$unset: "dates",
},
]);
but the result doesnt returning the fullname field, is there is something wrong with my code? i seek for documentation and already follow the step
In your group stage, since you are grouping based on username, the resulting document will have _id.username as the field. Use this field as localField in your lookup.
{
$lookup: {
from: "users",
localField: "_id.username",
foreignField: "NIK",
as: "fullname",
pipeline: [{ $project: { fullname: 0 } }],
}
i have fix it, hope it will helps other..
const dataAnaylitics = await Answer.aggregate([
// $match stage
{
$group: {
_id: {
username: "$username",
title: "$title",
date: "$date",
},
count: {
$sum: 1,
},
position: {
$first: "$position",
},
department: {
$first: "$department",
},
},
},
{
$lookup: {
from: "users",
localField: "_id.username",
foreignField: "NIK",
as: "fullname",
pipeline: [{ $project: { _id: 0, fullname: 1 } }],
},
},
{
$group: {
_id: {
username: "$_id.username",
title: "$_id.title",
},
dates: {
$push: {
k: "$_id.date",
v: "$count",
},
},
position: {
$first: "$position",
},
department: {
$first: "$department",
},
fullname: {
$first: { $arrayElemAt: ["$fullname.fullname", 0] },
},
},
},
{
$project: {
_id: 0,
username: "$_id.username",
title: "$_id.title",
position: 1,
department: 1,
dates: 1,
fullname: 1,
},
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$$ROOT",
{
$arrayToObject: "$dates",
},
],
},
},
},
{
$unset: "dates",
},
]);
Related
I have these two aggregations, as shown below.
The first one returns the clients that have zero visits(no visit objects created for the client).
The second one returns the clients with less visits than the others(at least 5).
I want to combine these two aggregations results into one so that they will be ordered like this:
[ no visits clients,
least visits clients ]
Is that possible without simply using Array concat method?
these two aggregations:
let clients = await clientModel.aggregate([
{
$lookup: {
from: "visits",
localField: "_id",
foreignField: "client",
as: "visits",
},
},
{
$project: {
_id: 1,
name: 1,
count: {
$size: "$visits",
},
},
},
{
$match: {
count: 0,
},
},
{
$project: {
_id: 1,
name: 1,
},
},
]);
with this result :
"Zero visits": [
{
"_id": "6182ebe5ea218257521cdc36",
"name": "cleint_807"
},
{
"_id": "6182ebfaea218257521cdc9a",
"name": "cleint_907"
},
{
"_id": "6182ec02ea218257521cdcbe",
"name": "cleint_943"
},
{
"_id": "6182ec20ea218257521cdd48",
"name": "cleint_71"
},
{
"_id": "6182ec29ea218257521cdd74",
"name": "cleint_115"
},
{
"_id": "6182ec54ea218257521cde5a",
"name": "cleint_345"
},
{
"_id": "6182ec61ea218257521cdea3",
"name": "cleint_418"
},
{
"_id": "6182ec71ea218257521cdef4",
"name": "cleint_499"
},
{
"_id": "6182ec96ea218257521cdfbc",
"name": "cleint_699"
}
],
Second aggregation:
visits = await visitModel.aggregate([
{ $match: { time: { $lte: +to, $gte: +from } } },
{
$project: {
date: {
$toDate: "$time",
},
client: 1,
},
},
{
$project: {
day: {
$dayOfWeek: "$date",
},
client: 1,
},
},
{
$match: {
day: 2,
},
},
{
$group: {
_id: {
client: "$client",
},
count: {
$sum: 1,
},
},
},
{
$sort: {
count: 1,
},
},
{
$limit: 10,
},
{
$lookup: {
from: "clients",
localField: "_id.client",
foreignField: "_id",
as: "client",
},
},
{
$unwind: {
path: "$client",
preserveNullAndEmptyArrays: false,
},
},
{
$project: {
_id: "$client._id",
name: "$client.name",
},
},
]);
with this result :
"Less visits": [
{
"_id": "6182eb73ea218257521cd9f6",
"name": "cleint_231"
},
{
"_id": "6182ebe9ea218257521cdc48",
"name": "cleint_825"
},
{
"_id": "6182ec7dea218257521cdf35",
"name": "cleint_564"
},
{
"_id": "6182ec2cea218257521cdd83",
"name": "cleint_130"
},
{
"_id": "6182ebd6ea218257521cdbe8",
"name": "cleint_729"
},
{
"_id": "6182ebc6ea218257521cdb9c",
"name": "cleint_653"
},
{
"_id": "6182ec0bea218257521cdced",
"name": "cleint_990"
},
{
"_id": "6182ebd3ea218257521cdbd8",
"name": "cleint_713"
},
{
"_id": "6182ec81ea218257521cdf48",
"name": "cleint_583"
},
{
"_id": "6182ec2cea218257521cdd85",
"name": "cleint_132"
}
]
Response
res.json({
success: true,
"Zero visits": clients,
"Less visits": visits,
});
};
I need to combine both results
Use $unionWith:
clientModel.aggregate([
<stage1>, ...
{ $unionWith: { coll: "visits", pipeline: [ <stage1>, ... ] } }
])
I'm trying to count my "$attendance.status" with aggregation mongodb.
I've get my data with relations. then i want to count by relation columns like 'present', 'off', etc.
code
Employee.aggregate([
{
$lookup: {
from: "Attendance",
let: { employeeId: "$_id" },
pipeline: [
{
$match: {
$and: [
{ $expr: { $eq: ["$employeeId", "$$employeeId"] } },
{ isApproved: true },
{
createdAt: {
$gte: startOfMonth.toDate(),
$lte: endOfMonth.toDate(),
},
},
],
},
},
],
as: "attendance",
},
},
{
$project: {
_id: 1,
username: 1,
name: 1,
attendance: 1,
present: { $sum: { $eq: ["$attendance.status", "present"] } },
},
},
]);
But why cannot count my column?
i use $eq, with $sum then count the result. but the result is 0
{
"username": "Ethyl",
"name": "Kuhn",
"id": "614d43cde735f3e601dea165",
"attendance": [
{
"_id": "614d43cde735f3e601dea16f",
"status": "present",
"start": "2021-09-24T03:19:41.645Z",
"employeeId": "614d43cde735f3e601dea165",
"isApproved": true
},
],
"present": 0,
"sick": 0,
"off": 0,
},
I have aggregated my data to give this output.
[
{
"_id": {
"source": "source_1",
"medium": "medium_1",
"campaign": "campaign_1"
},
"visitors": [
{
"_id": "60073f564d6c915237dbe158",
"location": {
"city": "Miami",
"postal": "33177"
}
},
{
"_id": "60073f564d6c915237dbe158",
"location": {
"city": "Miami",
"postal": "33163"
}
}
]
},
{
"_id": {
"source": "source_2",
"medium": "medium_2",
"campaign": "campaign_2"
},
"visitors": [
{
"_id": "60073f564d6c915237dbe158",
"location": {
"city": "Miami",
"postal": "33177"
}
},
{
"_id": "60073f564d6c915237dbe158",
"location": {
"city": "Miami",
"postal": "33162"
}
}
]
}
]
I want to group inner visitors array and get this output.
[
{
"_id": {
"source": "source_1",
"medium": "medium_1",
"campaign": "campaign_1"
},
"visitors": [
{
"city": "Miami",
"postal": "33177",
"count": 2
},
{
"city": "Miami",
"postal": "33163",
"count": 5
}
]
},
{
"_id": {
"source": "source_2",
"medium": "medium_2",
"campaign": "campaign_2"
},
"visitors": [
{
"city": "Miami",
"postal": "33177",
"count": 1
},
{
"city": "Miami",
"postal": "33163",
"count": 3
}
]
}
]
aggregate pipeline executed on campaigns collection:
[{$match: {
website_id: 1,
$or: [
{
source:{
$regex:/goo/,
$options: 'i'
}
},
{
medium:{
$regex:/goo/,
$options: 'i'
}
},
{
campaign:{
$regex:/goo/,
$options: 'i'
}
}
]
}}, {$addFields: {
visitor_id: {
$toObjectId: "$visitor_id"
}
}}, {$lookup: {
from: 'visitors',
localField: 'visitor_id',
foreignField: '_id',
as: 'visitors'
}}, {$unwind: {
path: '$visitors'
}}, {$group: {
_id: {
source: '$source',
medium: '$medium',
campaign: '$campaign',
},
visitors:{
$push: '$visitors'
}
}}, {$unwind: {
path: '$visitors'
}}, {$group: {
_id: {
'city': '$visitors.location.city',
'postal': '$visitors.location.postal'
},
'count': {
'$sum': 1
}
}}, {$project: {
'_id': 0,
'city': '$_id.city',
'postal': '$_id.postal',
'count': '$count',
'total': {
'$sum': '$count'
}
}}, {$project: {
'city': '$city',
'postal': '$postal',
'count': '$count',
'total': {
'$sum': '$total'
}
}}]
So the idea is first group the visitors by their postal number along with the campaign details to get the count and then aggregate it by only campaign details to accumulate the visitors.
Try this query:
db.campaigns.aggregate([
{
$match: {
// Put your condtions here.
}
},
{
$project: {
source: 1,
medium: 1,
campaign: 1,
visitor_id: 1
}
},
{
$addFields: {
visitor_id: { $toObjectId: "$visitor_id" }
}
},
{
$lookup: {
from: "visitors",
let: { "visitor_id": "$visitor_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$_id", "$$visitor_id"] }
}
},
{
$project: {
location: {
city: 1,
postal: 1
}
}
}
],
as: "visitor"
}
},
{ $unwind: "$visitor" },
{
$group: {
_id: {
source: "$source",
medium: "$medium",
campaign: "$campaign",
postal: "$visitor.location.postal"
},
visitors: { $push: "$visitor" },
count: { $sum: 1 }
}
},
{
$group: {
_id: {
source: "$_id.source",
medium: "$_id.medium",
campaign: "$_id.campaign"
},
visitors: {
$push: {
city: { $arrayElemAt: ["$visitors.location.city", 0] },
postal: { $arrayElemAt: ["$visitors.location.postal", 0] },
count: "$count"
}
}
}
}
]);
You need to correct group stage,
$group by source, medium, campaign and postal, get first city and count total sum
$group by source, medium, campaign and construct visitors array with required fields
db.campaigns.aggregate([
{ $match: .. } //skipped
{ $addFields: .. }, //skipped
{ $lookup: .. }, //skipped
{ $unwind: .. }, //skipped
{
$group: {
_id: {
source: "$source",
medium: "$medium",
campaign: "$campaign",
postal: "$visitors.location.postal"
},
city: { $first: "$visitors.location.city" },
count: { $sum: 1 }
}
},
{
$group: {
_id: {
source: "$_id.source",
medium: "$_id.medium",
campaign: "$_id.campaign"
},
visitors: {
$push: {
city: "$city",
postal: "$_id.postal",
count: "$count"
}
}
}
}
])
Playground
I am new to mongoose and mongodb.
In my addtocart schema I have added $lookups and projection to populate products in add to cart.
In current response product_purchase_quantity it was in array in add to cart collection it is key and value. So, in $lookups I tried to add {$unwind: '$product_purchase_quantity'} but after adding add_to_cart_products object prints twice. I don't know where the mistake was?
Below i have mentioned expected result.
AddToCart Schema:
lookups: [
{
from: 'shop_db_products',
let: {
productId: '$add_to_cart_products.product',
purchaseQuantity: '$add_to_cart_products.product_quantity',
productItemId: '$add_to_cart_products.product_item',
},
pipeline: [
{
$match: { $expr: { $in: ['$_id', '$$productId'] } },
},
{
$lookup: {
from: 'shop_db_products',
localField: 'product_id',
foreignField: '_id',
as: 'products',
},
},
{
$project: {
_id: true,
product: {
_id: '$_id',
product_name: '$product_name',
},
product_purchase_quantity: '$$purchaseQuantity',
product_item: {
$reduce: {
input: {
$filter: {
input: '$product_items',
cond: {
$in: ['$$this._id', '$$productItemId'],
},
},
},
initialValue: {},
in: {
_id: '$$this._id',
product_size: { $concat: [{ $toString: '$$this.product_size.value' }, '$$this.product_size.unit'] },
product_price: '$$this.product_price',
product_type: '$$this.product_type'
},
},
},
},
},
],
localField: '',
as: 'add_to_cart_products',
model: 'ProductModel',
},
],
AddToCart Collection
[
{
"add_to_cart_user": "5f0076b7bd530928fc0c0285",
"add_to_cart_products": [
{
"product": "5f05a0270b4f3a5c41c70826",
"product_item": "5f05a0270b4f3a5c41c70877",
"product_quantity": 5
},
{
"product": "5f05a0270b4f3a5c41c70827",
"product_item": "5f05a0270b4f3a5c41c70666",
"product_quantity": 3
}
],
"add_to_cart_product_total": 5,
"add_to_cart_discount": 50,
"add_to_tax": "5eae321d21924800122f978e",
"add_to_cart_grand_total": 500
}
]
Current Response:
{
"_id": "5fa2a09b3c6316482098f6ff",
"add_to_cart_status_is_active": true,
"add_to_cart_discount": 50,
"add_to_cart_tax": 8,
"add_to_cart_products": [
{
"product": {
"_id": "5f05a0270b4f3a5c41c70826",
"product_name": "Avery Apricot Sour"
},
"product_purchase_quantity": [
5,
3
],
"product_item": {
"_id": "5f05a0270b4f3a5c41c70877",
"product_size": "22oz",
"product_price": 13.99
}
},
{
"product": {
"_id": "5f05a0270b4f3a5c41c70827",
"product_name": "Avery Dugana"
},
"product_purchase_quantity": [
5,
3
],
"product_item": {
"_id": "5f05a0270b4f3a5c41c70666",
"product_size": "22oz",
"product_price": 8.99
}
}
]
}
Expected response:
{
"_id": "5fa2a09b3c6316482098f6ff",
"add_to_cart_status_is_active": true,
"add_to_cart_discount": 50,
"add_to_cart_tax": 8,
"add_to_cart_products": [
{
"product": {
"_id": "5f05a0270b4f3a5c41c70826",
"product_name": "Avery Apricot Sour"
},
"product_purchase_quantity": 5,
"product_item": {
"_id": "5f05a0270b4f3a5c41c70877",
"product_size": "22oz",
"product_price": 13.99
}
},
{
"product": {
"_id": "5f05a0270b4f3a5c41c70827",
"product_name": "Avery Dugana"
},
"product_purchase_quantity": 3,
"product_item": {
"_id": "5f05a0270b4f3a5c41c70666",
"product_size": "22oz",
"product_price": 8.99
}
}
]
}
Product (shop_db_products) collection:
[
{
"_id": "5f05a0270b4f3a5c41c70826",
"product_no": "PRO04087",
"product_store_no": "1001",
"product_dept_no": "Irish Whiskey",
"product_name": "2Gingers",
"product_overview": "No Overview",
"product_items": [
{
"_id": "5f05a0270b4f3a5c41c70877",
"product_item_number": "857566003019",
"product_price": 20.99,
"product_cost": 20.99,
"product_size": "750ml",
"product_type": "Bottle",
"product_value": 0,
"product_quantity": 0,
"product_images": [
"pro04087-857566003019-1.png"
],
"product_item_is_active": true
}
]
},
...
]
You have to $unwind add_to_cart_products before performing $lookup
db.addToCart.aggregate([
{
$unwind: "$add_to_cart_products"
},
{
$lookup: {
from: "shop_db_products",
let: {
productId: "$add_to_cart_products.product",
purchaseQuantity: "$add_to_cart_products.product_quantity",
productItemId: "$add_to_cart_products.product_item"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$_id",
"$$productId"
]
}
}
},
{
$project: {
_id: true,
product: {
_id: "$_id",
product_name: "$product_name",
},
product_purchase_quantity: "$$purchaseQuantity",
product_item: {
$reduce: {
input: {
$filter: {
input: "$product_items",
cond: {
$eq: [
"$$this._id",
"$$productItemId"
],
},
},
},
initialValue: {},
in: {
_id: "$$this._id",
product_size: "$$this.product_size",
product_price: "$$this.product_price",
product_type: "$$this.product_type"
}
}
}
}
}
],
as: "add_to_cart_products"
}
},
{
$unwind: "$add_to_cart_products"
},
{
$group: {
_id: "$_id",
"add_to_cart_products": {
$push: "$add_to_cart_products"
}
}
}
])
MongoDB Playground
Another way
Without reducer, with $unwind
https://mongoplayground.net/p/3uWA5pVBv83
db.addToCart.aggregate([
{
"$unwind": "$add_to_cart_products"
},
{
"$lookup": {
"from": "shop_db_products",
"let": {
"productId": "$add_to_cart_products.product",
"purchaseQuantity": "$add_to_cart_products.product_quantity",
"productItemId": "$add_to_cart_products.product_item"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$productId"
]
}
}
},
{
"$unwind": "$product_items"
},
{
"$match": {
"$expr": {
"$eq": [
"$product_items._id",
"$$productItemId"
]
}
}
},
{
"$project": {
"_id": true,
"product": {
"_id": "$_id",
"product_name": "$product_name"
},
"product_purchase_quantity": "$$purchaseQuantity",
"product_item": "$product_items"
}
}
],
"as": "productResolved"
}
},
{
"$unwind": "$productResolved"
},
{
"$group": {
"_id": "$_id",
"add_to_cart_products": {
"$push": "$productResolved"
}
}
}
])
We are using MongoDB to record statistics. The approach is to record each action for an object in its own document and later aggregate them on hourly basis and store them in different collection. Sample documents are below:
[{
"_id" : ObjectId("5e05de1e86029610dc2c6f9c"),
"object_type" : 1,
"object_id" : 1003,
"browser" : "chrome",
"os" : "osx",
"device" : "android",
"category" : 3,
"country" : "gb",
"action" : "impression",
"date_added" : ISODate("2019-12-26T19:00:00.000Z")
},{
"_id" : ObjectId("5e06226586029610db417b7a"),
"object_type" : 1,
"object_id" : 1003,
"browser" : "firefox",
"os" : "osx",
"device" : "lg_tv",
"category" : 1,
"country" : "pe",
"action" : "impression",
"date_added" : ISODate("2019-12-25T19:00:00.000Z")
},{
"_id" : ObjectId("5e06226586029610db417b7b"),
"object_type" : 1,
"object_id" : 1009,
"browser" : "uc_browser",
"os" : "osx",
"device" : "android",
"category" : 4,
"country" : "ru",
"action" : "view",
"date_added" : ISODate("2019-12-25T19:00:00.000Z")
}]
Output should be:
[{
"object_id": 1003,
"object_type": 1,
"action": "impression",
"total": 2,
"date": "2019-12-26 19:00:00",
"browsers": { "firefox": 1, "chrome": 1 },
"systems": { "osx": 2 },
"countries": { "gb": 1, "pe": 1 },
"devices": { "android": 1, "lg_tv": 1 },
"categories": { "3": 1, "1": 1 }
},
{
"object_id": 1009,
"object_type": 1,
"action": "view",
"total": 1,
"date": "2019-12-26 19:00:00",
"browsers": { "uc_browser": 1 },
"systems": { "osx": 1 },
"countries": { "ru": 1 },
"devices": { "android": 1 },
"categories": { "4": 1 }
}]
Aggregation pipeline:
[
{
"$match": {
"date_added": {
"$gte": {
"$date": {
"$numberLong": "1576820825000"
}
}
}
}
},
{
"$group": {
"_id": {
"object_id": "$object_id",
"object_type": "$object_type",
"action": "$action",
"date": {
"$dateToString": {
"format": "%Y-%m-%d %H-00-00",
"date": "$date_added"
}
}
},
"total": {
"$sum": 1
},
"countries": {
"$push": "$country"
}
}
},
{
"$project": {
"action": "$_id.action",
"object_id": "$_id.object_id",
"object_type": "$_id.object_type",
"date": "$_id.date",
"total": 1,
"countries": 1,
"systems": 1,
"devices": 1,
"categories": 1,
"_id": 0
}
},
{
"$sort": {
"total": -1
}
}
]
This pipeline provides total of an object for a certain action on given hour and push each country into countries array - for readability removed other indexes from $group.
I’m stuck at transforming countries array into desired object. Two question popped in my mind.
Is it possible with single aggregation pipeline?
Should I just return documents using above pipeline and process rest of the indexes with scripting?
It's possible, but a bit tedious...
You need to $group each new field in the next stage and acumulate previous fields.
ASSUMPTION
Your expected result for "object_id": 1003 with total:2, but date_added is 2019-12-26 and 2019-12-25. So, I've changed to 2019-12-26 both documents.
db.collection.aggregate([
{
"$match": {
"date_added": {
"$gte": {
"$date": {
"$numberLong": "1576820825000"
}
}
}
}
},
{
$group: {
_id: {
"object_id": "$object_id",
"object_type": "$object_type",
"action": "$action",
"date": {
"$dateToString": {
"format": "%Y-%m-%d %H-00-00",
"date": "$date_added",
timezone: "GMT"
}
}
},
data: {
"$push": "$$ROOT"
},
total: {
$sum: 1
}
}
},
{
$unwind: "$data"
},
{
$group: {
_id: {
_id: "$_id",
"tmp": "$data.category"
},
data: {
$push: "$data"
},
total: {
$first: "$total"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: "$_id._id",
data: {
$push: "$data"
},
total: {
$first: "$total"
},
categories: {
$push: {
k: {
$toString: "$_id.tmp"
},
v: "$count"
}
}
}
},
{
$unwind: "$data"
},
{
$unwind: "$data"
},
{
$group: {
_id: {
_id: "$_id",
"tmp": "$data.device"
},
categories: {
$first: "$categories"
},
data: {
$push: "$data"
},
total: {
$first: "$total"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: "$_id._id",
data: {
$push: "$data"
},
total: {
$first: "$total"
},
categories: {
$first: "$categories"
},
devices: {
$push: {
k: "$_id.tmp",
v: "$count"
}
}
}
},
{
$unwind: "$data"
},
{
$unwind: "$data"
},
{
$group: {
_id: {
_id: "$_id",
"tmp": "$data.country"
},
devices: {
$first: "$devices"
},
categories: {
$first: "$categories"
},
data: {
$push: "$data"
},
total: {
$first: "$total"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: "$_id._id",
data: {
$push: "$data"
},
total: {
$first: "$total"
},
devices: {
$first: "$devices"
},
categories: {
$first: "$categories"
},
countries: {
$push: {
k: "$_id.tmp",
v: "$count"
}
}
}
},
{
$unwind: "$data"
},
{
$unwind: "$data"
},
{
$group: {
_id: {
_id: "$_id",
"tmp": "$data.os"
},
countries: {
$first: "$countries"
},
devices: {
$first: "$devices"
},
categories: {
$first: "$categories"
},
data: {
$push: "$data"
},
total: {
$first: "$total"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: "$_id._id",
data: {
$push: "$data"
},
total: {
$first: "$total"
},
countries: {
$first: "$countries"
},
devices: {
$first: "$devices"
},
categories: {
$first: "$categories"
},
systems: {
$push: {
k: "$_id.tmp",
v: "$count"
}
}
}
},
{
$unwind: "$data"
},
{
$unwind: "$data"
},
{
$group: {
_id: {
_id: "$_id",
"tmp": "$data.browser"
},
systems: {
$first: "$systems"
},
countries: {
$first: "$countries"
},
devices: {
$first: "$devices"
},
categories: {
$first: "$categories"
},
data: {
$push: "$data"
},
total: {
$first: "$total"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: "$_id._id",
data: {
$push: "$data"
},
total: {
$first: "$total"
},
systems: {
$first: "$systems"
},
countries: {
$first: "$countries"
},
devices: {
$first: "$devices"
},
categories: {
$first: "$categories"
},
browsers: {
$push: {
k: "$_id.tmp",
v: "$count"
}
}
}
},
{
$project: {
_id: 0,
action: "$_id.action",
date: "$_id.date",
object_id: "$_id.object_id",
object_type: "$_id.object_type",
total: 1,
categories: {
$arrayToObject: "$categories"
},
countries: {
$arrayToObject: "$countries"
},
devices: {
$arrayToObject: "$devices"
},
systems: {
$arrayToObject: "$systems"
},
browsers: {
$arrayToObject: "$browsers"
}
}
},
{
$sort: {
object_id: 1,
date: 1
}
}
])
MongoPlayground
Note: Other approach was to use $facet and create fields separately and then merge them into final object, but MongoPlayground sometimes worked buggy (click Run button several times and you get different result)