I have a Mongo database filled with "Events" records, that look like this:
{
timestamp: 2022-03-15T22:11:34.711Z,
_id: new ObjectId("62310f16b0d71321e887a905")
}
Using a NodeJs server, I need to fetch the last 30 days of Events, grouped/summed by date, and any dates within that 30 days with no records need to be filled with 0.
Using this code I can get the correct events, grouped/summed by date:
Event.aggregate( [
{
$match: {
timestamp: {
$gte: start,
$lte: end,
}
}
},
{
$project: {
date: {
$dateToParts: { date: "$timestamp" }
},
}
},
{
$group: {
_id: {
date: {
year: "$date.year",
month: "$date.month",
day: "$date.day"
}
},
"count": { "$sum": 1 }
}
}
] )
This will return something like this:
[
{
"_id": {
"date": {
"year": 2022,
"month": 3,
"day": 14
}
},
"count": 3
},
{
"_id": {
"date": {
"year": 2022,
"month": 3,
"day": 15
}
},
"count": 8
},
]
I also have this Javascript code to generate the last 30 days of dates:
const getDateRange = (start, end) => {
const arr = [];
for(let dt = new Date(start); dt <= end; dt.setDate(dt.getDate() + 1)){
arr.push(new Date(dt));
}
return arr;
};
const subtractDays = (date, days) => {
return new Date(date.getTime() - (days * 24 * 60 * 60 * 1000));
}
const end = new Date();
const start = subtractDays(end, 30);
const range = getDateRange(start, end);
Which returns something like this:
[
2022-03-09T01:13:10.769Z,
2022-03-10T01:13:10.769Z,
2022-03-11T01:13:10.769Z,
2022-03-12T01:13:10.769Z,
2022-03-13T01:13:10.769Z,
...
]
It seems like I have all the pieces, but I’m having trouble putting all this together to do what I need in an efficient way. Any push in the right direction would be appreciated.
2
Answers
Whenever one has to work with date/time arithmetic then I recommend a library like moment.js
In MongoDB version 5.0 you can use $dateTrunc(), which is shorter than
$dateToParts
and{ year: "$date.year", month: "$date.month", day: "$date.day" }
You need to put all data in an array (
{$group: {_id: null, data: { $push: "$$ROOT" }}
) and then at missing elements with$ifNull
:$range
operator supports only integer values, that’s the reason for using$let
. Otherwise, if you prefer to use the external generated range, it would beAnd for MongoDB version 5.1 you may have a look at $densify
Use aggregation stage densify if you’re using MongoDB version 5.1 or later. But for lower version, below query can be used.
Link to online playground. https://mongoplayground.net/p/5I0I04HoHXm