MongoDB aggregation add months or years to a Date - mongodb

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" }
}
}
}
}
])

Related

How to get the results from the last x days/months/years in MongoDB

I am trying to get a set of results in MongoDB (version 4.2) using only MongoDB Query Language where a specific date is greater than 30 days before today (that is, I need the data from the last 30 days, starting at 00:00h of the day 30 days ago).
I have tried this, but it does not yield any results:
{
date: {
$gte: {
$dateFromParts: {
year: {
$year: new Date()
},
month: {
$month: new Date()
},
day: {
$add: [
{$dayOfMonth: new Date()},
-30
]
}
}
}
}
}
Obviously, I checked that there were results when I entered the date directly this way (being today the 13th of December of 2022):
{date: {$gte: ISODate('2022-11-13')}}
Thank you for the help!
UPDATE 1:
Thanks to #wernfried-domscheit, I came to this solution, although it is very slow. If there was a solution that does not involve using $expr...
{
$expr: {
$gte: [
"$date",
{
$dateFromParts: {
year: {
$year: new Date()
},
month: {
$month: new Date()
},
day: {
$add: [
{
$dayOfMonth: Date()
},
-30
]
}
}
}
]
}
}
Try this one:
db.collection.find({
$expr: {
$gt: [
"$date",
{
$dateSubtract: {
startDate: { $dateTrunc: { date: ISODate(), unit: "day" } },
unit: "day",
amount: 30
}
}
]
}
})
For older version of MongoDB, use
db.collection.find({
$expr: {
$gt: [
"$date",
{
$subtract: [
{
$dateFromParts: {
year: { $year: ISODate() },
month: { $month: ISODate() },
day: { dayOfMonth: ISODate() }
}
},
1000 * 60 * 60 * 24 * 30
]
}
]
}
})

MongoDB group results by time interval

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

MongoDB - How to get last month records

How can I fetch last month's records in MongoDB?
Following is the MYSQL query used to do the same:
SELECT * FROM table
WHERE YEAR(createdon) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH)
AND MONTH(createdon) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
Solution 1: $where operator for db.collection.find()
Supported version: Before 4.4
You can use $where which is supported with Javascript function().
db.collection.find({
$where: function() {
var currentDate = new Date();
var lastMonthDate = new Date(currentDate.setMonth(currentDate.getMonth() - 1));
return this.createdOn.getFullYear() === lastMonthDate.getFullYear()
&& this.createdOn.getMonth() === lastMonthDate.getMonth();
}
})
Note:
Wrap the function with the quotes in Mongo Playground is due to reported Github issue.
Sample Solution 1 on Mongo Playground
Solution 2: With $dateAdd for db.collection.aggregate()
Supported version: 5.0
With $dateAdd is introduced in version 5.0, you can perform the query as below:
$match:
With $match pipeline, using $expr query expression to include the aggregation operation.
$and operator to ensure both expressions are fulfilled.
Using $dateAdd operator to add month (unit) by -1 (amount) to get previous month date.
$month and $year are for getting the month and year value from date respectively.
And perform $eq operator to check both $month and $year from createdOn are matched with both $month and $year from previous month date respectively.
db.collection.aggregate([
{
$match: {
$expr: {
$and: [
{
"$eq": [
{
$month: "$createdOn"
},
{
$month: {
$dateAdd: {
startDate: new Date(),
unit: "month",
amount: -1
}
}
}
]
},
{
"$eq": [
{
$year: "$createdOn"
},
{
$year: {
$dateAdd: {
startDate: new Date(),
unit: "month",
amount: -1
}
}
}
]
}
]
}
}
}
])
Sample Solution 2 on Mongo Playground
Solution 3: With $dateFromParts for db.collection.aggregate()
Supported version: From 3.6
You may also apply with $dateFromParts which is suggested by this Jira comment.
db.collection.aggregate([
{
$match: {
$expr: {
$and: [
{
"$eq": [
{
$month: "$createdOn"
},
{
$month: {
$let: {
"vars": {
"todayDate": new Date()
},
"in": {
$dateFromParts: {
year: {
$year: "$$todayDate"
},
month: {
$subtract: [
{
$month: "$$todayDate"
},
1
]
},
day: {
$dayOfMonth: "$$todayDate"
}
}
}
}
}
}
]
},
{
"$eq": [
{
$year: "$createdOn"
},
{
$year: {
$let: {
"vars": {
"todayDate": new Date()
},
"in": {
$dateFromParts: {
year: {
$year: "$$todayDate"
},
month: {
$subtract: [
{
$month: "$$todayDate"
},
1
]
},
day: {
$dayOfMonth: "$$todayDate"
}
}
}
}
}
}
]
}
]
}
}
}
])
Sample Solution 3 on Mongo Playground
You can select the records between two dates using the $gte and $lte options. Example:
db.getCollection('your-collection-name').find({
{createdAt:{$gte:ISODate(“2020-03-01”),$lt:ISODate(“2021-03-31”)}}
})
more information about these operators:
$lte -> https://docs.mongodb.com/manual/reference/operator/query/lte/
$gte -> https://docs.mongodb.com/manual/reference/operator/query/gte/

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.

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
}
}
])