Query to get last X minutes data with Mongodb - mongodb

I'm trying to query my db that have this document format:
{
"_id" : ObjectId("520b8b3f8bd94741bf006033"),
"value" : 0.664,
"timestamp" : ISODate("2013-08-14T13:48:35Z"),
"cr" : ISODate("2013-08-14T13:50:55.834Z")
}
I can get the last records from a datetime with this query:
db.mycol.find({timestamp:{$gt: ISODate("2013-08-14T13:48:00Z")}}).sort({x:1});
But I'm trying to get a set with the value fields and timestamps from 18 minutes ago.

For the 18 minutes part, that's not really about MongoDB, but about JavaScript and what's available in the mongo shell:
query = {
timestamp: { // 18 minutes ago (from now)
$gt: new Date(ISODate().getTime() - 1000 * 60 * 18)
}
}
Works in the mongo shell, but using Mongo drivers for other languages would be really different.
To "project" over a smaller schema with both values and timestamps:
projection = {
_id: 0,
value: 1,
timestamp: 1,
}
Applying both:
db.mycol.find(query, projection).sort({timestamp: 1});
Well, that's still not a "set" since there might be duplicates. To get rid of them you can use the $group from the aggregation framework:
db.mycol.aggregate([
{$match: query},
{$group: {
_id: {
value: "$value",
timestamp: "$timestamp",
}
}},
{$project: {
value: "$_id.value",
timestamp: "$_id.timestamp",
}},
{$sort: {timestamp: 1}},
])

You could also do below
db.getCollection('collectionName').find({timestamp : {$gte: new Date().getTime()-(60*60*1000) } } )
The above query ll give you records of timestamp b/w now and 60 mins. if you like more then 60 mins - say 2 hrs you could change expression to (2*60*60*1000)
for 30 mins (30*60*1000)

Starting in Mongo 5, you can use $dateSubtract:
// { date: ISODate("2021-12-05T20:32:56Z") } <= 5 minutes ago
// { date: ISODate("2021-12-05T20:07:56Z") } <= 25 minutes ago (older than 18 minutes)
db.collection.aggregate([
{ $match: {
$expr: {
$gt: [
"$date",
{ $dateSubtract: { startDate: "$$NOW", unit: "minute", amount: 18 } }
]
}
}}
])
// { date: ISODate("2021-12-05T20:32:56Z") } <= 5 minutes ago
With $dateSubtract, we create the oldest date/time after which we keep documents, by subtracting 18 (amount) "minute" (unit) out of the current date $$NOW (startDate).

