给定一组包含时间开始和结束时间的文档:
{
times: [
{ startAt: ... endAt: ...},
{ startAt: ... endAt: ...},
{ startAt: ... endAt: ...}
...
]
}
有没有办法找到不仅属于日期范围,而且还有一天中某个时间的所有文件。
例如,可以在1/1/16之前查询至少有一个范围在任何一天结束的所有文档,其中该日的结束时间是> = 17:00?
答案 0 :(得分:3)
我能做的最好的是获取所有文档的ID,其中至少有一个范围在2016年1月1日之前的任何一天结束,其中当天的结束时间是> = 17:00。您将不得不使用id来再次查询实际文档以获取完整数据
示例数据
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") } ]
});
查询
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" } } } }
]);
输出
{ "_id" : ObjectId("56659a492b9eaad2d9e6d4d2") }
因为我们要查找的数据是在数组中,所以要使项目工作,我们需要unwind
才能检查endAt。
在第一个unwind
阶段之后,输出是(也为下一阶段输入)
{ "_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") } }
然后转到$project
的下一阶段,我们从endAt中提取小时并保留时间对象否则我们将丢失列表
{ "_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 }
注意mongodb使用ISODate,在我当地时间是GMT + 7所以,小时结束不是18但是18-7 = 11 在此阶段之后,我们已经拥有了所需的所有信息,现在我们可以根据endAt和endHour进行过滤
{ $match: { $and: [ { "time.endAt": { $lt: new Date("2016-01-01") } }, { endHour: { $gte: 10 }} ] } }
在这个$match
中,我们只想要2016-01-01之前的endAt和endHour大于10(也就是17:00)的文档
最后一个阶段只是按ID分组以删除多余的文档
希望有所帮助