How to find all date-ranges overlapping another date-range in MongoDB? - mongodb

everyone!
I have a record of this type:
{
start: 2015-03-27T15:00:00.000Z,
end: 2015-03-27T17:00:00.000Z
}
and trying to find in the database crossing periods.
{
start: 2015-03-27T15:30:00.000Z,
end: 2015-03-27T16:00:00.000Z
}
I do scheduling system. And I was not there for some time occupied.

I believe you are trying to find documents with overlapping date ranges. In other words any document whose start or end dates falls in between given date range.
You can definitely achieve this with little bit match and logic.
Let's assume I have two documents in my collection
{
"_id" : ObjectId("56f692730c96eddb0a2c287e"),
"start" : "2015-03-27T15:00:00.000Z",
"end" : "2015-03-27T17:00:00.000Z"
}
{
"_id" : ObjectId("56f6928c0c96eddb0a2c287f"),
"start" : "2015-03-27T16:00:00.000Z",
"end" : "2015-03-27T27:00:00.000Z"
}
When I execute following piece of code
var startDate = "2015-03-27T20:00:00.000Z";
var endDate = "2015-03-27T21:00:00.000Z";
var findOverlapingDates = function(startDate, endDate){
return db.collection.find({
$or: [
{$and: [
{start:{$gte: startDate}}, {start:{$lte: endDate}}
]},
{start:{$lte: startDate}, end:{$gte: startDate}}
]
});
};
printjson(findOverlapingDates(startDate, endDate).toArray());
I get
[
{
"_id" : ObjectId("56f6928c0c96eddb0a2c287f"),
"start" : "2015-03-27T16:00:00.000Z",
"end" : "2015-03-27T27:00:00.000Z"
}
]
Which is overlapping document for given date range. Hope it all makes sense. For best performance, I'll recommend having index on both start and end fields.

Related

Querying date range in aggregate query returns nothing or ignores dates

In my aggregate query I'm trying to add conditions in the $match statement to return only records within given date range. Without converting to ISOString, I get a set of records that ignores the date range completely. When I convert to ISOString, I get nothing (returns empty set). I've tried using the $and operator, still nothing.
I've tried all the solutions on stack to no avail. Here's my code:
$match: {
$and: [
{'author.id': { $ne: req.user._id }},
{'blurtDate': { $gte: test1.toISOString() }},
{'blurtDate': { $lte: test2.toISOString() }}
]
}
test1 and test2 are correct, I checked them on console log they reflect as follows:
2019-06-02T12:44:39.000Z -- 2019-07-02T12:44:39.928Z
I also tried without the $and operator like so:
$match: {
'author.id': { $ne: req.user._id },
'blurtDate': { $gte: test1.toISOString() },
'blurtDate': { $lte: test2.toISOString() }
}
Which again returns nothing. Any help much appreciated!
EDIT: Wanted to emphasize that test1 and test2 are new date objects:
test1 = new Date(qryDateFrom); //Tried .toISOString() as well
test2 = new Date(qryDateTo);
Without .toISOString(), I get a return of values that ignores the dates. With .toISOString I get an empty return.
Here's an example document that should be returned:
{
"_id" : ObjectId("5d0a807345c85d00ac4b7217"),
"text" : "<p>Seriously RV style.</p>",
"blurtDate" : ISODate("2019-06-19T18:35:31.156Z"),
"blurtImg" : "04643410-92c1-11e9-80b6-a3262311afff.png",
"vote" : 0,
"author" : {
"id" : ObjectId("5cb5df0ef7a3570bb4ac6e05"),
"name" : "Benjamin Paine"
},
"__v" : 0
}
When I remove .toISOString(), I get documents outside of the expected date range, such as this one in May (query should only return between june 2 and july 2).
{
"_id" : ObjectId("5d07ebaf9a035117e4546349"),
"text" : "<p>A start to something more...</p>",
"blurtDate" : ISODate("2019-05-15T19:36:15.737Z"),
"blurtImg" : "2be7a160-9137-11e9-933f-6966b2e503c7.png",
"vote" : 0,
"author" : {
"id" : ObjectId("5cb5df0ef7a3570bb4ac6e05"),
"name" : "Benjamin Paine"
},
"__v" : 0
}
Your docs contain actual Date objects, so remove the .toISOString()s from your query. But you'll also need to combine your $gte and $lte terms into a single object:
$match: {
'author.id': { $ne: req.user._id },
'blurtDate': { $gte: test1, $lte: test2 }
}

