MongoDB group results by time interval - mongodb

I have a collection like below.
{
"field1":"value1",
"created_at":"2022-01-01T11:42:01Z"
},
{
"field1":"value2",
"created_at":"2022-01-01T11:22:15Z"
}
I need to group the results by 15 minute time interval and project the results like below from this collection.
[{
"from":"2022-01-01T11:15:00Z",
"to":"2022-01-01T11:30:00Z",
"count":1
},
{
"from":"2022-01-01T11:30:00Z",
"to":"2022-01-01T11:45:00Z",
"count":1
}]
I am able to get the count by 15 minute time interval using the below query. But I want to project from and to dates as well.
db.collection.aggregate([
{ "$group": {
"_id": {
"year": { "$year": "$created_at" },
"dayOfYear": { "$dayOfYear": "$created_at" },
"hour": { "$hour": "$created_at" },
"interval": {
"$subtract": [
{ "$minute": "$created_at" },
{ "$mod": [{ "$minute": "$created_at"}, 15] }
]
}
}},
"count": { "$sum": 1 }
}}
])

You can try an approach,
$dateToParts get parts of the created_at date
$group by year, month, day, hour, and interval as per mod and subtraction calculation and get the total count
to get from and to date from interval you can use $dateFromParts operator, just to add 15 minutes into the date.
db.collection.aggregate([
{
$addFields: {
created_at: { $dateToParts: { date: "$created_at" } }
}
},
{
$group: {
_id: {
year: "$created_at.year",
month: "$created_at.month",
day: "$created_at.day",
hour: "$created_at.hour",
interval: {
$subtract: [
"$created_at.minute",
{ $mod: ["$created_at.minute", 15] }
]
}
},
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
count: 1,
from: {
$dateFromParts: {
year: "$_id.year",
month: "$_id.month",
day: "$_id.day",
hour: "$_id.hour",
minute: "$_id.interval"
}
},
to: {
$dateFromParts: {
year: "$_id.year",
month: "$_id.month",
day: "$_id.day",
hour: "$_id.hour",
minute: { $add: ["$_id.interval", 15] }
}
}
}
}
])
Playground

Related

How to calculate average records per month?

