MongoDB时间范围搜索

时间:2015-12-07 11:29:18

标签: mongodb

给定一组包含时间开始和结束时间的文档:

{
   times: [
      { startAt: ... endAt: ...},
      { startAt: ... endAt: ...},
      { startAt: ... endAt: ...}
      ...
   ]
}

有没有办法找到不仅属于日期范围,而且还有一天中某个时间的所有文件。

例如,可以在1/1/16之前查询至少有一个范围在任何一天结束的所有文档,其中该日的结束时间是> = 17:00?

1 个答案:

答案 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分组以删除多余的文档

希望有所帮助