Can I query MongoDB ObjectId by date? - javascript

I know that ObjectIds contain the date they were created on. Is there a way to query this aspect of the ObjectId?

Popping Timestamps into ObjectIds covers queries based on dates embedded in the ObjectId in great detail.
Briefly in JavaScript code:
/* This function returns an ObjectId embedded with a given datetime */
/* Accepts both Date object and string input */
function objectIdWithTimestamp(timestamp) {
/* Convert string date to Date object (otherwise assume timestamp is a date) */
if (typeof(timestamp) == 'string') {
timestamp = new Date(timestamp);
}
/* Convert date object to hex seconds since Unix epoch */
var hexSeconds = Math.floor(timestamp/1000).toString(16);
/* Create an ObjectId with that hex timestamp */
var constructedObjectId = ObjectId(hexSeconds + "0000000000000000");
return constructedObjectId
}
/* Find all documents created after midnight on May 25th, 1980 */
db.mycollection.find({ _id: { $gt: objectIdWithTimestamp('1980/05/25') } });

In pymongo, it can be done this way:
import datetime
from bson.objectid import ObjectId
mins = 15
gen_time = datetime.datetime.today() - datetime.timedelta(mins=mins)
dummy_id = ObjectId.from_datetime(gen_time)
result = list(db.coll.find({"_id": {"$gte": dummy_id}}))

Using inbuilt function provided by mongodb drivers in in Node.js lets you query by any timestamp:
var timestamp = Date.now();
var objectId = ObjectID.createFromTime(timestamp / 1000);
Alternatively, to search for records before the current time, you can simply do:
var objectId = new ObjectID(); // or ObjectId in the mongo shell
Source: http://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html

You can use $convert function to extract the date from ObjectId starting in 4.0 version.
Something like
$convert: { input: "$_id", to: "date" }
You can query on date comparing between start and end time for date.
db.collectionname.find({
"$expr":{
"$and":[
{"$gte":[{"$convert":{"input":"$_id","to":"date"}}, ISODate("2018-07-03T00:00:00.000Z")]},
{"$lte":[{"$convert":{"input":"$_id","to":"date"}}, ISODate("2018-07-03T11:59:59.999Z")]}
]
}
})
OR
You can use shorthand $toDate to achieve the same.
db.collectionname.find({
"$expr":{
"$and":[
{"$gte":[{"$toDate":"$_id"}, ISODate("2018-07-03T00:00:00.000Z")]},
{"$lte":[{"$toDate":"$_id"},ISODate("2018-07-03T11:59:59.999Z")]}
]
}
})

how to find Find the Command (this date[2015-1-12] to this Date[2015-1-15]):
db.collection.find({
_id: {
$gt: ObjectId(Math.floor((new Date('2015/1/12'))/1000).toString(16) + "0000000000000000"),
$lt: ObjectId(Math.floor((new Date('2015/1/15'))/1000).toString(16) + "0000000000000000")
}
}).pretty()
Count the Command (this date[2015-1-12] to this Date[2015-1-15]):
db.collection.count({
_id: {
$gt: ObjectId(Math.floor((new Date('2015/1/12'))/1000).toString(16) + "0000000000000000"),
$lt: ObjectId(Math.floor((new Date('2015/1/15'))/1000).toString(16) + "0000000000000000")
}
})
Remove the Command (this date[2015-1-12] to this Date[2015-1-15]):
db.collection.remove({
_id: {
$gt: ObjectId(Math.floor((new Date('2015/1/12'))/1000).toString(16) + "0000000000000000"),
$lt: ObjectId(Math.floor((new Date('2015/1/15'))/1000).toString(16) + "0000000000000000")
}
})

Since the first 4 bytes of an ObjectId represent a timestamp, to query your collection chronologically, simply order by id:
# oldest first; use pymongo.DESCENDING for most recent first
items = db.your_collection.find().sort("_id", pymongo.ASCENDING)
After you get the documents, you can get the ObjectId's generation time like so:
id = some_object_id
generation_time = id.generation_time

Yes you can query object by date using MongoDB inserted ID
db.collectionname.find({_id: {$lt: ObjectId.fromDate( new ISODate("TZformat") ) } });
let's suppose users is my collection and I want all users created less than 05 January 2018
db.users.find({_id: {$lt: ObjectId.fromDate( new ISODate("2018-01-05T00:00:00.000Z") ) } });
For running from a query we can use like
db.users.find({_id: {$lt: ObjectId.fromDate(new Date((new Date().getTime() - (1 * 3 * 60 * 60 * 1000))) ) } })
All the users from the current time - 3 hours

