Searching date in mongodb - mongodb

In mongodb collection i have field called event_startdate Which is in following format
event_startdate:2018-10-14 16:00:00.000 (type:date)
i am storing date and time together is this field and want to serach event which is happening today and using query given bellow
var date= moment().format('YYYY-MM-D')
Event.find({ event_startdate:date}).exec()
Since time also attached to event_startdate i am unable to fetch today's detail.
is there a way to find this?

var date= moment().format('YYYY-MM-D') would give you a string which then you are trying to compare to an ISO date in mongo.
Try something among these lines:
db.getCollection('COLNAME').aggregate([
{
"$match": {
"$expr": {
$eq: [
"2018-10-10", // <--- You string date goes here
{
"$dateToString": {
"date": "$date",
"format": "%Y-%m-%d"
}
}
]
}
}
}
])
This is using the $expr pipeline operator with dateToString.

You can also do using find.
db.getCollection('loginDetail').find({
"$expr": {
$eq:
[ "2018-09-21", { "$dateToString": { "date": "$eventDate", "format": "%Y-%m-%d"
}
}
]
}
})

Related

How to write a query to find the mongoDB documents whose time difference between two Date fields is larger than a certain value?

I have a mongoDB that contains documents like this:
The data types of start_local_datetime and last_update_local_datetime are both Date.
How can I find the documents whose difference between last_update_local_datetime and start_local_datetime is larger than 10 days?
I mean I want to query data like this:
start_local_datetime: 2019-08-23T10:17:42.000+00:00
terminate_local_datetime: 2019-09-19T10:17:42.000+00:00
Documents like this aren't something that I want.
start_local_datetime: 2019-08-23T10:17:42.000+00:00
terminate_local_datetime: 2019-08-25T10:17:42.000+00:00
Because terminate_local_datetime - start_local_datetime is smaller than 10 days.
You can write an aggregation pipeline, using $dateDiff operator, like this:
db.collection.aggregate([
{
"$match": {
$expr: {
"$gt": [
{
"$dateDiff": {
"startDate": "$start_local_datetime",
"unit": "day",
"endDate": "$terminate_local_datetime"
}
},
10
]
}
}
}
])
See it working here. However, this will only work in Mongo 5.0 or above, as the operator was added in that version. For other versions, this will work
db.collection.aggregate([
{
"$addFields": {
"timeDifference": {
"$divide": [
{
"$subtract": [ <-- Returns difference between two dates in milliseconds
"$terminate_local_datetime",
"$start_local_datetime"
]
},
86400000
]
}
}
},
{
"$match": {
$expr: {
"$gt": [
"$timeDifference",
10
]
}
}
},
{
"$project": {
timeDifference: 0
}
}
])
Here, we calculate the time difference manually and then compare it, with 10.
This is the playground link.

MongoDB $type check on a field inside $cond

I'm new to MongoDB and my requirement is to convert a string date to date. But that particular field is sometimes in date format sometimes in string format.
Effectively, If the date is in string format I want to convert it to date else leave as it is.
Sample data:
paymentDate:2021-11-19T05:36:32.596+00:00
paymentDate:'2021-11-19T05:36:32.596+00:00'
My attempt is
{
convertedDate: {
$cond: {
if:
{'$eq': [{$type:"$paymentDate"},9]},
then:"$newField",
else:{
$dateFromString: {
dateString: '$paymentDate'
}
}
}
}
}
You're almost to the answer. Specify the compare value in $type as "date".
db.collection.find({},
{
convertedDate: {
$cond: {
if: {
"$eq": [
{
$type: "$paymentDate"
},
"date"
]
},
then: "$paymentDate",
else: {
$dateFromString: {
dateString: "$paymentDate"
}
}
}
}
})
Sample Mongo Playground
References
Available type | $type
If you are using MongoDB 4.2+, you may simply use $toDate to convert your field in an update with aggregation pipeline operation.
db.collection.update({},
[
{
"$set": {
"paymentDate": {
"$toDate": "$paymentDate"
}
}
}
])
Here is the Mongo playground for your reference.

Mongo query to search between given date range while date is stored as string in db

I have db schema that has string date format("date":"2020-09-01 16:07:45").
I need to search between given date range, I know this is possible if we're using ISO date format but I'm not sure if we can query with date format being string.
I have tried the following query, it doesn't seem to show accurate results.
db.daily_report_latest.find({"date":{$gte: "2021-01-01 00:00:00", $lte:"2021-03-01 00:00:00"}},{"date":1})
Is there any alternative to this? Appreciate your help.
You're right, you can't query a date field with a string, but you can just cast it to date type like so:
Mongo Shell:
db.daily_report_latest.find({
"date": {$gte: ISODate("2021-01-01T00:00:00Z"), $lte: ISODate("2021-03-01T00:00:00Z")}
}, {"date": 1});
For nodejs:
db.daily_report_latest.find({
"date": {$gte: new Date("2021-01-01 00:00:00"), $lte: new Date("2021-03-01 00:00:00")}
}, {"date": 1});
For any other language just check what the mongo driver date type is and do the same.
Note that the mongo shell isn't able to parse the string input in the format you provided, you should read here about the supported formats and transform your string pre-query like I did.
Another thing to consider for the nodejs usecase is timezones, the string will be parsed as the machine current timezone so again you need to adjust to that.
You can use $dateFromString feature of aggregation. (Documentation)
pipeline = []
pipeline.append({"$project": {document: "$$ROOT", "new_date" : { "$dateFromString": { "dateString": '$date', "timezone": 'America/New_York' }}}})
pipeline.append({"$match":{"new_date": {"$gte": ISODate("2021-01-01 00:00:00"), "$lte":ISODate("2021-03-01 00:00:00")}}})
data = db.daily_report_latest.aggregate(pipeline=pipeline)
So in the both the solutions, first typecast the date field in DB to date and then compare it with your input date range.
SOLUTION #1: For MongoDB Version >= 4.0 using $toDate.
db.daily_report_latest.find(
{
$expr: {
$and: [
{ $gte: [{ $toDate: "$date" }, new Date("2021-01-01 00:00:00")] },
{ $lte: [{ $toDate: "$date" }, new Date("2021-03-01 00:00:00")] }
]
}
},
{ "date": 1 }
)
SOLUTION #2: For MongoDb version >= 3.6 using $dateFromString.
db.daily_report_latest.find(
{
$expr: {
$and: [
{ $gte: [{ $dateFromString: { dateString: "$date" }}, new Date("2021-01-01 00:00:00")] },
{ $lte: [{ $dateFromString: { dateString: "$date" }}, new Date("2021-03-01 00:00:00")] }
]
}
},
{ "date": 1 }
)

