首页 > 解决方案 > 如何在Mongo中独立地按日期和时间过滤?

问题描述

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

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

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

标签: mongodbmongoosemongodb-queryaggregation-framework

解决方案


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

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

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查询运算符来评估聚合框架逻辑运算符日期运算符日期范围表达式的常规比较运算符

较低版本 - 聚合和 $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管道阶段应用。日期范围表达式 withing 的相同正则比较运算符$match

所有版本 - $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}`
})

将 JavaScript 评估与$where. 除非已明确禁用服务器端脚本,否则在所有版本中都可用。请注意,“相同”和常规比较运算符用于日期范围的主要选择。

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

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

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

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

因此,在可能的情况下,始终“使用索引”并确保您的查询表达式实际上是为使用它们而构建的,而不是其他不使用的形式。


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

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


推荐阅读