you can access the data of current timestamp from mongodb using nodejs
const collection1 = dbo.collection('customers');
var dateq = new Date();
collection1.find({ "Timestamp" : { $gt: new Date(dateq.getTime() - 6000)}
}).toArray(function(err , docs){
console.log(docs);
}
code end

Wow, thanks to #Alistair_Nelson I was able to get the data from n minutes ago, for example to get the last 18 minutes from ISODate("2013-08-14T14:00:00Z"):
db.mycol.find({timestamp:{$gt: new Date(ISODate("2013-08-14T14:00:00Z")-18*60000)}})
To get only the fields I need:
db.mycol.find({timestamp:{$gt: new Date(ISODate("2013-08-14T14:00:00Z")-18*60000)}},{value:1,timestamp:1, _id:0})

const xMins = 10;
db.mycol.find({ timestamp: { $gt: new Date(Date.now() - 1000 * 60 * xMins) } }).sort({ x: 1 });

Related

MongoDB select last documents in 24 hours

I'm using the MongoDB version 4.4, I'm trying to return the last documents inserted in the last 24 hous, and i need to run this query every day, because of this, i don't set the date manualy, I need to use new Date()
The problem is, when a put more arguments to bring me the last 24 hours, the MongoDB return a error message to me, and i try for many ways to resolve this, but nothing worked
This is my code
{
"$match" : {
"CreatedAt2" : {
"$gte" : ISODate("2022-04-21")
}
}
}
If i run this code, this works fine, but if a change the string by new Date(), the problem start.
In my test, if a put only new Date() like bellow and find by '$lte', the code rans good.
'$lte' is only to check if new Date() will works
"CreatedAt2" : { $lte: new Date() }
So, if a tried to include the value to return the last 24 hours, my query don't work, look bellow
"CreatedAt2" : { $gte: new Date(new Date().getTime() - 86400000) }
The MongoDB bring to me this error message
Now I don't know what I need to do to fix this.
Instead of subtracting 86400000 Milliseconds, you can also use
db.collection.aggregate([
{
"$match": {
$expr: {
$gte: [
"$CreatedAt2",
{
$dateSubtract: {
startDate: "$$NOW",
unit: "hour",
amount: 24
}
}
]
}
}
}
])
Or use a 3rd party date-library, e.g. moment.js
{
"$match" : {
"CreatedAt2" : {
"$gte" : moment().subtract(24, 'hours').toDate()
}
}
}
Consider leveraging the $$NOW aggregation variable and $subtract it by 24hours * 60minutes * 60 seconds * 1000 milliseconds = 86400000 ms to get the records within 24 hours
db.collection.aggregate([
{
"$match": {
$expr: {
$gte: [
"$CreatedAt2",
{
"$subtract": [
"$$NOW",
86400000
]
}
]
}
}
}
])
Here is the Mongo playground for your reference.
Try this
"createdAt2":{$gte:new Date(Date.now() - 86400)
This should work fine

MongoDB aggregation group by month in big collection - optimize pipeline

I'm aware that this question has been asked before at SO - but I can't seem to find how to handle aggregation grouping in bigger collections. I have a set of +10 million records, and I just can't get any speed to it.
Running MongoDB v 3.2.
Having a field __createDateUtc (ISODate) in the schema, I'm trying the following pipeline:
db.transactions.aggregate([
{
$project: {
__createDateUtc: 1
}
},
{
$group: {
'_id': { $year: '$__createDateUtc' },
'count': {$sum: 1},
}
},
{
$limit: 10
},
])
This runs at +20 seconds. Could it be made faster? This is a fairly simple pipeline - so really - is there any other strategy that might help in this situation?
I did some bench marking with four different ways of getting the results that I wanted. The results are a discouraging.
Again, with a schema looking like:
{
"_id" : ObjectId("5d665491fd5852755236a5dc"),
...
"__createDateUtc" : ISODate("2019-08-28T10:16:49Z"),
"__createDate" : {
"year" : 2019,
"month" : 8,
"day" : 28,
"yearMonth" : 201908,
"yearMonthDay" : 20190829
}
}
The results:
// Group by __createDate.yearMonth
db.transactions.aggregate([
{ $group: {
'_id': '$__createDate.yearMonth',
'count': {$sum: 1},
} },
{ $limit: 10 },
{ $sort: {'_id': -1 } }
])
// 20 169 ms
// Group by year and month
db.transactions.aggregate([
{$group: {
'_id': {year: '$__createDate.year', month: '$__createDate.month' },
'count': {$sum: 1},
}},
{ $limit: 10 },
{ $sort: {'_id': -1 } }
])
// 23 777 ms
// Group by calculating year and month from ISODate
db.transactions.aggregate([
{$group: {
'_id': {year: { $year: '$__createDateUtc' }, month: { $month: '$__createDateUtc' } },
'count': {$sum: 1},
}},
{ $limit: 10 },
{ $sort: {'_id': -1 } }
])
// 16 444 ms
// Last stupid method to just run many queries with count
var years = [2017, 2018, 2019];
var results = {}
years.forEach(year => {
results[year] = {};
for(var i = 1; i < 13; i++) {
var count = db.transactions.find({'__createDate.year': year, '__createDate.month': i}).count();
if(count > 0) results[year][i] = count;
}
})
// 10 701 ms
As you can see the last method of just running multiple counts is by far the fastest. Especially since I'm actually fetching a lot more data compared to the three other methods.
This just seems stupid to me. I know MongoDB is no search engine, but still. Aggregation is just not fast at all. Makes me wanna sync data to elastic search and try to aggregate within ES instead.

How do I do a 'group by' for a datetime when I want to group by just the date using $group? [duplicate]

I am working on a project in which I am tracking number of clicks on a topic.
I am using mongodb and I have to group number of click by date( i want to group data for 15 days).
I am having data store in following format in mongodb
{
"_id" : ObjectId("4d663451d1e7242c4b68e000"),
"date" : "Mon Dec 27 2010 18:51:22 GMT+0000 (UTC)",
"topic" : "abc",
"time" : "18:51:22"
}
{
"_id" : ObjectId("4d6634514cb5cb2c4b69e000"),
"date" : "Mon Dec 27 2010 18:51:23 GMT+0000 (UTC)",
"topic" : "bce",
"time" : "18:51:23"
}
i want to group number of clicks on topic:abc by days(for 15 days)..i know how to group that but how can I group by date which are stored in my database
I am looking for result in following format
[
{
"date" : "date in log",
"click" : 9
},
{
"date" : "date in log",
"click" : 19
},
]
I have written code but it will work only if date are in string (code is here http://pastebin.com/2wm1n1ix)
...please guide me how do I group it
New answer using Mongo aggregation framework
After this question was asked and answered, 10gen released Mongodb version 2.2 with an aggregation framework, which is now the better way to do this sort of query. This query is a little challenging because you want to group by date and the values stored are timestamps, so you have to do something to convert the timestamps to dates that match. For the purposes of example I will just write a query that gets the right counts.
db.col.aggregate(
{ $group: { _id: { $dayOfYear: "$date"},
click: { $sum: 1 } } }
)
This will return something like:
[
{
"_id" : 144,
"click" : 165
},
{
"_id" : 275,
"click" : 12
}
]
You need to use $match to limit the query to the date range you are interested in and $project to rename _id to date. How you convert the day of year back to a date is left as an exercise for the reader. :-)
10gen has a handy SQL to Mongo Aggregation conversion chart worth bookmarking. There is also a specific article on date aggregation operators.
Getting a little fancier, you can use:
db.col.aggregate([
{ $group: {
_id: {
$add: [
{ $dayOfYear: "$date"},
{ $multiply:
[400, {$year: "$date"}]
}
]},
click: { $sum: 1 },
first: {$min: "$date"}
}
},
{ $sort: {_id: -1} },
{ $limit: 15 },
{ $project: { date: "$first", click: 1, _id: 0} }
])
which will get you the latest 15 days and return some datetime within each day in the date field. For example:
[
{
"click" : 431,
"date" : ISODate("2013-05-11T02:33:45.526Z")
},
{
"click" : 702,
"date" : ISODate("2013-05-08T02:11:00.503Z")
},
...
{
"click" : 814,
"date" : ISODate("2013-04-25T00:41:45.046Z")
}
]
There are already many answers to this question, but I wasn't happy with any of them. MongoDB has improved over the years, and there are now easier ways to do it. The answer by Jonas Tomanga gets it right, but is a bit too complex.
If you are using MongoDB 3.0 or later, here's how you can group by date. I start with the $match aggregation because the author also asked how to limit the results.
db.yourCollection.aggregate([
{ $match: { date: { $gte: ISODate("2019-05-01") } } },
{ $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date"} }, count: { $sum: 1 } } },
{ $sort: { _id: 1} }
])
To fetch data group by date in mongodb
db.getCollection('supportIssuesChat').aggregate([
{
$group : {
_id :{ $dateToString: { format: "%Y-%m-%d", date: "$createdAt"} },
list: { $push: "$$ROOT" },
count: { $sum: 1 }
}
}
])
Late answer, but for the record (for anyone else that comes to this page): You'll need to use the 'keyf' argument instead of 'key', since your key is actually going to be a function of the date on the event (i.e. the "day" extracted from the date) and not the date itself. This should do what you're looking for:
db.coll.group(
{
keyf: function(doc) {
var date = new Date(doc.date);
var dateKey = (date.getMonth()+1)+"/"+date.getDate()+"/"+date.getFullYear()+'';
return {'day':dateKey};
},
cond: {topic:"abc"},
initial: {count:0},
reduce: function(obj, prev) {prev.count++;}
});
For more information, take a look at MongoDB's doc page on aggregation and group: http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group
This can help
return new Promise(function(resolve, reject) {
db.doc.aggregate(
[
{ $match: {} },
{ $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, count: { $sum: 1 } } },
{ $sort: { _id: 1 } }
]
).then(doc => {
/* if you need a date object */
doc.forEach(function(value, index) {
doc[index]._id = new Date(value._id);
}, this);
resolve(doc);
}).catch(reject);
}
Haven't worked that much with MongoDB yet, so I am not completely sure. But aren't you able to use full Javascript?
So you could parse your date with Javascript Date class, create your date for the day out of it and set as key into an "out" property. And always add one if the key already exists, otherwise create it new with value = 1 (first click). Below is your code with adapted reduce function (untested code!):
db.coll.group(
{
key:{'date':true},
initial: {retVal: {}},
reduce: function(doc, prev){
var date = new Date(doc.date);
var dateKey = date.getFullYear()+''+date.getMonth()+''+date.getDate();
(typeof prev.retVal[dateKey] != 'undefined') ? prev.retVal[dateKey] += 1 : prev.retVal[dateKey] = 1;
},
cond: {topic:"abc"}
}
)
thanks for #mindthief, your answer help solve my problem today. The function below can group by day a little more easier, hope can help the others.
/**
* group by day
* #param query document {key1:123,key2:456}
*/
var count_by_day = function(query){
return db.action.group(
{
keyf: function(doc) {
var date = new Date(doc.time);
var dateKey = (date.getMonth()+1)+"/"+date.getDate()+"/"+date.getFullYear();
return {'date': dateKey};
},
cond:query,
initial: {count:0},
reduce: function(obj, prev) {
prev.count++;
}
});
}
count_by_day({this:'is',the:'query'})
Another late answer, but still. So if you wanna do it in only one iteration and get the number of clicks grouped by date and topic you can use the following code:
db.coll.group(
{
$keyf : function(doc) {
return { "date" : doc.date.getDate()+"/"+doc.date.getMonth()+"/"+doc.date.getFullYear(),
"topic": doc.topic };
},
initial: {count:0},
reduce: function(obj, prev) { prev.count++; }
})
Also If you would like to optimize the query as suggested you can use an integer value for date (hint: use valueOf(), for the key date instead of the String, though for my examples the speed was the same.
Furthermore it's always wise to check the MongoDB docs regularly, because they keep adding new features all the time. For example with the new Aggregation framework, which will be released in the 2.2 version you can achieve the same results much easier http://docs.mongodb.org/manual/applications/aggregation/
If You want a Date oject returned directly
Then instead of applying the Date Aggregation Operators, instead apply "Date Math" to round the date object. This can often be desirable as all drivers represent a BSON Date in a form that is commonly used for Date manipulation for all languages where that is possible:
db.datetest.aggregate([
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [ "$date", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$date", new Date(0) ] },
1000 * 60 * 60 * 24
]}
]},
new Date(0)
]
},
"click": { "$sum": 1 }
}}
])
Or if as is implied in the question that the grouping interval required is "buckets" of 15 days, then simply apply that to the numeric value in $mod:
db.datetest.aggregate([
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [ "$date", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$date", new Date(0) ] },
1000 * 60 * 60 * 24 * 15
]}
]},
new Date(0)
]
},
"click": { "$sum": 1 }
}}
])
The basic math applied is that when you $subtract two Date objects the result returned will be the milliseconds of differnce numerically. So epoch is represented by Date(0) as the base for conversion in whatever language constructor you have.
With a numeric value, the "modulo" ( $mod ) is applied to round the date ( subtract the remainder from the division ) to the required interval. Being either:
1000 milliseconds x 60 seconds * 60 minutes * 24 hours = 1 day
Or
1000 milliseconds x 60 seconds * 60 minutes * 24 hours * 15 days = 15 days
So it's flexible to whatever interval you require.
By the same token from above an $add operation between a "numeric" value and a Date object will return a Date object equivalent to the millseconds value of both objects combined ( epoch is 0, therefore 0 plus difference is the converted date ).
Easily represented and reproducible in the following listing:
var now = new Date();
var bulk = db.datetest.initializeOrderedBulkOp();
for ( var x = 0; x < 60; x++ ) {
bulk.insert({ "date": new Date( now.valueOf() + ( 1000 * 60 * 60 * 24 * x ))});
}
bulk.execute();
And running the second example with 15 day intervals:
{ "_id" : ISODate("2016-04-14T00:00:00Z"), "click" : 12 }
{ "_id" : ISODate("2016-03-30T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-03-15T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-02-29T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-02-14T00:00:00Z"), "click" : 3 }
Or similar distribution depending on the current date when the listing is run, and of course the 15 day intervals will be consistent since the epoch date.
Using the "Math" method is a bit easier to tune, especially if you want to adjust time periods for different timezones in aggregation output where you can similarly numerically adjust by adding/subtracting the numeric difference from UTC.
Of course, that is a good solution. Aside from that you can group dates by days as strings (as that answer propose) or you can get the beginning of dates by projecting date field (in aggregation) like that:
{'$project': {
'start_of_day': {'$subtract': [
'$date',
{'$add': [
{'$multiply': [{'$hour': '$date'}, 3600000]},
{'$multiply': [{'$minute': '$date'}, 60000]},
{'$multiply': [{'$second': '$date'}, 1000]},
{'$millisecond': '$date'}
]}
]},
}}
It gives you this:
{
"start_of_day" : ISODate("2015-12-03T00:00:00.000Z")
},
{
"start_of_day" : ISODate("2015-12-04T00:00:00.000Z")
}
It has some pluses: you can manipulate with your days in date type (not number or string), it allows you to use all of the date aggregation operators in following aggregation operations and gives you date type on the output.