My records like this [{ createdAt }, {createdAt}, {createdAt} ]
I need average records per month.
january => 3 records
february => 2 records etc..
You can try to $group by month and year when counting and by month when averaging:
db.collection.aggregate([
{
$group: {
_id: {
month: {
$month: "$createdAt"
},
year: {
$year: "$createdAt"
},
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: {
month: "$_id.month"
},
average: {
$avg: "$count"
}
}
},
{
$project: {
_id: 0,
month: "$_id.month",
average: 1
}
}
])
Link to playground
Not fully clear what you mean by "average records per month" but I think it would be this:
db.collection.aggregate([
{
$group: {
_id: {
$dateTrunc: {
date: "$createdAt",
unit: "month"
}
},
count: { $count: {} }
}
},
{
$group: {
_id: null,
data: { $push: { k: { $toString: { $month: "$_id" } }, v: "$count" } }
}
},
{ $replaceWith: { $arrayToObject: "$data" } }
])
Getting the month name is not so easy, either you use a external library or build your own with $switch

MongoDB aggregation add months or years to a Date

We can use $add to add Dates in the aggregation pipe. Is there a way to add a specific number of time units (ie days months or years) except milliseconds?
The current way to add 3 years is date: { $add: [ "$date", 3*365*24*60*60000 ] }
My expected syntax format is date: { $add: [ "$date", { $years:3 } ] } which is not working
As mentioned in my comment, you will get this function Mongo 5.0
In the meantime you can use this workaround:
db.collection.aggregate([
{
$addFields: {
new_date: {
$dateFromParts: {
year: { $add: [{ $year: "$date" }, 3] },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" },
hour: { $hour: "$date" },
minute: { $minute: "$date" },
second: { $second: "$date" },
millisecond: { $millisecond: "$date" }
}
}
}
}
])

How to group MongoDB aggregation [duplicate]

Who knows a better solution to group Orders by date and sum total and count by source. Of course I can group by Source and then I get only totals for this source only, I can alter the result thereafter to get the desired result. But I would like to know if it is possible in one simple $group statement.
Eg. ordersByApp = 1, ordersByWEB = 2
Orders collection
{
_id: 'XCUZO0',
date: "2020-02-01T00:00:03.243Z"
total: 9.99,
source: 'APP'
},
{
_id: 'XCUZO1',
date: "2020-01-05T00:00:03.243Z"
total: 9.99,
source: 'WEB'
},
{
_id: 'XCUZO2',
date: "2020-01-02T00:00:03.243Z"
total: 9.99,
source: 'WEB'
}
My current aggregation
Order.aggregate([
{
$group: {
_id: {
month: { $month: "$date",
year: { $year: "$date" }
},
total: {
$sum: "$total"
}
}
}
])
Current result
[
{
_id: { month: 01, year: 2020 },
total: 19.98
},
{
_id: { month: 02, year: 2020 },
total: 9.99
}
]
Desired result, How can I achieve the below?
[
{
_id: { month: 01, year: 2020 },
total: 19.98,
countByApp: 1, <---
countByWEB: 0, <---
},
{
_id: { month: 02, year: 2020 },
total: 9.99,
countByWEB: 2, <---
countByAPP: 0 <---
}
]
You can use $cond like below:
Order.aggregate([
{
$group: {
_id: {
month: { $month: "$date" },
year: { $year: "$date" }
},
total: { $sum: "$total" },
countByApp: { $sum: { $cond: [ {$eq: [ "$source", "APP" ]} , 1, 0] } },
countByWeb: { $sum: { $cond: [ {$eq: [ "$source", "WEB" ]} , 1, 0] } },
}
}
])
Mongo Playground

Get Days Between 2 MongoDB ISODates [duplicate]

This question already has answers here:
Find the total time spent by an user in mongoDB
(1 answer)
How to find the hours difference between two dates in mongodb
(2 answers)
Closed 3 years ago.
I am trying to get the days difference between 2 ISODates and put it in days field. I have these documents below:
{
created_at: ISODate("2019-06-06TT00:00:00Z"),
completed_at: "2019-06-08"
},
{
created_at: ISODate("2019-06-06TT00:00:00Z"),
completed_at: null
},
{
created_at: ISODate("2019-06-06TT00:00:00Z"),
completed_at: "2019-06-04"
}
What I have tried so far is to get the milliseconds. But I don't want that. I just want to substract the dates and get the difference in days.
db.collection.aggregate([
{
$addFields: {
completed_at: {
$ifNull: [
{
"$dateFromString": {
"dateString": "$completed_at"
}
},
new ISODate()
]
}
}
},
{
$addFields: {
days: {
$cond: [
{"$lte": ["$completed_at", "$created_at"]},
0,
{
$divide: [
{
$subtract: [
"$completed_at",
"$created_at"
]
},
86400000
]
}
]
}
}
},
{
$project: {
_id: 0,
created_at: 1,
completed_at: 1,
days: 1
}
}
])
If completed_at field is null, then the value for this should be the current date. If completed_at is lesser than created_at, then days is 0. Let's say current date is June 6, 2019, desired result should be:
{
days: 2
},
{
days: 0
},
{
days: 0
}
I think I was able to get it. I removed the time in the date fields and the results that I wanted is showing correctly. Please let me know if I got it correctly or there are some adjustments that I need to make in order to make it faster. Thank you!
db.collection.aggregate([
{
$addFields: {
created_at: {
$dateFromParts: {
"year": {
$year: "$created_at"
},
"month": {
$month: "$created_at"
},
"day": {
$dayOfMonth: "$created_at"
}
}
},
completed_at: {
$ifNull: [
{
"$dateFromString": {
"dateString": "$completed_at"
}
},
{
$dateFromParts: {
"year": {
$year: new Date()
},
"month": {
$month: new Date()
},
"day": {
$dayOfMonth: new Date()
}
}
}
]
}
}
},
{
$addFields: {
days: {
$cond: [
{
"$lte": [
"$completed_at",
"$created_at"
]
},
0,
{
$divide: [
{
$subtract: [
"$completed_at",
"$created_at"
]
},
86400000
]
}
]
}
}
},
{
$project: {
_id: 0,
created_at: 1,
completed_at: 1,
days: 1
}
}
])

Tried Aggregating by Date using the below query but didn't get a result

db.track_visits_y17_m01.aggregate([{
$match: {
"trackingData.gradeId": {
$in: ["12", "15", "16", 12, 15, 16]
},
"created": {
$gte: "2017-01-01 00:00:00",
$lt: "2017-01-23 00:00:00"
}
}
}, {
$group: {
_id: {
year: {
$year: "$created"
},
month: {
$month: "$created"
},
day: {
$dayOfMonth: "$created"
},
},
total: {
$sum: 1
}
}
}])
It runs but returns nothing.