【问题标题】:How to filter by both date and time independently in Mongo?如何在Mongo中独立地按日期和时间过滤?
【发布时间】:2018-10-11 14:33:41
【问题描述】:

如何在 Mongo 中同时按日期和时间独立过滤?

我希望能够在数据库中独立保存日期和日期,我可以在其中独立地按日期和时间进行过滤。

我应该只有一个字段来保存日期对象吗?如果是,我怎么能按时间过滤。我如何获得有关记录的统计信息,例如日期从 01/05/2017 到 15/03/2018并且记录的时间应该在下午 3 点到下午 5 点之间(所以记录日期为 2018 年 3 月 12 日和下午 5:05 的时间不应包括在内)。

【问题讨论】:

  • 您能显示示例数据和预期输出吗?
  • 答案真的取决于你需要什么性能。如果您需要能够使用索引获取此查询的结果,那么您应该添加时间字段,但如果扫描与日期查询部分匹配的文档很好,您可以只使用日期字段。
  • 我认为一个日期字段就足够了,但我如何才能真正使用 mongo 进行此过滤?
  • 我认为这回答了我的问题stackoverflow.com/questions/39868872/…

标签: mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

有如下文件:

db.col.save({ date: new Date("2018-03-02T16:00:00Z") }); // matches
db.col.save({ date: new Date("2018-03-16T16:00:00Z") }); // doesn't match
db.col.save({ date: new Date("2017-05-02T19:00:00Z") }); // doesn't match
db.col.save({ date: new Date("2017-06-15T15:00:00Z") }); // matches

您可以使用$redact 将条件指定为表达式。要从Date 中提取一个小时,您可以使用$hour 运算符:

db.col.aggregate([
    {
        $redact: {
            $cond: {
                if: { $and: [
                        { $gte: [ "$date", new Date("2017-05-01T00:00:00Z") ] },
                        { $lt: [ "$date", new Date("2018-03-16T00:00:00Z") ] },
                        { $gte: [ { $hour: "$date" }, 15 ] },
                        { $lt: [ { $hour: "$date" }, 17 ] }
                    ] 
                },
                then: "$$KEEP",
                else: "$$PRUNE"            
            }
        } 
    }
])

【讨论】:

  • $lt ...17 在小时部分,否则会上升到将近下午 6 点:17:59(假设可以排除下午 5 点本身)
  • @IanMercer 谢谢,修改了我的答案。现在它不包括下午 5 点,要包括它,$minutes$or 应该有更复杂的条件。我想这是最简单的解决方案
【解决方案2】:

这里要考虑的重要一点是,您实际上需要在此处继续使用"regular query operators",否则您的性能将大大降低。这个想法是始终编写一个实际上可以“使用索引”的查询,然后确保您有索引。

因此“天”的选择是一个标准的范围查询,剩下的只是考虑到通过正确指定标准“已经选择”的文档来过滤具有“计算”的表达式查询条件first

MongoDB 3.6 - $expr

db.collection.find({
  "date": { "$gte": new Date("2017-05-01"), "$lt": new Date("2018-03-16") },
  "$expr": {
    "$and": [
      { "$gte": [{ "$hour": "$date" }, 15 ] },
      { "$lt": [{ "$hour": "$date", 17 ] }
    ]
  }
})

使用$expr 查询运算符来评估聚合框架logical operatorsdate operatorsRegular comparison operators 用于日期范围表达式。

低版本 - 聚合和 $redact

db.collection.aggregate([
  { "$match": { 
    "date": { "$gte": new Date("2017-05-01"), "$lt": new Date("2018-03-16") }
  }},
  { "$redact": {
    "$cond": {
      "if": {
        "$and": [
          { "$gte": [{ "$hour": "$date" }, 15 ] },
          { "$lt": [{ "$hour": "$date", 17 ] }
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

相同的聚合表达式,但使用 $redact 管道阶段应用。与$match 相同的日期范围表达式Regular comparison operators

所有版本 - $where

var startHour = 15,
    endHour = 17;

db.collection.find({
  "date": { "$gte": new Date("2017-05-01"), "$lte": new Date("2018-03-15") },
  "$where": `this.data.getUTCHours() => ${startHour}
    && this.data.getUTCHours() < ${endHour}`
})

通过$where 使用 JavaScript 评估。除非已明确禁用服务器端脚本,否则在所有版本中都可用。请注意,“相同”和Regular comparison operators 用于日期范围的主要选择。

ALL 情况下,必须表达“标准查询运算符”条件“first”。如果没有这个,MongoDB 将无法使用数据中存在的索引,并且需要扫描集合中的每个文档以计算条件并查看是否返回文档。

$gte$lt 范围内添加“标准运算符”条件确保可以使用索引,而剩余的“计算”逻辑表达式仅实际应用于那些已经满足“第一”的文档条件。

对于“奖励”,您甚至可以将时间限制放在“天”本身上,因此您甚至不用分别考虑开始和结束日期的下午 3 点之前或下午 5 点之后的时间:

"date": { "$gte": new Date("2017-05-01T15:00"), "$lt": new Date("2018-03-16T17:00") }

因此,尽可能始终使用"use an index",并确保您的查询表达式实际构造为使用它们,而不是其他形式。


注意这里的一般逻辑是这里所有提出的解决方案的“性能”应该按呈现顺序扩展,$expr 最好到$where 最差.然而,在目前的写作中,MongoDB 3.6 版本似乎出现了回归,实际上$where 通常比实际使用本机运算符的对应项性能更好。

然而,这不应该是“持续”的情况,应该得到解决,因此通常建议您使用原生运算符组合,而不是 $where 的 JavaScript 逻辑。

【讨论】:

  • 这意味着日期将被保存为数据库中的普通javascript日期对象而不是ISOstring?
  • @YoussefSherif MongoDB 存储 BSON 日期。它们不是字符串,你不应该有字符串。我们只是对每种单独的语言使用“本机”数据操作,然后驱动程序“转换”它们。对于 Date() 的 JavaScript。请参阅Find objects between two dates MongoDB 多年来一直存在的一般解释。但是您想要那个基本查询,并且只计算“小时”以“单独”保留
猜你喜欢
  • 2022-07-16
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 2013-10-20
  • 1970-01-01
  • 2020-08-03
  • 1970-01-01
  • 2021-08-30
相关资源
最近更新 更多