Note: Mongo version 3.6.2.
I have a document that looks like this:
const Ticket = new mongoose.Schema({
event_id: [
{
required: false,
type: mongoose.Schema.Types.ObjectId,
ref: 'Event'
}
],
ticket_type: String,
createdAt: String,
}, { collection: 'tickets' });
I want to do a mongodb groupBy on ticket_type and createdAt where an event_id = X. So the output should like this:
[{ ticket_type: 'VIP', date: 2011-11-11, count: 12}, {..}]
The hard part is that createdAt is stored as timemillis in a string, ex:
{
_id : ObjectId(123),
ticket_type: 'VIP',
createdAt: '1233434',
event_id: [ObjectId(345)]
}
The answer should look something like this:
Ticket.find({ event_id: req.params.event_id }, function(err, tickCount) {
if (err) {
console.log(err);
} else {
res.json(tickCount);
}
});
Any help is greatly appreciated. Thanks.
This is what I came up with:
Ticket.aggregate([
{ $match: { event_id: ObjectId(req.body.event_id)} },
{
$addFields: {
"createdAt": {
$reduce: {
"input": {
$map: { // split string into char array so we can loop over individual characters
"input": {
$range: [ 0, { $strLenCP: "$createdAt" } ] // using an array of all numbers from 0 to the length of the string
},
"in":{
$substrCP: [ "$createdAt", "$$this", 1 ] // return the nth character as the mapped value for the current index
}
}
},
"initialValue": { // initialize the parser with a 0 value
"n": 0, // the current number
"sign": 1, // used for positive/negative numbers
"div": null, // used for shifting on the right side of the decimal separator "."
"mult": 10 // used for shifting on the left side of the decimal separator "."
}, // start with a zero
"in": {
$let: {
"vars": {
"n": {
$switch: { // char-to-number mapping
branches: [
{ "case": { $eq: [ "$$this", "1" ] }, "then": 1 },
{ "case": { $eq: [ "$$this", "2" ] }, "then": 2 },
{ "case": { $eq: [ "$$this", "3" ] }, "then": 3 },
{ "case": { $eq: [ "$$this", "4" ] }, "then": 4 },
{ "case": { $eq: [ "$$this", "5" ] }, "then": 5 },
{ "case": { $eq: [ "$$this", "6" ] }, "then": 6 },
{ "case": { $eq: [ "$$this", "7" ] }, "then": 7 },
{ "case": { $eq: [ "$$this", "8" ] }, "then": 8 },
{ "case": { $eq: [ "$$this", "9" ] }, "then": 9 },
{ "case": { $eq: [ "$$this", "0" ] }, "then": 0 },
{ "case": { $and: [ { $eq: [ "$$this", "-" ] }, { $eq: [ "$$value.n", 0 ] } ] }, "then": "-" }, // we allow a minus sign at the start
{ "case": { $eq: [ "$$this", "." ] }, "then": "." }
],
default: null // marker to skip the current character
}
}
},
"in": {
$switch: {
"branches": [
{
"case": { $eq: [ "$$n", "-" ] },
"then": { // handle negative numbers
"sign": -1, // set sign to -1, the rest stays untouched
"n": "$$value.n",
"div": "$$value.div",
"mult": "$$value.mult",
},
},
{
"case": { $eq: [ "$$n", null ] }, // null is the "ignore this character" marker
"then": "$$value" // no change to current value
},
{
"case": { $eq: [ "$$n", "." ] },
"then": { // handle decimals
"n": "$$value.n",
"sign": "$$value.sign",
"div": 10, // from the decimal separator "." onwards, we start dividing new numbers by some divisor which starts at 10 initially
"mult": 1, // and we stop multiplying the current value by ten
},
},
],
"default": {
"n": {
$add: [
{ $multiply: [ "$$value.n", "$$value.mult" ] }, // multiply the already parsed number by 10 because we're moving one step to the right or by one once we're hitting the decimals section
{ $divide: [ "$$n", { $ifNull: [ "$$value.div", 1 ] } ] } // add the respective numerical value of what we look at currently, potentially divided by a divisor
]
},
"sign": "$$value.sign",
"div": { $multiply: [ "$$value.div" , 10 ] },
"mult": "$$value.mult"
}
}
}
}
}
}
}
}
}, {
$addFields: { // fix sign
"createdAt": { $multiply: [ "$createdAt.n", "$createdAt.sign" ] }
}
},
{
$group: {
_id: {
ticket_type: "$ticket_type",
createdAt: { $dateToString: { format: "%Y-%m-%d", date: { $add: [ new Date(0), "$createdAt" ] }} },
count: { $sum: 1 }
}
}
},
{ $sort: { "createdAt": 1 } }
], function(err, tickCount) {
if (err) {
console.log(err);
} else {
res.json(tickCount);
}
});
You can use $group aggregation pipeline stage. To convert string to number you can use $toLong operator and then you can use $add (works for ISODate as well) to add that value to the date with zero miliseconds (new Date(0)) to get ISODate. Try:
Ticket.aggregate([
{ $match: { event_id: req.params.event_id } },
{
$group: {
_id: {
ticket_type: "$ticket_type",
createdAt: { $add: [ new Date(0), { $toLong: "$createdAt" } ] }
}
}
}
], function(err, tickCount) {
if (err) {
console.log(err);
} else {
res.json(tickCount);
}
});
EDIT: assuming that you manually converted string to number you can run following query to aggregate on date part of ISODate:
db.col.aggregate([
{ $match: { event_id: req.params.event_id } },
{
$group: {
_id: {
ticket_type: "$ticket_type",
createdAt: { $dateToString: { format: "%Y-%m-%d", date:{ $add: [ new Date(0), "$createdAt" ] } } },
count: { $sum: 1 }
}
}
},
{ $sort: { "_id.createdAt": 1 } }
])
Related
I want to find last the number of maximum consecutive number of days for user id.
userId(string) active_date(string) Note: Today (2022-02-20)
------------------------------------------
{ "userId": "DbdBve", "day": "2022-02-20" }
{ "userId": "DbdBve", "day": "2022-02-19" }
{ "userId": "DbdBve", "day": "2022-02-18" }
{ "userId": "DbdBve", "day": "2022-02-17" } <- Gap here | so user's been active for the last 3 days
{ "userId": "DbdBve", "day": "2022-02-15" }
userId(string) active_date(string)
------------------------------------------
{ "userId": "Gj6WEth", "day": "2022-02-20" }
{ "userId": "Gj6WEth", "day": "2022-02-15" } <- Gap here | so user's been active for the last 1 days
{ "userId": "Gj6WEth", "day": "2022-02-14" }
{ "userId": "Gj6WEth", "day": "2022-02-13" }
using mongodb v5:
first convert date to numeric value using $toLong
then using $setWindowFields extract the user active_days ranges
mongoplayground
db.collection.aggregate([
{
"$addFields": {
"active_date": {
"$toLong": "$active_date"
}
}
},
{
$setWindowFields: {
partitionBy: "$user_id",
sortBy: {
active_date: 1
},
output: {
days: {
$push: "$active_date",
window: {
range: [
-86400000, // one day in millisecond
0
]
}
}
}
}
},
{
"$set": {
"days": {
"$cond": [
{
"$gt": [
{
"$size": "$days"
},
1
]
},
0,
1
]
}
}
},
{
$setWindowFields: {
partitionBy: "$user_id",
sortBy: {
active_date: 1
},
output: {
count: {
$sum: "$days",
window: {
documents: [
"unbounded",
"current"
]
}
}
}
}
},
{
"$group": {
"_id": {
user_id: "$user_id",
count: "$count"
},
"active_days": {
$sum: 1
},
"to": {
"$max": "$active_date"
},
"from": {
"$min": "$active_date"
}
}
}
])
and at the end get the latest active_day range by adding these two stages:
{
"$sort": {
to: -1
}
},
{
"$group": {
"_id": "$_id.user_id",
"last_active_days": {
"$first": "$active_days"
}
}
}
older versions of mongodb using $reduce
mongoplayground
db.collection.aggregate([
{
$sort: {
active_date: 1
}
},
{
"$group": {
_id: "$user_id",
dates: {
"$push": {
"$toLong": "$active_date"
},
},
from: {
$first: {
"$toLong": "$active_date"
}
},
to: {
$last: {
"$toLong": "$active_date"
}
}
}
},
{
$project: {
active_days: {
$let: {
vars: {
result: {
$reduce: {
input: "$dates",
initialValue: {
prev: {
$subtract: [
"$from",
86400000
]
},
range: {
from: "$from",
to: 0,
count: 0
},
ranges: []
},
in: {
$cond: [
{
$eq: [
{
$subtract: [
"$$this",
"$$value.prev"
]
},
86400000
]
},
{
prev: "$$this",
range: {
from: "$$value.range.from",
to: "$$value.range.to",
count: {
$add: [
"$$value.range.count",
1
]
}
},
ranges: "$$value.ranges"
},
{
ranges: {
$concatArrays: [
"$$value.ranges",
[
{
from: "$$value.range.from",
to: "$$value.prev",
count: "$$value.range.count"
}
]
]
},
range: {
from: "$$this",
to: "$to",
count: 1
},
prev: "$$this"
},
]
}
}
}
},
in: {
$concatArrays: [
"$$result.ranges",
[
"$$result.range"
]
]
}
}
}
}
},
{
"$project": {
active_days: {
"$last": "$active_days.count"
}
}
}
])
Similar to another question I had (Here). But now I'm trying to count unique and total events on daily basis for each event type, based on the following data shape:
{
username: "jack",
events: [
{
eventType: "party",
createdAt: "2022-01-23T10:26:11.214Z",
visitorInfo: {
visitorId: "87654321-0ebb-4238-8bf7-87654321"
}
},
{
eventType: "party",
createdAt: "2022-01-23T10:26:11.214Z",
visitorInfo: {
visitorId: "87654321-0ebb-4238-8bf7-87654321"
}
},
{
eventType: "party",
createdAt: "2022-01-23T10:26:11.214Z",
visitorInfo: {
visitorId: "01234567-0ebb-4238-8bf7-01234567"
}
},
{
eventType: "meeting",
createdAt: "2022-01-23T12:26:11.214Z",
visitorInfo: {
visitorId: "87654321-0ebb-4238-8bf7-87654321"
}
},
{
eventType: "meeting",
createdAt: "2022-01-23T11:26:11.214Z",
visitorInfo: {
visitorId: "87654321-0ebb-4238-8bf7-87654321"
}
},
{
eventType: "meeting",
createdAt: "2022-01-23T12:26:11.214Z",
visitorInfo: {
visitorId: "01234567-0ebb-4238-8bf7-01234567"
}
},
{
eventType: "party",
createdAt: "2022-01-30T10:26:11.214Z",
visitorInfo: {
visitorId: "12345678-0ebb-4238-8bf7-12345678"
}
},
{
eventType: "party",
createdAt: "2022-01-30T10:16:11.214Z",
visitorInfo: {
visitorId: "12345678-0ebb-4238-8bf7-12345678"
}
},
{
eventType: "meeting",
createdAt: "2022-01-30T12:36:11.224Z",
visitorInfo: {
visitorId: "12345678-0ebb-4238-8bf7-12345678"
}
},
{
eventType: "meeting",
createdAt: "2022-01-30T11:46:11.314Z",
visitorInfo: {
visitorId: "12345678-0ebb-4238-8bf7-12345678"
}
}
]
}
I'm trying to count events (all and unique ones based on visitorId) on date (daily).
This is what I have so far (thanks to #R2D2's guide on the approach):
Event.aggregate([
{ $match: { username: 'jack' } },
{ $unwind: "$events" },
{
$project: {
totalPartyEvents: {
$cond: [
{
$eq: ["$events.eventType", "party"],
},
1,
0,
],
},
uniquePartyEvents: { // where I'm stuck. I need to count unique events based on visitorId on current date for 'party' event type.
$cond: [
{
$eq: ["$events.eventType", "party"],
},
1,
0,
],
},
totalMeetingEvents: {
$cond: [
{
$eq: ["$events.eventType", "meeting"],
},
1,
0,
],
},
uniqueMeetingEvents: { // do the same for other events. maybe there's a better way to combine these (with facets).
$cond: [
{
$eq: ["$events.eventType", "meeting"],
},
1,
0,
],
},
date: "$events.createdAt",
},
},
{
$group: {
_id: {
$dateToString: { format: "%Y-%m-%d", date: "$date" },
},
totalPartyEvents: {
$sum: "$totalMeetingEvents",
},
uniquePartyEvents: {
$sum: "$totalMeetingEvents",
},
totalMeetingEvents: {
$sum: "$totalMeetingEvents",
},
uniqueMeetingEvents: {
$sum: "$uniqueMeetingEvents",
},
},
},
{
$project: {
date: "$_id",
uniquePartyEvents: 1,
totalPartyEvents: 1,
totalMeetingEvents:1,
uniqueMeetingEvents: 1,
},
},
{
$group: {
_id: "0",
dateAndEventFrequency: {
$push: "$$ROOT",
},
},
},
{
$project: {
_id: 0,
dateAndEventFrequency: 1,
},
},
]);
I tried using $addToSet but it's not used with $project (it works with $group).
Any new approach is welcome based on the data shape and the desired result I'm expecting. I used $project because I was already using it.
Basically what I'm hoping to get in the end:
dateAndEventFrequency: [
{
_id: "2022-01-23",
totalPartyEvents: 3,
uniquePartyEvents: 2,
totalMeetingEvents: 3,
uniqueMeetingEvents: 2,
date: "2022-01-23",
},
{
_id: "2022-01-30",
totalPartyEvents: 2,
uniquePartyEvents: 1,
totalMeetingEvents: 2,
uniqueMeetingEvents: 1,
date: "2022-01-30",
},
]
I'm using Mongoose and Nodejs. Any help or guidance is appreciated. Thanks!
mongo playground
db.collection.aggregate([
{
$match: {
username: "jack"
}
},
{
"$unwind": "$events"
},
{
"$match": {
"events.eventType": {
"$in": [
"meeting",
"party"
]
}
}
},
{
"$group": {
"_id": {
date: {
"$dateToString": {
format: "%Y-%m-%d",
date: "$events.createdAt"
}
},
"visitorId": "$events.visitorInfo.visitorId",
"eventType": "$events.eventType"
},
"count": {
"$sum": 1
}
}
},
{
"$group": {
"_id": {
"date": "$_id.date",
"eventType": "$_id.eventType"
},
"uniqueTotal": {
"$sum": 1
},
total: {
"$sum": "$count"
}
}
},
{
"$group": {
"_id": "$_id.date",
"partyUniqueTotal": {
"$sum": {
"$cond": [
{
$eq: [
"$_id.eventType",
"party"
],
},
"$uniqueTotal",
0
]
}
},
"totalPartyEvents": {
"$sum": {
"$cond": [
{
$eq: [
"$_id.eventType",
"party"
],
},
"$total",
0
]
}
},
"meetingUniqueTotal": {
"$sum": {
"$cond": [
{
$eq: [
"$_id.eventType",
"meeting"
],
},
"$uniqueTotal",
0
]
}
},
"totalmeetingEvents": {
"$sum": {
"$cond": [
{
$eq: [
"$_id.eventType",
"meeting"
],
},
"$total",
0
]
}
}
}
}
])
Consider I have a timesheets collection like this:
[
{
_id: 1,
createdBy: "John",
duration: "00:30"
},
{
_id: 2,
createdBy: "John",
duration: "01:30"
},
{
_id: 3,
createdBy: "Mark",
duration: "00:30"
},
]
My input is an array of usernames:
["John", "Mark", "Rikio"]
I want to use mongodb aggregate to calculate the total duration of timesheets for each user in the usernames array and If there are no timesheets found, it should return duration: "00:00". For example, it should return:
[
{createdBy: "John", totalDuration: "02:00"},
{createdBy: "Mark", totalDuration: "00:30"},
{createdBy: "Rikio", totalDuration: "00:00"}
]
However, when I use $match query, if there are no timesheets it will not return anything so I don't know which user does not have the timesheets and return "00:00" for them.
I totally agree with #turivishal , but still can make it through mongo query with an ugly one.
db.collection.aggregate([
{
$match: {}
},
{
$set: {
minutes: {
$let: {
vars: {
time: {
$split: [
"$duration",
":"
]
}
},
in: {
"$add": [
{
"$multiply": [
{
$toInt: {
$first: "$$time"
}
},
60
]
},
{
$toInt: {
$last: "$$time"
}
}
]
}
}
}
}
},
{
$group: {
"_id": "$createdBy",
"totalMinutes": {
"$sum": "$minutes"
}
}
},
{
$group: {
"_id": null,
"docs": {
"$push": "$$ROOT"
}
}
},
{
$set: {
"docs": {
$map: {
"input": [
"John",
"Mark",
"Rikio"
],
"as": "name",
"in": {
$let: {
vars: {
findName: {
"$first": {
"$filter": {
"input": "$docs",
"as": "d",
"cond": {
"$eq": [
"$$d._id",
"$$name"
]
}
}
}
}
},
in: {
"$cond": {
"if": "$$findName",
"then": "$$findName",
"else": {
_id: "$$name",
totalMinutes: 0
}
}
}
}
}
}
}
}
},
{
$unwind: "$docs"
},
{
$replaceRoot: {
"newRoot": "$docs"
}
},
{
$set: {
"hours": {
$floor: {
"$divide": [
"$totalMinutes",
60
]
}
},
"minutes": {
"$mod": [
"$totalMinutes",
60
]
}
}
},
{
$set: {
"hours": {
"$cond": {
"if": {
"$lt": [
"$hours",
10
]
},
"then": {
"$concat": [
"0",
{
"$toString": "$hours"
}
]
},
"else": {
"$toString": "$hours"
}
}
},
"minutes": {
"$cond": {
"if": {
"$lt": [
"$minutes",
10
]
},
"then": {
"$concat": [
"0",
{
"$toString": "$minutes"
}
]
},
"else": {
"$toString": "$minutes"
}
}
}
}
},
{
$project: {
duration: {
"$concat": [
"$hours",
":",
"$minutes"
]
}
}
}
])
mongoplayground
i'm trying to distinct different string values of "comment" and "dates" and concatenate them in two different strings one for comments and one for reclamation dates I used $addToSet to distinct values but every time the concatenating is giving empty result
and this is m code :
db.collection.aggregate([
{
$group: {
_id: {
b: "$type"
},
total: {
$sum: 1
},
root: {
$push: "$$ROOT"
}
}
},
{
"$unwind": "$root"
},
{
$group: {
_id: {
r: "$root.situation",
b: "$root.type"
},
cnt: {
$sum: 1
},
total: {
"$first": "$total"
},
}
},
{
$project: {
a: [
{
k: "$_id.r",
v: "$cnt"
}
],
type: "$_id.b",
total: "$total",
_id: 0,
}
},
{
$project: {
d: {
$arrayToObject: "$a"
},
type: 1,
total: 1
}
},
{
$group: {
_id: "$type",
situation: {
$push: "$d"
},
sum: {
"$first": "$total"
},
//add to set of commets and dates
comment: {
$addToSet: "$comment"
},
daterec: {
$addToSet: "$daterec"
},
}
},
{
$project: {
_id: 0,
type: "$_id",
sum: 1,
"options": {
$mergeObjects: "$situation"
},
//concatenating results
comment: {
$reduce: {
input: "$comment",
initialValue: "",
in: {
$cond: [
{
"$eq": [
"$$value",
""
]
},
"$$this",
{
$concat: [
"$$value",
" ",
"$$this"
]
}
]
}
}
},
daterec: {
$reduce: {
input: "$daterec",
initialValue: "",
in: {
$cond: [
{
"$eq": [
"$$value",
""
]
},
"$$this",
{
$concat: [
"$$value",
" ",
"$$this"
]
}
]
}
}
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$$ROOT",
"$options"
]
}
}
},
{
$project: {
options: 0,
}
},
])
and this is the output :
{
"after sales management": 4,
"comment": "",
"daterec": "",
"instock": 1,
"sum": 5,
"type": "pc"
}
its always giving "comment" and"daterec": empty while i want it to show the concatenating of distinct strings it should looks something like this :
{
"after sales management": 4,
"comment": "waiting for pieces waiting for approval nothing",
"daterec": "1",//this is just an example beacause all dates are set to "1"
"instock": 1,
"sum": 5,
"type": "pc"
}
this is an example of my work with a collection example:
https://mongoplayground.net/p/dB3xBFsIfun
PS : i think the problem is in the of using the $addToSet insite $group
I have a Schema in mongodb like:
INPUT
{
_id: ObjectId("5e05c1089b3e4e333cee8c39"),
name:"Alex",
activity:[
{
_id: ObjectId("5e05c1089b3e4e333cee8c39"),
type: 'run',
start_timestamp: ISODate("2020-01-11T11:34:59.804Z"),
end_timestamp: ISODate("2020-01-11T11:40:00.804Z")
},
{
_id: ObjectId("5e05c1089b3e4e333cee8c40"),
type: 'stop',
start_timestamp: ISODate("2020-01-11T11:40:00.804Z"),
end_timestamp: ISODate("2020-01-11T11:42:00.804Z")
},
{
_id: ObjectId("5e05c1089b3e4e333cee8c41"),
type: 'wait',
start_timestamp: ISODate("2020-01-11T11:42:00.804Z"),
end_timestamp: ISODate("2020-01-11T11:52:00.804Z")
},
{
_id: ObjectId("5e05c1089b3e4e333cee8c41"),
type: 'stop',
start_timestamp: ISODate("2020-01-11T11:52:00.804Z"),
end_timestamp: ISODate("2020-01-11T12:02:00.804Z")
},
{
_id: ObjectId("5e05c1089b3e4e333cee8c41"),
type: 'sleep',
start_timestamp: ISODate("2020-01-11T12:02:00.804Z"),
end_timestamp: ISODate("2020-01-11T12:48:00.804Z")
}
]
}
This is a schema for a man activity, i need to find brake-up of every 15 minute (brake-up duration in minute),i have found a solution stackoverflow but here only single timestamp but in my case there are 2 timestamp and first i have to calculate duration and then group by according to 15 minute
OUTPUT
[
{
_id: "2020-01-11T11:34 to 2020-01-11T11:49" ,
duration: "15 min",
"brake-up":{
run:"6 min",
stop:"2 min",
wait:"7 min"
}
},
{
_id: "2020-01-11T11:49 to 2020-01-11T12:04" ,
duration: 15 min,
"brake-up":{
wait:"3 min"
stop:"10 min"
sleep:"2 min"
}
{
_id: "2020-01-11T12:04 to 2020-01-11T12:19" ,
duration: 15 min,
"brake-up":{
sleep:"15 min"
}
}
]
Thanks
It's a bit tedious solution.
Explanation
I assume activity.type not repeated inside X min break-up
I assume start_timestamp and end_timestamp as is (don't ignore seconds:milliseconds)
We calculate min / max dates from start_timestamp and end_timestamp
We calculate how many 15 min breaks are between min / max dates
We create from and to variables that includes X min break-up from min / max dates
For each from and to, we filter activities
Once we filter, we calculate waste time taking in mind from and to and start_timestamp and end_timestamp dates
We create Array with activity + waste time and transform it into object
db.collection.aggregate([
{
$project: {
root: "$$ROOT",
duration: {
$toInt: 15
},
"start": {
$reduce: {
"input": "$activity",
initialValue: ISODate("2100-01-01"),
in: {
$min: [
"$$value",
"$$this.start_timestamp",
"$$this.end_timestamp"
]
}
}
},
"end": {
$reduce: {
"input": "$activity",
initialValue: ISODate("1970-01-01"),
in: {
$max: [
"$$value",
"$$this.start_timestamp",
"$$this.end_timestamp"
]
}
}
}
}
},
{
$addFields: {
interval: {
$range: [
0,
{
$round: {
$divide: [
{
$toLong: {
$subtract: [
"$end",
"$start"
]
}
},
{
$multiply: [
"$duration",
60,
1000
]
}
]
}
},
1
]
}
}
},
{
$unwind: "$interval"
},
{
$addFields: {
from: {
$add: [
"$start",
{
$multiply: [
"$interval",
{
$multiply: [
"$duration",
60,
1000
]
}
]
}
]
},
to: {
$min: [
"$end",
{
$add: [
"$start",
{
$multiply: [
{
$add: [
"$interval",
1
]
},
{
$multiply: [
"$duration",
60,
1000
]
}
]
}
]
}
]
},
activity: "$root.activity"
}
},
{
$addFields: {
activity: {
$filter: {
input: "$activity",
cond: {
$or: [
{
$and: [
{
$gte: [
"$$this.start_timestamp",
"$from"
]
},
{
$lte: [
"$$this.end_timestamp",
"$to"
]
}
]
},
{
$and: [
{
$lte: [
"$$this.start_timestamp",
"$to"
]
},
{
$gte: [
"$$this.end_timestamp",
"$from"
]
}
]
}
]
}
}
}
}
},
{
$project: {
_id: {
$concat: [
{
$toString: "$from"
},
" to ",
{
$toString: "$to"
}
]
},
name: "$root.name",
duration: {
$concat: [
{
$toString: "$duration"
},
" min"
]
},
"brake-up": {
$map: {
input: "$activity",
in: {
k: "$$this.type",
v: {
$round: {
$divide: [
{
"$subtract": [
{
$min: [
"$$this.end_timestamp",
"$to"
]
},
{
$max: [
"$$this.start_timestamp",
"$from"
]
}
]
},
{
$multiply: [
60,
1000
]
}
]
}
}
}
}
}
}
},
{
$unwind: "$brake-up"
},
{
$group: {
_id: {
_id: "$_id",
duration: "$duration",
name: "$name",
"brake-up-k": "$brake-up.k"
},
"brake-up-v": {
$sum: "$brake-up.v"
}
}
},
{
$group: {
_id: {
_id: "$_id._id",
duration: "$_id.duration",
name: "$_id.name"
},
"brake-up": {
$push: {
k: "$_id.brake-up-k",
v: {
$concat: [
{
$toString: "$brake-up-v"
},
" min"
]
}
}
}
}
},
{
$project: {
_id: "$_id._id",
name: "$_id.name",
duration: "$_id.duration",
"brake-up": {
$arrayToObject: "$brake-up"
}
}
},
{
$sort: {
_id: 1
}
}
])
MongoPlayground