Reputation: 15442
Given a set of documents that contains an array of time start and end times:
{
times: [
{ startAt: ... endAt: ...},
{ startAt: ... endAt: ...},
{ startAt: ... endAt: ...}
...
]
}
Is there a way to find all the documents which not only fall within a date range, but also a time of day.
For example, it is possible to query for all documents where at least one of the ranges ends on any day before the 1/1/16 where the end time for that day is >= 17:00?
Upvotes: 0
Views: 673
Reputation: 2043
Best i can do is get the id of all documents where at least one of the ranges ends on any day before the 1/1/16 where the end time for that day is >= 17:00. You will have to query for the actual document again using the id to get full data
Sample data
db.times.insert({ time: [
{ startAt: new Date("2015-01-01 10:00"), endAt: new Date("2015-01-01 18:00") },
{ startAt: new Date("2015-01-02 10:00"), endAt: new Date("2015-01-02 18:00") } ]
});
db.times.insert({ time: [
{ startAt: new Date("2015-01-01 10:00"), endAt: new Date("2016-01-01 16:00") },
{ startAt: new Date("2015-01-02 10:00"), endAt: new Date("2015-01-02 16:00") } ]
});
db.times.insert({ time: [
{ startAt: new Date("2015-01-01 10:00"), endAt: new Date("2016-01-01 16:00") },
{ startAt: new Date("2015-01-02 10:00"), endAt: new Date("2016-01-02 16:00") } ]
});
To query
db.times.aggregate([
{ $unwind: "$time" },
{ $project: { endHour: { $hour: "$time.endAt" }, time: { startAt: "$time.startAt", endAt: "$time.endAt" } } },
{ $match: { $and: [ { "time.endAt": { $lt: new Date("2016-01-01") } }, { endHour: { $gte: 9 }} ] } },
{ $group: { _id: "$_id", time: { $push: { startAt: "$time.startAt", endAt: "$time.endAt" } } } }
]);
Output
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d2") }
Because the data we are looking for is in the array times, for project to work we will need to unwind
it to be able to examine the endAt.
After first unwind
stage the output is (also input for next stage)
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d2"), "time" : { "startAt" : ISODate("2015-01-01T03:00:00Z"), "endAt" : ISODate("2015-01-01T11:00:00Z") } }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d2"), "time" : { "startAt" : ISODate("2015-01-02T03:00:00Z"), "endAt" : ISODate("2015-01-02T09:00:00Z") } }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d3"), "time" : { "startAt" : ISODate("2015-01-01T03:00:00Z"), "endAt" : ISODate("2016-01-01T09:00:00Z") } }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d3"), "time" : { "startAt" : ISODate("2015-01-02T03:00:00Z"), "endAt" : ISODate("2015-01-02T09:00:00Z") } }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d4"), "time" : { "startAt" : ISODate("2015-01-01T03:00:00Z"), "endAt" : ISODate("2016-01-01T09:00:00Z") } }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d4"), "time" : { "startAt" : ISODate("2015-01-02T03:00:00Z"), "endAt" : ISODate("2016-01-02T09:00:00Z") } }
Then go to next stage the $project
, we extract the hours from endAt and keep time object otherwise we will lost list
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d2"), "time" : { "startAt" : ISODate("2015-01-01T03:00:00Z"), "endAt" : ISODate("2015-01-01T11:00:00Z") }, "endHour" : 11 }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d2"), "time" : { "startAt" : ISODate("2015-01-02T03:00:00Z"), "endAt" : ISODate("2015-01-02T09:00:00Z") }, "endHour" : 9 }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d3"), "time" : { "startAt" : ISODate("2015-01-01T03:00:00Z"), "endAt" : ISODate("2016-01-01T09:00:00Z") }, "endHour" : 9 }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d3"), "time" : { "startAt" : ISODate("2015-01-02T03:00:00Z"), "endAt" : ISODate("2015-01-02T09:00:00Z") }, "endHour" : 9 }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d4"), "time" : { "startAt" : ISODate("2015-01-01T03:00:00Z"), "endAt" : ISODate("2016-01-01T09:00:00Z") }, "endHour" : 9 }
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d4"), "time" : { "startAt" : ISODate("2015-01-02T03:00:00Z"), "endAt" : ISODate("2016-01-02T09:00:00Z") }, "endHour" : 9 }
Note that mongodb use ISODate, at my local time is GMT+7 so, the hourEnd is not 18 but 18-7=11 After this stage we already have all information we need, we can now filter based on endAt and endHour
{ $match: { $and: [ { "time.endAt": { $lt: new Date("2016-01-01") } }, { endHour: { $gte: 10 }} ] } }
In this $match
, we want only document that endAt before 2016-01-01 and endHour greater than 10 (aka 17:00)
The last stage is only to group by id to remove redundant document
Hope that helps
Upvotes: 2