Mongo DB Collection Scan OR Index Scan

I have an index on "timeofcollection". There is an issue that one query using same field shown collection is being scanned while on shown index scan. These are the "$match" steps in an aggregation pipeline I am posting below. Can someone help me out explaining what is an issue and how should I handle it?
If I have following in $match step in pipeline, it evaluates as an index scan
{
"timeofcollection":{$gte:ISODate("2020-09-24T00:00:00.000+0000"),$lt:ISODate('2020-09-25T00:00:00.000+0000')}
}
If I have following step in pipeline, it evaluates as collection scan
{
$match: {
"$expr": {
"$and": [{
"$gte": [
"$_id.dt",
{
"$subtract": [{
"$toDate": {
"$dateToString": {
"date": "$$NOW",
"format": "%Y-%m-%dT00:00:00.000+0000"
}
}
},
86400000
]
}
],
},
{
"$lt": [
"$_id.dt",
{
"$toDate": {
"$dateToString": {
"date": "$$NOW",
"format": "%Y-%m-%dT00:00:00.000+0000"
}
}
}
]
}
]
}
}
}
Basically what I am trying to achieve is to pull records falling in last day. This works fine but involves collection scan which I can not do.
Any help?
The query planner will only use an index for equality comparison when using the $expr operator.
It will also only use the index when the values of the expressions are constant for the query. Since the $$NOW variable is not bound until query execution begins, and will have a different value for every execution, the query planner will not use an index for a query using that variable.
This may not be a complete answer, but one obvious problem I see with your above aggregation is that, for some reason, you seem to be converting dates to text, only to convert them back to dates again. Typically, if your filter were to contain a function of timeofcollection, then the index on timeofcollection might not be usable. Try this version:
$match: {
"$expr": {
"$and": [
{
"$gte": [
"$_id.dt",
{
"$subtract": [ "$$NOW", 86400000 ]
}
],
},
{
"$lt": [
"$_id.dt", "$$NOW",
]
}
]
}
}
Note that I am assuming here that dt in the above fragment is an alias for timeofcollection, defined somewhere earlier.
The key point here is that using timeofcollection inside a function might render your index unusable. The above version may get around this problem.

mongodb aggregate get current date

I'm using metabase and I have native mongodb query. I want to filter documents created yesterday. The problem is that I only have json. Is there any way to compute yesterday?
my json is:
[...
{
"$match": {
"createdAt": { "$ne": "$yesterday" },
}
},
...]
Unfortunately Metabase does not allow to use Date() to get now Date. also you should notice that dateFromString is available in version 3.6 of mongoDB and newer but another problem here is i think dateFromString does not work well with now Date in aggregate and mongoDB return an error that you should pass a string to convert to Date in dateFromString!
so i suggest to you to get yesterday Date in metabase write code something like this :
[{
"$project": {
"user": 1,
"createdAt": 1,
"yesterday": {
"$subtract": [ISODate(), 86400000]
}
}
},
{
"$project": {
"user": 1,
"yesterday": 1,
"createdAt": 1,
"dateComp": { "$cmp": ["$yesterday", "$createdAt"] }
}
},
{
"$match": {
"dateComp": -1
}}]
In Metabase, you can actually use dates relative to today, such as yesterday, by using Date(). This is a workaround currently implemented in the mongo driver for metabase. This workaround internally handles the date as a String, so we have to parse it before trying to use yesterday as a Date.
[
{
"$project": {
"_id": 1,
"todayHandledAsString": Date(),
"todayHandledAsDate": {"$dateFromString": {
"dateString": Date()
}}
}
},
{
"$project": {
"_id": 1,
"todayHandledAsString": 1,
"todayHandledAsDate": 1,
"yesterday": {
"$subtract": ["$todayHandledAsDate", 86400000]
}
}
}
]
Unfortunately, Metabase also disallows operations or comments, so we have to use a hardcoded value of 86400000 instead of 24 * 60 * 60 * 1000, which is a single day in millis.
pay_status is a field of current table. After map them,then you can choose.
[
{$match:{{pay_status}}},
{$match:{{pay_time}}},
{$project:{...}}
]