Mongo database query operation question on dates as string - mongodb

Let’s say some external component inserts data into my mongo database with Field called “startDate”. They store the value as string in mongo db in format yyyymmdd-HHmmss.
Can I still run the date time queries to fetch documents between two dates ?
Will below work as expected for above scenario?
db.test.find({ startDate:{$gt: “20201121”, $lte: “20210121”}})

Yes, it will but not for the same end date. It'd be better to add a day to cater for trailing zeros.
//data
[
{
startDate: "20210101-010101"
},
{
startDate: "20210121-000000"
},
{
startDate: "20210121-010000"
}
]
// query 1 - not successful maybe
[
{
startDate: "20210101-010101"
},
{
startDate: "20210121-000000"
},
{
startDate: "20210121-010000"
}
]
//query 2, successful
db.collection.find({
startDate: {
$gt: "20201121",
$lt: "20210122"
}
})

Related

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

How to query with Dates in Aggregate functions in mongodb

I'm using Metabase v0.41 and writing query to Cosmos DB as a mongoDb.
Metabase only allows execution of db.collection.aggregate, and I first want a match stage that does a basic string match and date range.
In SQL it'd be something like this:
SELECT *
FROM TABLE
WHERE TABLE.COLUMN = 'WORD' AND TABLE.CREATEDAT > DATE_ADD(NOW(), INTERVAL -1 DAY)
and this is what I have for the equivalent mongo query.
So here are several attempts that I tried in the match query
1)
{
$and: [
{
column: {$eq: "word" }
},
{
createdAt:{$lt : //new ISODate()
{
$dateAdd:{
startDate: new ISODate(),
unit: "day",
amount: -1
}
}
}
}
]
}
-> doesn't work even on mongo compass,this returns no results even if there are matching records in the database. but no syntax error either.
Subtract the difference in millisec
{
$and: [
{
column: {$eq: "word" }
},
{
createdAt:{$lt :new ISODate() -1231312321}
}
]
}
-> this does work on mongo compass, but not on Metabase.
the parser on the Metabase doesn't parse the number properly.
"JSON reader was expecting a name but found '-1231312321'."
I tried the subtraction using $subtract but still doesn't work.
Have any ideas?
Try
{ $expr:
{ $lt: [
"$createdAt",
{
$dateSubtract:{
startDate: "$$NOW",
unit: "day",
amount: 1
}
}]
}
}

MongoError: can’t convert from BSON type string to Date in Meteor

