mongodb find between given time irrespective of date - mongodb

Below query will give all user who logged in between 1-AUG-2018, 6AM to 1-AUG-2018, 8AM. Is there any way to find users who log-in between say 6AM to 8AM irrespective of date. In other words all user who log-in to any date but the time should be between 6AM-8AM.
db.user.find({
"log-time" : {"$gte": ISODate("2018-08-1T00:06:00.000Z"), "$lt": ISODate("2018-08-05T08:00:00.000Z")}
})
 

You can use $let to define temporary variable which will contain hour extracted from ISODate using $hour and then you can apply your conditions in $match inside $expr
db.user.aggregate([
{
$match: {
$expr: {
$let: {
vars: { hour: { $hour: "$log-time" } },
in: {
$and: [
{ $gte: [ "$$hour", 6 ] },
{ $lt: [ "$$hour", 8 ] }
]
}
}
}
}
}
])

Related

MongoDB : aggregate query matching a specific date from array of start and end dates

I have a mongodb collection of users. The users can activate and deactivate themselves whenever they want. I am storing the start and end date of each activation. Now I want to get the list of users who were active on a specific date.
A record looks like that:
{
"active" : true,
"endDates" : [
16.11.2021,
27.06.2020
],
"startDates" : [
21.10.2022,
16.10.2021,
09.04.2020
]
}
The startDates are more than endDates in the above example as the user is active at the moment.
Now if I want to check if a user was active on 12.05.2020 or 22.11.2022, it should return true, but it should return false for 17.12.2021.
I tried using unwind on both endDates and startDates like that:
collection
.aggregate([
{ $match: {} },
{ $unwind: '$endDates' },
{ $unwind: '$startDates' },
])
But it gave me all possible combinations of start and end dates and returns 6 documents which is not useful to find the date range.
One option to format your data is to use $dateFromString to format these strings to proper dates, and $zip with $reverseArray to couple them in the right order:
db.collection.aggregate([
{$project: {
endDates: {
$map: {
input: "$endDates",
in: {$dateFromString: {
dateString: "$$this",
format: "%d.%m.%Y"
}}
}
},
startDates: {
$map: {
input: "$startDates",
in: {$dateFromString: {
dateString: "$$this",
format: "%d.%m.%Y"
}}
}
}
}},
{$project: {
dates: {
$zip: {
inputs: [
{$reverseArray: "$startDates"},
{$reverseArray: "$endDates"}
],
useLongestLength: true
}
}
}}
])
See how it works on the playground example
Now, if you want to check a specific date, you can add one more step to $filter your array according to your requested date and replace the result with a boolean:
{$project: {
res: {
$toBool: {$size: {
$filter: {
input: "$dates",
cond: {
$and: [
{$gte: [ISODate("2021-12-17T00:00:00Z"), {$first: "$$this"}]},
{$or: [
{$lt: [ISODate("2021-12-17T00:00:00Z"), {$last: "$$this"}]},
{$eq: [{$last: "$$this"}, null]}
]}
]
}
}
}}
}
}}
See how it works on the playground example

MongoDB - Dates between using $match

So I try to use MongoDB $match to get data between 2 dates, but it turns out that the data is not returning a.k.a empty here. What it looks like:
db.collection.aggregate([
{
$match: {
date: {
$gte: new Date("2022-10-23"),
$lt: new Date("2022-10-25"),
},
}
},
{
$group: {
_id: "$title",
title: {
$first: "$title"
},
answer: {
$push: {
username: "$username",
date: "$date",
formId: "$formId",
answer: "$answer"
}
}
}
},
])
Here is the data that I try to run on the Mongo playground:
https://mongoplayground.net/p/jKx_5kZnJNz
I think there is no error with my code anymore... but why it gives an empty return.
Migrate the comment to the answer post for the complete explanation.
Issue 1
The document contains the date field as a string type while you are trying to compare with Date which leads to incorrect output.
Ensure that you are comparing both values in the exact type.
Either that migrate the date value to Date type or
converting the date field to Date type in the query via $toDate.
{
$match: {
$expr: {
$and: [
{
$gte: [
{
$toDate: "$date"
},
new Date("2022-10-23")
]
},
{
$lt: [
{
$toDate: "$date"
},
new Date("2022-10-25")
]
}
]
}
}
}
Issue 2
Since you are using $lt ($lt: new Date("2022-10-25")), it won't include the documents with date: new Date("2022-10-25").
For inclusive end date, you shall use $lte.
Demo # Mongo Playground

Get document on subarray containing date between aggregation mongodb

