在 MongoDB 中存储和查询时间间隔

Storing and querying time intervals in MongoDB

建模轮班计划应用程序:

我想出了这样的数据结构来描述shift

{
    "fromHour" : 7,
    "fromMinute" : 30,
    "toHour" : 9,
    "toMinute" : 30,
    "week" : 5,
    "date" : "2015-01-26",
    "user" : {
       // ...
    },
    "_id" : ObjectId("54d0e4a82b9dc26c0c0f36e7")
}

我需要存储的主要是信息: 1.轮班开始时(小时和分钟), 2. 当轮班结束时(小时和分钟)和 3. 实际发生的日期。

字段 fromHourfromMinutetoHourtoMinutedate 作为 ISO 字符串对我来说非常适合存储和查询班次按特定日期。

当我需要从中构建报告时出现了问题。说,我想获得从“2015-01-01”到“2015-02-01”的所有班次,范围从 07:00 到 23:00.

我可以在查询中添加 $and 子句,例如

[ { fromHour: { '$gte': 7 } },
  { fromMinute: { '$gte': 0 } },
  { toHour: { '$lt': 23 } },
  { toMinute: { '$lt': 0 } } ]

但这不是很好,因为那里的 shift toMinute 是 30,$lt 将是错误的。

我正在尝试寻找高效的数据结构,以允许存储易于查询的时间跨度。

在两个不同的字段中分开存储小时和分钟太容易出错,并且会使您的工作更加困难。由于 Mongo 没有明显的 "Time" 数据类型,只有日期,而班次通常在 "easy" 次开始和结束,我建议实施类似将时间转换为实数的操作在你的应用程序中是这样的:

00:00 --> 0
01:00 --> 1
...
08:00 --> 8
08:15 --> 8.25
08:30 --> 8.5
...
16:30 --> 16.5
...

这在应用程序中有点额外的工作,因为您必须在保存或显示时进行转换,但它仍然比在两个不同字段中使用一个时间值要好。

因此您的数据将如下所示:

{
    "shiftStart" : 7.5,
    "shiftEnd" : 9.5,
    "week" : 5,
    "date" : "2015-01-26",
    "user" : {
       // ...
    },
    "_id" : ObjectId("54d0e4a82b9dc26c0c0f36e7")
}

您的查询:

[ { shiftStart: { '$gte': 7 } },
  { shiftEnd: { '$lt': 23 } } ]

您可以将日期类型的数据存储为ISODate(...)格式,然后使用$project and Date Aggregation Operators查询数据。

以你的例子为例:

db.shifts.aggregate([
  { $match:  //matches the dates first to filter out before $project step
    { datetimeStart:
      { $gte: ISODate("2015-01-01T07:00:00.000Z"),
        $lt: ISODate("2015-02-01T00:00:00.000Z")
      },
      datetimeEnd:
      { $gte: ISODate("2015-01-01T07:00:00.000Z"),
        $lt: ISODate("2015-02-01T00:00:00.000Z")
      }
    }
  },
  { $project:  // $project step extracts the hours
    { otherNeededFields: 1,  // any other fields you want to see
      datetimeStart: 1,
      datetimeEnd: 1,
      hourStart: { $hour: "$datetimeStart" },
      hourEnd: { $hour: "$datetimeEnd" }
    }
  },
  { $match: // match the shift hours
    { hourStart: { $gte: 7 },
      hourEnd: { $lte: 23 }
    }
  }
])

有了这个系统,可能,但是 复杂 找到更像是在 7:30AM 和 10:30PM:

db.shifts.aggregate([
  { $match:  //matches the dates first to filter out before $project step
    { datetimeStart:
      { $gte: ISODate("2015-01-01T07:30:00.000Z"),
        $lt: ISODate("2015-02-01T00:00:00.000Z")
      },
      datetimeEnd:
      { $gte: ISODate("2015-01-01T07:30:00.000Z"),
        $lt: ISODate("2015-02-01T00:00:00.000Z")
      }
    }
  },
  { $project:  // $project step extracts the hours
    { otherNeededFields: 1,  // any other fields you want to see
      datetimeStart: 1,
      datetimeEnd: 1,
      hourStart: { $hour: "$datetimeStart" },
      minStart: { $minute: "$datetimeStart" },
      hourEnd: { $hour: "$datetimeEnd" },
      minEnd: { $minute: "$date
    }
  },
  { $match: // match the shift hours
    { $or:
      [
        {hourStart: 7, minStart: {$gte: 30}}, // hour is 7, minute >= 30
        {hourStart: { $gte: 8 }}  // hour is >= 8
      ],
      $or:
      [
        {hourEnd: 22, minEnd: {$lte: 30}}, // hour is 22, minute <= 30
        {hourEnd: { $lte: 21 }} // hour is <= 21
      ]
    }
  }
])