Query mongodb for a specific date range - mongodb

I've googled and googled this, and for the life of me I can't make it work...which I know I am just missing something totally simple, so I'm hoping someone here can save my sanity.
I have am trying to lookup a range of documents from a mongodb collection (using mongoose) based on a date range.
I have this code:
var startDate = new Date(req.query.startDate);
var stopDate = new Date(req.query.stopDate);
studentProgression.find({ $and: [ {'ACT_START': {$gte: startDate, $lte: stopDate} } ]})
But it doesn't return any documents.
The date in the MongoDb collection is stored as a string, and looks like this (for example):
ACT_START: "25-MAY-20"
a
I know I'm probaby tripping somewhere with the fact it's a string and not a date object in mongo, as I haven't had this issue before. I'd rather not go through the collection and change all the string dates to actual date objects.
Is there anyway to do this lookup the way I've laid it out?

Unfortunately, you have to change the format of your ACT_START field and cast it to a Date.
By doing $gte and/or $lte on a string field, mongo will compare the two strings based on their alphabetical order.

Related

mongodb compass query with objectId in date range

I'm trying to perform a date range query on the _id field using compass.
I've tried what I found here with the following filter:
{_id: { $gte: ObjectId.fromDate(new Date('2019-01-01')) } }
What am I missing? I'd like to get a list of all documents from some date forward (in this example from 1 Jan 2019 to present). Unfortunately there isn't a timestamp in the document fields so I need to extract it from the object id.
You need to pass a date object to the ObjectId.fromDate, not a string. Try this:
ObjectId.fromDate(new Date('2019-01-01'))
This function works only in the shell and doesn't exist in the drivers.
EDIT after comments:
Here is a solution that works in Compass as well:
{
$expr: {
$gte: [
{"$toDate":"$_id"},
ISODate("2021-01-01T00:00:00.000Z")
]
}
}
Keep in mind, however, that it requires a version of mongo of 4.0+. You can checkout the docs here.
Also, checkout this related topic: Can I query MongoDB ObjectId by date?
It is not about Compass, but it provides solutions for generating the ObjectIds from a date without being dependent on ObjectId.fromDate().
I found a solution, maybe not the best but it does the job. Hopefully this can help someone with the same problem if there isn't a cleaner solution.
I converted the date I needed to an ObjectId outside of compass online here.
Then I wrote the query with that ObjectId:
{_id: { $gte: ObjectId(' object id here ') } }
As suggested in the comments, see this related topic. It's not specific to Compass but it provides a solution for generating ObjectIds from a date without being dependent on ObjectId.fromDate().

How to embed the date-to-timestamp conversion in a mongodb query?

Suppose I have documents in a mongo collections with a timestamp field. I want to query the documents based on the timestamp value (greater than, less than). In order to do that, instead of calculating each time the timestamp for my current date, I would like to embed the conversion from the date to timestamp in the query. I tried something like this (suppose "ts" is my timestamp field):
db.my_collection.find({"ts": {$lt: {$toLong : new Date("2020-12-09")}}})
instead of doing:
db.my_collection.find({"ts": {$lt: 1578524400}})
but it seems to return an empty result.
Is there a result to perform the date-to-timestamp conversion inside the query?
You can use $toLong but it's an aggregation pipeline operator, you need an aggregation pipeline not a plain find.

ISO8601 Date Strings in MongoDB (Indexing and Querying)

I've got a collection of documents in MongoDB that all have a "date" field, which is an ISO8601 formatted date string (i.e. created by the moment.js format() method).
I'd like to be able to efficiently run queries against this collection that expresses the sentiment "documents that have a date that is after A and before B" (i.e. documents within a range of dates).
Should I be storing my dates as something other than ISO-8601 strings? Contingent upon that answer, what would the MongoDB query operation look like for my aforementioned requirement? I'm envisioning something like:
{$and: [
{date: {$gt: "2017-05-02T03:15:22-04:00"}},
{date: {$lt: "2017-06-02T03:15:22-04:00"}},
]}
Does that "just work"? I need some convincing.
You definitely want to use the built-in date data type with an index on your date field and not strings. String comparison is going to be slow as hell compared to comparing dates which are just 64bit integers.
As far as querying is concerned, check out the answer here:
return query based on date

