Day wise + hour bucketing in mongo - mongodb

I have a collection with a timestamp in each record. I need to aggregate the records of the collection per day of the week, and within it, on an hourly basis. I took a look at the aggregation / grouping queries here in SO, but I'm not sure how to use it in my scenario
Collection: Alerts
Docs:
{
isOpen: true,
Time: // ISO("time_string"),
...
...
}
I would like the aggs to be similar to how elasticsearch datetime histogram behaves.
Output:
{
day: "sunday",
time: "12am",
openAlerts: 23
},
{
day: "sunday",
time: "1am",
openAlerts: 13
},
{
day: "sunday",
time: "2am",
openAlerts: 0
},
Special emphasis on the 0 counts in the hour bucket. Is there a way to fill in 0s when there is no doc count?
Thanks

Would be like this:
db.collection.aggregate([
{
$group: {
_id: {
year: { $year: "$Time" },
month: { $month: "$Time" },
day: { $dayOfMonth: "$Time" },
hour: { $hour: "$Time" }
},
openAlerts: { $sum: 1 }
}
}
])

Related

Mongo query: compare multiple of the same field (timestamps) to only show documents that have a few second difference

I am spinning my wheels on this. I am needing to find all documents within a collection that have a timestamp ("createdTs") that have a 3 second or less difference (to be clear: month/day/time/year all the same, save those few seconds). An example of createdTs field (it's type Date): 2021-04-26T20:39:01.851Z
db.getCollection("CollectionName").aggregate([
{ $match: { memberId: ObjectId("1234") } },
{
$project:
{
year: { $year: "$createdTs" },
month: { $month: "$createdTs" },
day: { $dayOfMonth: "$createdTs" },
hour: { $hour: "$createdTs" },
minutes: { $minute: "$createdTs" },
seconds: { $second: "$createdTs" },
milliseconds: { $millisecond: "$createdTs" },
dayOfYear: { $dayOfYear: "$createdTs" },
dayOfWeek: { $dayOfWeek: "$createdTs" },
week: { $week: "$createdTs" }
}
}
])
I've tried a lot of different variances. Where I'm struggling is how to compare these findings to one another. I'd also prefer to just search the entire collection and not match on the "memberId" field, just collect any documents that have less than a 3 second createdTs difference, and group/display those.
Is this possible? Newer to Mongo, and spun my wheels on this for two days now. Any advice would be greatly appreciated, thank you!
I saw this on another post, but not sure how to utilize it since I'm wanting to compare the same field:
db.collection.aggregate([
{ "$project": {
"difference": {
"$divide": [
{ "$subtract": ["$logoutTime", "$loginTime"] },
60 * 1000 * 60
]
}
}},
{ "$group": {
"_id": "$studentID",
"totalDifference": { "$sum": "$difference" }
}},
{ "$match": { "totalDifference": { "$gte": 20 }}}
])
Also am trying...
db.getCollection("CollectionName").aggregate([
{ $match: { memberId: ObjectId("1234") } },
{
$project:
{
year: { $year: "$createdTs" },
month: { $month: "$createdTs" },
total:
{ $add: ["$year", "$month"] }
}
}
])
But this returns a total of null. Not sure if it's because $year and $month are difference? The types are both int32, so I thought that'd work. Was wondering if there's a way to compare if all the fields are 0, then if seconds is not/difference is $gte 3 when using $group, could go from there.

Count of groups of certain groups of documents

Documents in this mongodb has the property createdAt = Date.now() which is the epoch time as well as the properties userid and name.
What is needed is the count of documents for name='abc' which are created less or equal to 4 months ago, but counting those documents which were created by the same userId and on the same day as one count.
What is the best way to go about it? aggregate or map or combination or ...
You can try below aggregation.
var date = new Date(Date.UTC(2017, 2, 3, 0, 0, 0));
date.setMonth(date.getMonth() - 4);
db.collection.aggregate([{
$match: {
createdAt: {
$gte: date,
$lte: new Date(Date.UTC(2017, 2, 3, 0, 0, 0))
},
name: "abc"
}
}, {
$group: {
_id: {
date: {
$dayOfMonth: "$createdAt"
},
month: {
$month: "$createdAt"
},
year: {
$year: "$createdAt"
},
userid: "$userid"
},
count: {
$sum: 1
}
}
}]);

MongoDB Aggregation by Shifted Date

Is there a way to aggregate by day, but over a 24 hour period that does not go from 12am - 11:59pm? A sample document looks like this:
{
date: ISODate("2012-11-02T17:04:11.102Z"),
user: 'testUser',
orders: 50
}
I need to aggregate the # of orders per user per day between 5 pm and 4:59:59.999 pm the next day. I can get the # of orders per user per day (over a 2 day range) using this:
db.hs.aggregate([{
$match: {
user: 'testUser',
date: {
$gte: new Date(2015,0,4,17,0,0,0),
$lt: new Date(2015,0,6,17,0,0,0)
}
}
}, {
$group: {
_id: {
date: {
month: {$month: "$date"},
day: {$dayOfMonth: "$date"},
year: {$year: "$date"}
},
user: "$user",
},
totord: {$sum: "$orders"}
}
}])
But this returns 3 results, one for Jan 4 after 5pm, one for Jan 5 all day, and one for Jan 6 before 5pm. I just don't know how to shift the aggregation to be between 5pm - 4:59pm.
I believe that you could use the $add aggregation operator on your dates in a $project aggregation step to get the desired result.
db.hs.aggregate([
{
$match: {
user: 'testUser',
date: {
$gte: new Date(2015,0,4,17,0,0,0),
$lt: new Date(2015,0,6,17,0,0,0)
}
}
}, {
$project: {
orders: 1,
user: 1,
date: { $add: [ "$date", 7*60*60000 ] }
}
}, {
$group: {
_id: {
date: {
month: { $month: "$date" },
day: { $dayOfMonth: "$date" },
year: { $year: "$date" }
},
user: "$user"
},
totord: {$sum: "$orders"}
}
}
])
I believe this should add 7 hours to the $date which should make it so anything after 5PM ends up after midnight the next day.

Mongodb aggregate for timeseries data

I have a timeseries dataset with a few hundred thousand records in it. I am trying to create an aggregate query in mongo to group this data in intervals all while averaging the price.
Ideally I would want 10minute intervals (600000ms) and the price averages. I'm not too sure how to carry on from where I am at.
Data ~a few hundred thousand records:
{
"time" : 1391485215000,
"price" : "0.00133355",
}
query = [
{
"$project": {
"_id":"$_id",
"price":"$price",
"time": {
xxxx
}
}
},
{
"$group": {xxxx}
}
]
So it would appear that I had a fundamental flaw in my Schema. I was using an epoch timestamp instead of mongo's Date type, as well as storing the other numbers as strings instead of doubles. I tried a few workarounds but it doesn't look like you are able to use the built in aggregate functions unless they are of the correct type.
$project: {
year: { $year: '$time'},
month: { $month: '$time'},
day: { $dayOfMonth: '$time'},
hour: { $hour: '$time'},
price: 1,
total: 1,
amount: 1
}
},
{
$group : {
_id: { year: '$year', month: '$month', day: '$day', hour: '$hour' },
price:{
$avg: "$price"
},
high:{
$max: "$price"
},
low:{
$min: "$price"
},
amount:{
$sum: "$amount"
},
total:{
$sum: "$total"
}
}

Counting user activity with MongoDB Aggregate Framework

I will try to keep this simple. I have user objects, inside my user objects I have a field that is an array which just contains ISODates of the the days a user has logged in. I would like to count how many users logged in on a particular date for all dates that exist.
Sample user:
{
"_id": "some_id",
"name": "bob",
"logins": [isodate, isodate, isodate...],
//...
}
I'd like an output that tells me something like:
{
"date": ISODate,
"number_of_users_logged_in": 10
}
Is this possible? How would I go about doing it?
You need to use $unwind operation explode array, then $group by date (using the granularity that you want) and $project only the date and count, as below:
db.user.aggregate({
$unwind: "$logins"
},
{
$group: {
_id: {
year: {
$year: "$logins"
},
month: {
$month: "$logins"
},
day: {
$dayOfMonth: "$logins"
},
hour: {
$hour: "$logins"
}
},
date: {
$first: "$logins"
},
count: {
$sum: 1
}
}
},
{
$project: {
_id : 0,
date: "$date",
number_of_users_logged_in: "$count"
}
})
I grouped by year/month/day/hour.