Provided following collection:
[
{
events: [
{
triggers: [
{
date: "2019-12-12T23:00:00"
}
]
}
]
}
]
I want to be able to pull the documents that have any date in between a range of dates, let's say today and tomorrow.
Using following query:
db.collection.aggregate([
{
$match: {
"events.triggers.date": {
$gte: "2019-12-11T23:00:00.000Z",
$lt: "2019-12-12T23:59:00.000Z"
}
}
}
]);
However, when I do this, the query seems to be looking at any document that has any date greater than and any date lower than but not necessarily in the same "trigger" object.
Anyone got any idea how you can filter in a subarray like this (I do more in my query afterwards so a find will not work) and have the date search be subitem specific?
You are almost there, just some mistakes in your query. This should work:
db.collection.aggregate([
{
'$match': {
'$and': [
{"events.triggers.date": { '$gte': "2019-12-11T23:00:00.000Z" }},
{"events.triggers.date": { '$lt': "2019-12-11T23:00:00.000Z" }}
]
}
}
]);
So I found it eventually.
Those looking for the solution. Here it is:
elemMatch
db.collection.aggregate([
{
$match: {
"events.triggers": {
$elemMatch: {
"date": {
$gte: "2019-12-11T23:00:00.000Z",
$lt: "2019-12-12T23:59:00.000Z"
}
}
}
}
}
]);

Date range not working in aggregation pipeline, but works in find()

I am trying to filter data by a date range. Example return the data that was created no more than 14 days ago.
I can do this in find with the following:
{
$match: {
eventTime: { $gte: startTime.toDate(), $lte: endTime.toDate() }
}
}
eventTime is an ISO date as well as startTime and endTime
I am using an aggregation and a lookup and trying to implement the same thing:
{
$lookup:
{
from: "data",
let: { dataId: "$dataId", patientId: "$patientId" },
pipeline: [
{
$match:
{
$expr:
{
$and:
[
{ $eq: ["$patientId", patientId] },
{ $eq: ["$dataId", "$$dataId"] },
{ $gte: ["$eventTime", startTime.toDate()] },
{ $lte: ["$eventTime", endTime.toDate()] },
]
}
}
}
],
as: "data"
}
}
But no data results are returned. If I remove the dates I get all the correct data based on dataId and patient. so the join is working.. but somehow the date range is not.
Again both the eventTime and startTime and endTime are all ISO dates.
example :
let endTime = Moment(new Date());
let startTime = Moment().subtract(days, "days");
"eventTime": "2019-08-07T03:37:40.738Z"
startTime "2019-07-30T00:02:11.611Z"
endTime "2019-08-13T00:02:11.610Z"
End time is 'today'
so in the example here the data time is between the two dates and should be returned.
I looked there : https://docs.mongodb.com/manual/reference/operator/aggregation/gte/
and it should work.. but not the case
I tried:
{eventTime: { '$gte': new Date(startTime), $lte: new Date(endTime)}}
and I get:
MongoError: An object representing an expression must have exactly one field: { $gte: new Date(1564495211043), $lte: new Date(1565704811042) }
also tried:
{ eventTime: {'$gte': new Date(startTime)}}
and get:
MongoError: Expression $gte takes exactly 2 arguments. 1 were passed in.
also tried:
{ $eventTime: {'$gte': new Date(startTime)}}, {$eventTime: {'$lte': new Date(endTime)}}
and get: MongoError: Unrecognized expression '$eventTime'
Any insight would certainly be appreciated
I was able to get it working via toDate:
{
$match:
{
$expr:
{
$and:
[
{ $eq: ["$patientId", patientId] },
{ $eq: ["$dataId", "iraeOverallAlert"] },
{ "$gte": [ {$toDate: "$eventTime"}, startTime.toDate()] },
{ "$lte": [ {$toDate: "$eventTime"}, endTime.toDate()] },
]
}
}
},
Note: This was not needed in the find, but somehow was needed using aggregation. Makes no sense but yah for trial and error.

mongodb find oldest date of three keys in each document

I have a document schema that looks like this:
{
status: String,
estimateDate: Date,
lostDate: Date,
soldDate: Date,
assignedDate: Date
}
With this schema all three dates could exists and none of them could exists. I need to do a check of all three and if at least one exists use the oldest date if none exists use todays date. With the "returned" date, get the difference in days from another key (assignedDate). I have figured out how to do what I want with one date but cannot figure out how to scale this up to include all three keys. Below is the working code I have for one key.
Within my aggregate pipeline $project stage I do the following:
days: {
$cond: {
if: {
$not: ["$date1"]
},
then: {
$floor: {
$divide: [
{
$subtract: [new Date(), "$assignedDate"]
},
1000 * 60 * 60 * 24
]
}
},
else: {
$floor: {
$divide: [
{
$subtract: [
"$estimateDate",
"$assignedDate"
]
},
1000 * 60 * 60 * 24
]
}
}
}
}
You can use $min and $ifNull operators to get oldest date specify new Date() as default value if any of those dates does not exist:
db.col.aggregate([
{
$project: {
oldest: {
$min: [
{ $ifNull: [ "$lostDate", new Date() ] },
{ $ifNull: [ "$soldDate", new Date() ] },
{ $ifNull: [ "$assignedDate", new Date() ] },
]
}
}
}
])