View last N documents using MongoDB Compass

I wish to view in MongoDB Compass the last N documents in a very large collection; too many to scroll through.
I could .skip(total - N) if I knew the syntax for that within Compass.
Alternatively, I have a date field and could use $gte with a date if I knew how to express a date in a manner acceptable to Compass.
Suggestion/example how to do this, please?
MongoDB Compass 1.6.1(Stable)
For date comparison you need to use $date operator with a string that represents a date in ISO-8601 date format.
{"date": {"$gte": {"$date": "2017-03-13T09:51:26.317Z"}}}
In my case the values of date field in Compass and mongo shell are different. So firstly I query the documents in the shell and then copy the "2017-03-13T09:51:26.317Z" from the result to the Compass filter line. In mongo shell it look like:
{
...
"date" : ISODate("2017-03-13T09:51:26.317Z"),
...
}
MongoDB Compass 1.7.0-beta.0 (Beta)
This version have an advanced query bar that lets you input not just the filter (as before), but also project, sort, skip and limit
(#Oleksandr I learned from your effective answer; thank you.)
I've also been shown that the Compass Schema tab allows one to drag a date range on the _id field to apply a filter query for that range. That range can be successively narrowed as desired.
Skip is descibed here
https://docs.mongodb.com/compass/current/query/skip/
In the Query Bar, click Options.
Enter an integer representing the number of documents to skip into the Skip field
Click Find to run the query and view the updated results.

How to query date saved as text in bad date format in mongoDB

I am very new to mongodb
I have a database with sale_date and the value is saved as text and the format is "dd:mm:yyyy". Now I want to query based on the date. Like I want to query the last month's entry.
I also have field sale_time and also saved as text and the format is "hh:mm" and I want to query the last hour's entry.
**I want to query from java and also from the mongo console.
One row of my collection:
{
"_id":112350,
"sale_date":"21.07.2011",
"sale_time":"18:50",
"store_id":"OK3889-45",
"region_code":45,
"product_id":"QKDGLHX5061",
"product_catagorie":53,
"no_of_product":1,
"price":1211.37,
"total_price":1211.37
}
I have million of entries. Now I want to find the entries for the month of July 2011 or hour from 18:00 to 19:00 in 21.07.2013.
You can query with a regex matching your results. You said format dd:mm:yyyy but the example looks like dd.mm.yyyy so I used that in examples
For example:
db.sales.find({sale_date: /..\.07\.2011/})
This will be ineficient since it can't use an index, but it will get the job done.
It would be better, if you stick with dates as strings to reverse the order to yyyy:mm:dd then you could use a anchored regex, which will hit an index like:
db.sales.find({sale_date: /2011\.07/})
For the hour query:
db.sales.find({sale_date: "21.07.2013", sale_time: {$gte: "18:00", $lt: "19:00"}})
There is no efficient and reliable way to query the for a date range you want given the date structure you have used. A regex style query would scan through the entire collection for example, and if you have millions of documents, that's not acceptable.
You could theoretically create a MapReduce to better structure the data into a new collection. But, that will be more work to maintain (as MapReduces aren't automatically updated, and may make other queries and data fetching involve more steps than you'd like).
Although, if you're willing to do that, I'd strongly suggest you instead just fix your data as I mentioned in my comment to be a standard YYYYMMDD. Even better, you may want to consider merging the time and would be to include the time stamp in the same field:
2013-07-21T14:30
If you don't though, you can still do the single date query reasonably (although you'd want to index both the date and time as a compound index):
db.sales.ensureIndex({ sale_date: 1, sale_time: 1})
Regarding the code, it's basically going to look like this:
BasicDBObject date = new BasicDBObject("sale_date", "21.07.2013");
BasicDBObject time = new BasicDBObject("sale_time",
new BasicDBObject("$gte", "18:00").
append("$lte", "19:00"));
BasicDBObject andQuery = new BasicDBObject();
List<BasicDBObject> obj = new ArrayList<BasicDBObject>();
obj.add(date);
obj.add(time);
andQuery.put("$and", obj);
cursor = coll.find(andQuery);