Storing and querying time intervals in MongoDB

Modeling shift planning app:
I came up with such data structure describing the shift.
{
"fromHour" : 7,
"fromMinute" : 30,
"toHour" : 9,
"toMinute" : 30,
"week" : 5,
"date" : "2015-01-26",
"user" : {
// ...
},
"_id" : ObjectId("54d0e4a82b9dc26c0c0f36e7")
}
The main thing that I need to store is the information:
1. when shift starts (hour and minute),
2. when shift ends (hour and minute) and
3. what date it's actually happening.
The fields fromHour, fromMinute, toHour, toMinute and date as ISO string worked pretty good for me for storing and querying the shifts by particular date.
The problem occurred when I needed to build reports out of it. Say, I want to get all shifts from "2015-01-01" to "2015-02-01" in range from 07:00, till 23:00.
I can add $and clause to my query, like
[ { fromHour: { '$gte': 7 } },
{ fromMinute: { '$gte': 0 } },
{ toHour: { '$lt': 23 } },
{ toMinute: { '$lt': 0 } } ]
But that doesn't work good, since for shift there toMinute is 30 the $lt will be false.
I'm trying to find efficient data structure that would allow to store timespans that would be easy to query.
Storing hours and minutes in two different fields separated is too error-prone and makes your job harder. Since Mongo does not have a distinct "Time" data type, only Date, and shifts usually start and end at "easy" times, I would recommend to implement something like converting the time to a real number in your application like this:
00:00 --> 0
01:00 --> 1
...
08:00 --> 8
08:15 --> 8.25
08:30 --> 8.5
...
16:30 --> 16.5
...
It is a bit of extra work in the app because you have to convert while saving or displaying but it's still better than having one single time value in two different fields.
So your data would look like this:
{
"shiftStart" : 7.5,
"shiftEnd" : 9.5,
"week" : 5,
"date" : "2015-01-26",
"user" : {
// ...
},
"_id" : ObjectId("54d0e4a82b9dc26c0c0f36e7")
}
and your query:
[ { shiftStart: { '$gte': 7 } },
{ shiftEnd: { '$lt': 23 } } ]
You can store the data with the date type in ISODate(...) format and then use $project and Date Aggregation Operators to query the data.
For your example:
db.shifts.aggregate([
{ $match: //matches the dates first to filter out before $project step
{ datetimeStart:
{ $gte: ISODate("2015-01-01T07:00:00.000Z"),
$lt: ISODate("2015-02-01T00:00:00.000Z")
},
datetimeEnd:
{ $gte: ISODate("2015-01-01T07:00:00.000Z"),
$lt: ISODate("2015-02-01T00:00:00.000Z")
}
}
},
{ $project: // $project step extracts the hours
{ otherNeededFields: 1, // any other fields you want to see
datetimeStart: 1,
datetimeEnd: 1,
hourStart: { $hour: "$datetimeStart" },
hourEnd: { $hour: "$datetimeEnd" }
}
},
{ $match: // match the shift hours
{ hourStart: { $gte: 7 },
hourEnd: { $lte: 23 }
}
}
])
With this system it would be possible, but complicated to find something more like shifts between 7:30AM and 10:30PM:
db.shifts.aggregate([
{ $match: //matches the dates first to filter out before $project step
{ datetimeStart:
{ $gte: ISODate("2015-01-01T07:30:00.000Z"),
$lt: ISODate("2015-02-01T00:00:00.000Z")
},
datetimeEnd:
{ $gte: ISODate("2015-01-01T07:30:00.000Z"),
$lt: ISODate("2015-02-01T00:00:00.000Z")
}
}
},
{ $project: // $project step extracts the hours
{ otherNeededFields: 1, // any other fields you want to see
datetimeStart: 1,
datetimeEnd: 1,
hourStart: { $hour: "$datetimeStart" },
minStart: { $minute: "$datetimeStart" },
hourEnd: { $hour: "$datetimeEnd" },
minEnd: { $minute: "$date
}
},
{ $match: // match the shift hours
{ $or:
[
{hourStart: 7, minStart: {$gte: 30}}, // hour is 7, minute >= 30
{hourStart: { $gte: 8 }} // hour is >= 8
],
$or:
[
{hourEnd: 22, minEnd: {$lte: 30}}, // hour is 22, minute <= 30
{hourEnd: { $lte: 21 }} // hour is <= 21
]
}
}
])

How to aggregate by year-month-day on a different timezone

I have a MongoDB whom store the date objects in UTC. Well, I want to perform aggregation by year,month day in a different timezone (CET).
doing this, works fine for UTC:
BasicDBObject group_id = new BasicDBObject("_id", new BasicDBObject("year", new BasicDBObject("$year", "$tDate")).
append("month", new BasicDBObject("$month", "$tDate")).
append("day", new BasicDBObject("$dayOfMonth", "$tDate")).
append("customer", "$customer"));
BasicDBObject groupFields = group_id.
append("eventCnt", new BasicDBObject("$sum", "$eventCnt"));
BasicDBObject group = new BasicDBObject("$group", groupFields);
or, if you use the command line (not tested, I only tested the java version):
{
$group: {
_id: {
"year": {
"$year", "$tDate"
},
"month": {
"$month", "$tDate"
},
"day": {
"$dayOfMonth", "$tDate"
},
"customer": "$customer"
},
"eventCount": {
"$sum": "$eventCount"
}
}
}
How do I convert these dates into CET inside the aggregation framework?
For example '2013-09-16 23:45:00 UTC' is '2013-09-17 00:45:00 CET', this is a different day.
I'm not an expert on CET and its relation to UTC, but the following code (for the shell) should do a proper conversion (adding an hour) to a MongoDB date type:
db.dates.aggregate(
{$project: {"tDate":{$add: ["$tDate", 60*60*1000]}, "eventCount":1, "customer":1}}
)
If you run that project command before the rest of your pipeline, the results should be in CET.
You can provide the timezone to the date operators starting in 3.6.
Replace the timezone with your timezone.
{
"$group":{
"_id":{
"year":{"$year":{"date":"$tDate","timezone":"America/Chicago"}},
"month":{"$month":{"date":"$tDate","timezone":"America/Chicago"}},
"dayOfMonth":{"$dayOfMonth":{"date":"$tDate","timezone":"America/Chicago"}}
},
"count":{"$sum":1}
}
}
After searching for hours, this is the solution that worked for me. It is also very simple. Just convert the timezone by subtracting the timezone offset in milliseconds.
25200000 = 7 hour offset // 420 min * 60 sec * 1000 mili
$group: {
_id = {
year: { $year : [{ $subtract: [ "$timestamp", 25200000 ]}] },
month: { $month : [{ $subtract: [ "$timestamp", 25200000 ]}] },
day: { $dayOfMonth : [{ $subtract: [ "$timestamp", 25200000 ]}] }
},
count = {
$sum : 1
}
};
Use for example moment.js to dertmine the current timezone offset for CET but this way you get the summer&winter offsets
var offsetCETmillisec = moment.tz.zone('Europe/Berlin').offset(moment())* 60 * 1000;
$group: {
_id: {
'year': {'$year': [{ $subtract: [ '$createdAt', offsetCETmillisec ]}] },
'month': {'$month': [{ $subtract: [ '$createdAt', offsetCETmillisec ]}] },
'day': {'$dayOfMonth': [{ $subtract: [ '$createdAt', offsetCETmillisec ]}] }
},
count: {$sum: 1}
}
}
MongoDB's documentation suggests that you save the timezone offset alongside the timestamp:
var now = new Date();
db.data.save( { date: now,
offset: now.getTimezoneOffset() } );
This is of course not the ideal solution – but one that works, until we have in MongoDb's aggregation pipeline a proper $utcOffset function.
The solution with timezone is a good one, but in version 3.6 you can also format the output using timezone, so, you get the result ready for use:
{
"$project":{
"year_month_day": {"$dateToString": { "format": "%Y-%m-%d", "date": "$tDate", "timezone": "America/Chicago"}}
},
"$group":{
"_id": "$year_month_day",
"count":{"$sum":1}
}
}
Make sure that your "$match" also considers timezone, or else you will get wrong results.
Mongo stores the dates in UTC,
so this is the procedure to get them in other zone
check that mongo saves the dates in UTC, insert some records etc.
get timezone offset with moment-timezone.js eg moment().tz('Europe/Zagreb').utcOffset() functions, for your
specified timezone
Prepare $gte and $lte for $match stage (eg user input for dates 1.1.2019 - 13.1.2019.):
If offset is positive subtract() those seconds in $match stage; If offset is negative add() those seconds in $match stage
Then normalize the dates (because $match stage will return them in UTC) to your zone like this:
-if timezone offset is positive add() those seconds in $project stage;
-if timezone offset is negative subtract() those seconds in $project stage.
$group goes last, this is important (because we want to group normalized results, and not $match-ed)
Basically it is this: shift input(s) to $match(UTC), and then normalize to your timezone.
<?php
date_default_timezone_set('Asia/Karachi');
$date=getdate(date("U"));
$day = $date['mday'];
$month =$date['mon'];
$year = $date['year'];
$currentDate = $year.'-'.$month.'-'.$day;
?>