Find closest date in one query

I'm currently trying to figure out a way to find the closest date of a entry in mongoDB to the on i'm looking for.
Currently i solved the problem by using 2 queries. One using $gte and limit(1) to look for the next larger date and then $lte - limit(1) to see if there is a closer on that might be lower.
I was wondering, if there might be a way to find the closest date in just one query, but was not able to find anything on that matter.
Hope you can help me with this, or at least tell me for sure that this is the only way to do so.
db.collection.find({"time":{$gte: isoDate}}).sort({"time":1}).limit(1)
db.collection.find({"time":{$lte: isoDate}}).sort({"time":-1}).limit(1)
But I am looking for a way to do this in one query so i dont have to subtract the results to find the closest one.
I solved a similar problem using an aggregation.
Sample data:
{
"_id" : ObjectId("5e365a1655c3f0bea76632a0"),
"time" : ISODate("2020-02-01T00:00:00Z"),
"description" : "record 1"
}
{
"_id" : ObjectId("5e365a1655c3f0bea76632a1"),
"time" : ISODate("2020-02-01T00:05:00Z"),
"description" : "record 2"
}
{
"_id" : ObjectId("5e365a1655c3f0bea76632a2"),
"time" : ISODate("2020-02-01T00:10:00Z"),
"description" : "record 3"
}
{
"_id" : ObjectId("5e365a1655c3f0bea76632a3"),
"time" : ISODate("2020-02-01T00:15:00Z"),
"description" : "record 4"
}
{
"_id" : ObjectId("5e365a1655c3f0bea76632a4"),
"time" : ISODate("2020-02-01T00:20:00Z"),
"description" : "record 5"
}
{
"_id" : ObjectId("5e365a1655c3f0bea76632a5"),
"time" : ISODate("2020-02-01T00:25:00Z"),
"description" : "record 6"
}
And I'm looking for the record nearest to ISODate('2020-02-01T00:18:00.000Z').
db.test_collection.aggregate([
{
$match:
{
time:
{
$gte: ISODate('2020-02-01T00:13:00.000Z'),
$lte: ISODate('2020-02-01T00:23:00.000Z')
}
}
},
{
$project:
{
time: 1,
description: 1,
time_dist: {$abs: [{$subtract: ["$time", ISODate('2020-02-01T00:18:00.000Z')]}]}}
},
{
$sort: {time_dist: 1}
},
{
$limit: 1
}])
The $match stage sets up a "time window". I used 5 minutes for this example.
The $project stage adds a time distance field. This is the time in milliseconds each record is from the query time of ISODate('2020-02-01T00:18:00.000Z').
Then I sorted on the time_dist field and limit the results to 1 to return the record with time closest to ISODate('2020-02-01T00:18:00.000Z').
The result of the aggregation:
{
"_id" : ObjectId("5e365a1655c3f0bea76632a4"),
"time" : ISODate("2020-02-01T00:20:00Z"),
"description" : "record 5",
"time_dist" : NumberLong(120000)
}
check this one
db.collection.find({"time":{$gte: isoDate,$lt: isoDate}}).sort({"time":1}).limit(1)
Please use the same format what mongodb support like following
ISODate("2015-10-26T00:00:00.000Z")
In Pymongo, I used the following function. The idea is to take a datetime object, subtract some days from it and add some days to it, then find a date between those two dates. If there are no such records, increase the date span:
import datetime, dateutil
def date_query(table, date, variance=1):
'''Run a date query using closest available date'''
try:
date_a = date - dateutil.relativedelta.relativedelta(days=variance)
date_b = date + dateutil.relativedelta.relativedelta(days=variance)
result = db[table].find({'date': {'$gte': date_a, '$lt': date_b}}).sort([('date', 1)])
result = list(result)
assert len(result) >= 1
return result[len(result)//2] # return the result closest to the center
except:
return date_query(table, date, variance=variance*2)
accourding to https://stackoverflow.com/a/33351918/4885936
don't need ISODate
simple easy solution is:
if you want 1 hour left to due date just simply :
const tasks = await task.find({
time: {
$gt: Date.now(),
$lt: Date.now() + 3600000 // one hour to miliseconds
}
})
this code get tasks from now to upcoming one hour later.

Mongodb how to find how much days ago from a timestamp field

I was trying to find the number of days ago using the timestamp but i dont know how to do that ?
{
"_id" : ObjectId("5504cc9ddd5af617caae30b3"),
"session_id" : 1,
"Timestamp" : "2014-04-07T10:51:09.277Z",
"Item_ID" : 214536502,
"Category" : 0
}
How I can calculate the number of days ago using the field "Timestamp" ?
You may use aggregate, $project with new Date() on the Timestamp field, then do the calculation, something like this:
pipe = {
"$project" : {
"_id" : 1,
"daySince" : {
"$divide" : [
{
"$subtract" : [
new Date(),
new Date("$Timestamp")
]
},
86400000
]
}
}
}
To calculate:
db.collection.aggregate(pipeline=pipe)
Since Timestamp isn't a ISODate object, you just need to convert it to one, then subtract to current date, and divide the result by 60*60*24*1000, then it will be the number of days since today.
You can also change the new Date() to what you need to be compared.
Updated:
Since I believe the Timestamp format might be malformed, alternatively you may use mapReduce functions to calculate this:
// in your mongo shell using the db
var mapTimestamp = function() {
daySince = parseInt(new Date() - new Date(this.Timestamp) / 86400000);
emit(this._id, daySince);
}
// since you map reduce only on one field, there's really no need for this
var reduceTimestamp = function (key, value) { return value; }
db.collection.mapReduce(mapTimestamp, reduceTimestamp, {out: "sample"})
To show the results:
db.sample.find()

MongoDB timestamp field sampling and aggregation

I'm kind of new to MongoDB, so bear with me.
Consider a collection which is built from documents in the form of the following:
{
"_id" : ObjectId("538d87a36da0bab7ff1a827d"),
"resource_id", "some_id",
"server_ts" : 1401784227674.05214213,
"location" : [
34.8383953,
32.1098175
],
"__v" : 0
}
Documents are being added per resource in a relatively fast rate, so I end up with a high resolution of timestamped locations (approx. half a second resolution) based on server_ts.
I'd like to be able to query the collection based on a resource id, but return documents in a a lower resolution (e.g. 5 seconds resolution, rather than the original 0.5).
In another words I'd like to divide the time to ranges of 5 seconds, and for each range, fetch one document which falls in that range (if it actually exists).
Is there a convenient way in mongodb either in the Aggregation framework or in the standard query interface to 'sample' data based on this criteria?
Obviously this can be done in server side code (Node.js in my case), but I still wonder if there's a better alternative.
Thanks!
If you store timestamp as an integer you can use modulo operator.
db.coll.find( { ts: { $mod: [ 5, 0 ] } } )
This will return all documents where value of the ts is e.g. 1401784227670, 1401784227675, 1401784227680...
Of course, this only works if you have only one document in the same second.
To filter out "duplicates" you can use aggregation like this:
db.x.aggregate([
{ $match : { ts : { $mod : [ 5, 0] } } },
{ $sort : { ts : 1 } }, /* without it $first is unpredictable */
{ $group : { _id : "$ts", location : { $first : "$location" } /* etc. */ } }
]);

Only retrieve back select sub properties and limit how many items they contain

I have a very simple document:
{
"_id" : ObjectId("5347ff73e4b0e4fcbbb7886b"),
"userName" : "ztolley",
"firstName" : "Zac",
"lastName" : "Tolley"
"data" : {
"temperature" : [
{
"celsius" : 22,
"timestamp" : 1212140000
}
]
}
}
I want to find a way to write a query that searches for userName = 'ztolley' and only returns back the last 10 temperature readings. I've been able to say just return the data field but I couldn't find a way to say just return data.temperature (there are many different data properties).
When I tried
db.user.find({userName:'ztolley'},{data: {temperature: {$slice: -10}}})
I got unsupported projection.
I'd try using the Aggregation framework http://docs.mongodb.org/manual/aggregation/ .
Using your schema this should work:
db.user.aggregate([{$match:{"userName":"ztolley"}},{$unwind:"$data.temperature"},{$sort:{"data.temperature.timestamp":-1}},{$limit:10}, {$project:{"data.temperature":1, _id:0}}])
It returns the temperature readings for that user, in reverse sorted order by timestamp, limited to 10.
It looks you write wrong projection, try it with dot '.' notation:
db.user.find( { userName:'ztolley' },{ 'data.temperature': { $slice: -10 } } );