To get last 60 days old documents in mongo collection i used below query in shell.
db.collection.find({_id: {$lt:new ObjectId( Math.floor(new Date(new Date()-1000*60*60*24*60).getTime()/1000).toString(16) + "0000000000000000" )}})

If you want to make a range query, you can do it like in this post. For example querying for a specific day (i.e. Apr 4th 2015):
> var objIdMin = ObjectId(Math.floor((new Date('2015/4/4'))/1000).toString(16) + "0000000000000000")
> var objIdMax = ObjectId(Math.floor((new Date('2015/4/5'))/1000).toString(16) + "0000000000000000")
> db.collection.find({_id:{$gt: objIdMin, $lt: objIdMax}}).pretty()

From the documentation:
o = new ObjectId()
date = o.getTimestamp()
this way you have date that is a ISODate.
Look at
http://www.mongodb.org/display/DOCS/Optimizing+Object+IDs#OptimizingObjectIDs-Extractinsertiontimesfromidratherthanhavingaseparatetimestampfield.
for more information

Using MongoObjectID you should also find results as given below
db.mycollection.find({ _id: { $gt: ObjectId("5217a543dd99a6d9e0f74702").getTimestamp().getTime()}});

A Solution Filtering within MongoDB Compass.
Based on versions:
Compass version: 1.25.0
MongoDB version: 4.2.8
Option 1:
#s7vr 's answer worked perfectly for me. You can paste this into the Filter field:
{$expr: { $and: [ {$gte: [{$toDate: "$_id"}, ISODate('2021-01-01')]}, {$lt: [{$toDate: "$_id"}, ISODate('2021-02-01')]} ] } }
Option 2:
I also found this to work (remember that the Date's month parameter is 0-based indexing so January is 0):
{_id: {$gte: ObjectId(Date(2021, 0, 1) / 1000), $lt: ObjectId(Date(2021, 1, 1) / 1000) } }
Option 3:
Equivalent with ISODate:
{_id: {$gte: ObjectId(ISODate('2021-01-01') / 1000), $lt: ObjectId(Date('2021-02-01') / 1000) } }
After writing this post, I decided to run the Explain on these queries. Here's the skinny on performance:
Option 1: 39 ms, 0 indexes used, 30 ms in COLLSCAN
Option 2: 0 ms, _id index used
Option 3: 1 ms, _id index used, 1 ms in FETCH
Based on my rudimentary analysis, it appears that option 2 is the most efficient. I will use Option 3, personally, as it is a little cleaner to use ISODate rather than remembering 0-based month indexing in the Date object.

In rails mongoid you can query using
time = Time.utc(2010, 1, 1)
time_id = ObjectId.from_time(time)
collection.find({'_id' => {'$lt' => time_id}})

Related

Mongoose lte method not working for moment

I am fetching Date saved in db. Then, I am doing a small date maths to substract date from today from 3, which is giving me Date in Format - (DD-MM-YYYY). Date saved in db format is also same - (DD-MM-YYYY). Can anyone help me out in validating $lte for that date. I am not getting any log for DipData.
nodeCron.schedule("* * * * *", async function () {
var DateNow = await moment().subtract(3, "days").format("DD-MM-YYYY");
console.log("Test Date Cron",DateNow);
console.log("-->",new Date(DateNow.format("DD-MM-YYYY")));
let DipData = await userModel.find({}, { LastAppOpenedTime: { $lte : new Date(DateNow.format("DD-MM-YYYY")) }})
console.log("-----DipData ------->", DipData);
});
First thing you need to identify if there is date which is stored in document of mongo collection is string or regular date format or epoch format. If it's string the query may gives not accurate result. If there is date format or epoch format, you can easily queried your result with proper result.
Therefore in case if there is string in LastAppOpenedTime document key you can have query with $toDate under find query.
If key is not in string format in stored document following code will work.
var DateNow = moment().subtract(3, "days");
const DipData = await userModel.find({ LastAppOpenedTime: { $lte: new Date(DateNow) } });
For the above two scenario would work if your query is in accurate form like removing the first empty braces.
userModel.find({}, { LastAppOpenedTime: { $lte : new Date(DateNow) }})
to
userModel.find({ LastAppOpenedTime: { $lte : new Date(DateNow) }})
Hello I got this working by making a few changes
const DateNow = await moment().subtract(3, "days");
console.log("Test Date Cron", DateNow);
console.log("-->", new Date(DateNow));
const DipData = await userModel.find({ createdAt: { $lte: new Date(DateNow) } });
console.log("-----DipData ------->", DipData);
res.status(200).json({ success: true, message: "Request was successful", DipData });
I noticed you had the .format("DD-MM-YYYY") at the end of your moment function but it returned a string that couldn't be converted with the new Date(DateNow). I removed mine when testing as the response from the moment was working fine without it.
And also I updated your userModel.find({}, { LastAppOpenedTime: { $lte : new Date(DateNow.format("DD-MM-YYYY")) }}) to remove the first empty {}. So you would have userModel.find({ createdAt: { $lte: new Date(DateNow) } });

TypeORM query all records from today

I am trying to query all records from today but I get nothing, so My question is:
How to query with date validations in TypeORM?
My code:
all = await connection
.createQueryBuilder(Earnings, 't0')
.addSelect('t3.id', 't3_id')
.addSelect('t3.UID', 't3_UID')
.addSelect('t3.name', 't3_name')
.addSelect('t3.chips', 't3_chips')
.addSelect('t3.tickets', 't3_tickets')
.addSelect('t3.masteredLevel', 't3_masteredLevel')
.addSelect('t2.UID', 't2_UID')
.addSelect('t2.name', 't2_name')
.addSelect('t2.rule', 't2_rule')
.addSelect('t1.id', 't1_id')
.innerJoin(Questions, 't1', 't0.questionID = t1.id')
.innerJoin(Lessons, 't2', 't1.lessonUID = t2.UID')
.innerJoin(Topics, 't3', 't2.topicUID = t3.UID')
.where('t0.challenge = 1')
.andWhere('t0.createdAt = :today')
.andWhere('t0.userID = :userID')
.setParameters({ userID: user.id })
.setParameters({ today: moment() })
.getRawMany()
Your .andWhere('t0.createdAt = :today') only selects rows created at the instant of today parameter. You have set this parameter as moment(), which is not a date.
Since we can safely assume no rows will be created in the future, your simplest solution here is: .andWhere('t0.createdAt >= :today'), which selects rows created AFTER 00:00 today.
You can combine the addWhere and setParameters into one:
.andWhere('t0.createdAt >= :today', { today: moment().toDate() }) // note moment.toDate()
Alternatively use the MySql CURDATE() function to get the current date:
.andWhere('t0.createdAt >= CURDATE()')
When you test this, I recommend that you turn on TypeOrm full logging so you can see the actual generated SQL and you will be able to quickly solve any problems. See TypeOrm logging.
For TypeORM + PSQL
Sort items by today's date
.andWhere('t0.createdAt >= CURRENT_DATE')

Query by date and time only, without year using SQLite

I am storing birthdays in my SQLite database, they are dates converted to my own timezone from another timezone, every hour I want to query my database to get the dates that are the same day and hour
// Stored in DB:
const date = parseFromTimeZone(`2020-${bdayReply} 13:42:00`, { timeZone });
// Query
const birthday = await Birthday.findOne({ where: { date: new Date() } });
// Generated query
query: SELECT "Birthday"."id" AS "Birthday_id", "Birthday"."userId" AS "Birthday_userId", "Birthday"."date" AS "Birthday_date", "Birthday"."birthdayConfigGuildId" AS "Birthday_birthdayConfigGuildId" FROM "birthday" "Birthday" WHERE "Birthday"."date" = ? LIMIT 1 -- PARAMETERS: ["2020-02-03T07:42:00.005Z"]
// Output of new Date() at the time of running the query
2020-02-03T07:42:00.023Z
// Example of record active in my DB at that point (date var from earlier)
2020-02-03 07:42:00.000
The ORM I'm using allows for raw queries as well, now I was wondering what my query should look like to return the above record in the example, or maybe I can use a date library like date-fns to convert the new Date() to match the format of the database, what would that look like?
If you want to use an SQL raw query, it should looks like this :
SELECT * FROM my_table WHERE MONTH(my_date) = MONTH(NOW()) AND DAY(my_date) = DAY(NOW())
In SQLite, the syntax is :
SELECT * FROM my_table WHERE strftime('%m',my_date) = strftime('%m','now') AND strftime('%d',my_date) = strftime('%d','now')

mongodb aggregation: How can i add minutes to a date field and then compare it

I want to make a complex query.
I'm building a cinema application.
I want to make a validation check for a new show.
it will check if the show time is in a range of another show that plays in the same theater.
In order to know the end time of the range i need to add the number of minutes of the movie to the movie start time.
Important to note: The new show time is not yet stored in the DB. so it's in different timezone (let's say +2). so when we compare the dates we should take into effect the different timezones as well.
I changed the time of the new show to utc so i guess it solves the issue.
Here is the code i tried running. obviously is not working. I don't know can i add the movie Duration to the movie start:
router.post("/", (req, res) => {
if (!req.body)
return res.sendStatus(400);
let show = new Show(req.body);
startDateTime= moment.utc(show.dateTime);
console.log('startDateTime in utc');
console.log(startDateTime);
Show.aggregate([
//next stage: add movies details
{
"$match": {
"dateTime": { $gte: new Date(startDateTime)},
"_TheaterId": show._TheaterId
}
},
{
$lookup:
{
from: 'movies',
localField: '_MovieId',
foreignField: '_id',
as: 'MovieDetails'
}
},
{
$unwind: "$MovieDetails"
},
{
"$match": {
{ $add: [ "$dateTime", $MovieDetails.durationInMin * 60000 ] }: { $lte: new Date(startDateTime)}
}
}
], function (err, result) {
if (err) {
//next(err);
return res.json(err);
} else {
res.json(result);
}
});
ANybody knows how can i add the minutes to the date field and then compare it to a different date?
Many thanks!
Here is a picture of the Db:
There is the add function provided by moment.
so assuming your startDateTime is a moment object :
startDateTime.add(20, 'minutes');
You can add minutes to the date by using the moment() as follows.
// To add the five minutes to the current date
var moment = require('moment'); // require
To get the current time using moment():
Current time:
datenow = moment()
To add the five minutes to the current date using moment().add():
datePulsFivemin = moment().add(5, 'm')//Add five minutes to the current work
To subtract the five minutes from the current date using moment().subtract():
dateSubractFivemin = moment().subtract(5, 'm') // Add a .format() if need of UTC format
To compare the date with other date using the moment().isBefore() and it will return Boolean:
//Syntax
//sourcedate.isBefore(targetdate)
moment().isBefore(datePulsFivemin)
Sample program:
var moment = require('moment'); // require
// Current date
datenow = moment()
//Add five minutes to the current date
datePulsFivemin = moment().add(5, 'm')
//subtract five minutes from the current date
dateSubractFivemin = moment().subtract(5, 'm') //Add a .format() if need of UTC format
console.log(moment()) // Moment<2022-12-10T19:36:09+05:30>
console.log(datePulsFivemin.format()) // 2022-12-10T19:41:09+05:30
console.log(dateSubractFivemin.format()) // 2022-12-10T19:31:09+05:30
// TO compare the date
console.log(moment().isBefore(datePulsFivemin)) // true
console.log(moment().isBefore(dateSubractFivemin)) // false

Find document by timestamp using mongojs

I want to find document in a mongodb table using node js.
I'm currently working with mongojs plugin.
Here's what I have problem with:
I connect into DB.
I get the current timestamp
Every 10 seconds I want to print all elements added within this 10 seconds.
var timestamp = new Date().getTime();
console.log('timestamp to compare: ' + timestamp);
setInterval(function() {
var x = db.collection.find({'create_time' : {$gt : timestamp}}).toArray(function(err, entity) {
console.log(entity);
});
console.log('checking...')
timestamp = new Date().getTime();
console.log('timestamp to compare: ' + timestamp);
}, 10000);
Somehow I'm getting no results. Below you can see the command prompt output.
http://s11.postimg.org/a8cnffedf/2015_03_11_1521.png
I'll apreciate any help. Thank you.
First, ensure that mongo recognizes the create_time property as a date. The easiest way to do that, is to insert standard javascript date instances:
db.collection.insert([{
create_time: new Date(),
...
}], callback);
Then, to query, again use date instances:
var now = new Date();
var tenMinutesAgo = new Date(now - 10*60*1000);
db.collection.find({
$gt: tenMinutesAgo
}).toArray(callback);
That should do the trick!

Categories

Resources