In Meteor JS I want to find users whose birthday is today.I have this piece of code that runs fine on my computer (locally) but it fails in production :
let today = new Date()
let users = Meteor.users.find({
"status.lastLogin": { $not: { $eq: null } },
$expr: {
$and: [
{
$eq: [
{
$dayOfMonth: {
date: "$profile.birthdate",
timezone: "Europe/Paris",
},
},
today.getDate(),
],
},
{
$eq: [
{
$month: {
date: "$profile.birthdate",
timezone: "Europe/Paris",
},
},
today.getMonth() + 1,
],
},
],
},
})
My server is hosted on Galaxy and the DB on mongodb.com
I checked the profile.birthdate type and it is Date on mongodb.com
The error is :
MongoError: can’t convert from BSON type string to Date\n at
Connection.
(/app/bundle/programs/server/npm/node_modules/meteor/npm-mongo/node_modules/mongodb/lib/core/connection/pool.js:450:61)\n
at Connection.emit (events.js:311:20)\n at
Connection.EventEmitter.emit (domain.js:482:12)\n at processMessage
(/app/bundle/programs/server/npm/node_modules/meteor/npm-mongo/node_modules/mongodb/lib/core/connection/connection.js:384:10)\n
at TLSSocket.
Does anyone know why this is happening and how can I fix it?
Edit: By following #Jankapunkt advice to use aggregate and by reading this post, I was able to write a better (I think...) query and now it is working.
This is the new code:
const today = new Date()
let users = Meteor.users.aggregate(
{
$project: {
status: "$status",
roles: "$roles",
month: {
$month: {
date: "$profile.birthdate",
timezone: "Europe/Paris",
},
},
day: {
$dayOfMonth: {
date: "$profile.birthdate",
timezone: "Europe/Paris",
},
},
},
},
{
$match: {
"status.lastLogin": { $ne: null },
roles: "candidate",
month: today.getMonth() + 1,
day: today.getDate(),
},
}
)
There are several issues here, I'd like to address:
$expr is usually only required in rare cases or when matching against a regular expression.
$dayOfMonth is an aggregate operator and not available in basic queries, but there are packages available
Meteor has builtin Date support through EJSON, which extends BSON by custom types (it abstracts the type conversion away for you):
Meteor.publish('allbirthdays', function () {
const today = new Date()
// correct timezone here
return Meteor.users.find({ '$profile.birthdate': today })
}
No need to convert Date to some mongo operators etc.
$and is a contradiction if differnt values are both required for the same field (birthdate can never be today and today in a month), did you intend to use $or?
{ $not: { $eq: null } } can be written as { $ne: null }
Always disable the services field if you publish users! Services contains the (hashed) password and other oauth providers, including resume token, which could lead to serious security issues:
Saving / querying Dates without aggregate
The above methods allow only an exact Date matches, because MongoDB provides Date-specific query only through aggregate.
Therefore, your options are:
A) Use the aggregate package to build $expr for $month and $dayOfMonth as in your example code
B) Create the birthday only as locale field (which makes it a String type):
export const getDate = () => {
// todo apply locale, timezone etc.
return new Date().toLocaleString(undefined, {
day: 'numeric', month: 'long'
})
}
and save it in the user's collection as a separate field (e.g. birthDay):
Meteor.users.update(userId, {
$set: {
'$profile.birthDay': getDate() // "June 3"
}
})
ans query only for this day:
Meteor.publish('allbirthdays', function () {
const today = getDate()
return Meteor.users.find({ '$profile.birthDay': today })
}
C) Save the month and day as Number types in separate fields:
const today = new Date()
Meteor.users.update(userId, {
$set: {
'$profile.birthDay': today.getDate() // 3
'$profile.birthMon': today.getMonth() // 6
}
})
ans query only for this day:
Meteor.publish('allbirthdays', function () {
const today = new Date()
return Meteor.users.find({
'$profile.birthDay': today.getDate()
'$profile.birthMon': today.getMonth()
})
})

Two date find between two data in collection

I have below collection data in mongo db.
"enddate" : ISODate("2019-03-27T14:30:00.000Z"),
"date" : ISODate("2019-03-27T10:30:00.000Z"),
I have two date like start date "2019-03-26T19:30:00.000Z" and end date "2019-03-26T20:30:00.000Z"
I want to find above two date time period exits in collection or not.
Please help to make mongodb query.
advt.date = m.utc().toISOString();
advt.enddate = me.utc().toISOString();
advt.time = m.utc().toISOString();
advt.endtime = me.utc().toISOString();
var Query = Advertisement.find({
$or: [
{ $and:[{ date: { $gte: advt.date }, enddate:{ $lte: advt.enddate } }] } ,
{ $and:[{ enddate: { $gte: advt.date }, date:{ $lte: advt.enddate } }] }
],"ad_type":"splash", "isDeleted":false, "_id":{ $ne: advt._id }
});
You can use ObjectId.getTimestamp() for that purpose.
Also check this Link

MongoDB how to fetch documents based on multiple dates

I have a collection of events, each have a start date. I want to fetch data starting on specific dates only. How will i do that ? I found a lot of solutions for date ranges but this case is different. If client inputs two dates i need to fetch only the events starting on those two dates, no need of dates in between. I tried using $or, $in etc but no use. The below code will not work.
let query = {
startDate: {
$or: [
{
$gte: new Date("2018-05-10T00:00:00.000Z"),
$lt: new Date("2018-05-10T23:59:59.000Z")
},
{
$gte: new Date("2018-08-06T00:00:00.000Z"),
$lt: new Date("2018-08-06T23:59:59.000Z")
}
]
}
};
Found the answer.
let query = {
$or: [
{
startDate: {
$gte: new Date("2018-05-10T00:00:00.000Z"),
$lt: new Date("2018-05-10T23:59:59.000Z")
}
},
{
startDate: {
$gte: new Date("2018-08-06T00:00:00.000Z"),
$lt: new Date("2018-08-06T23:59:59.000Z")
}
}
]
};