Javascript 查询数组中的最后匹配日期

Javascript 查询数组中的最后匹配日期,javascript,mongodb,Javascript,Mongodb,我需要在我的mongoDB中找到所有具有过期日期值的数据集。Expired表示最后一个数组元素时间戳比当前时间戳加上定义的间隔(由类别定义)旧 每个数据集都有这样一个字段 { "field" : [ { "category" : 1, "date" : ISODate("2019-03-01T12:00:00.464Z") }, { "category" : 1, "date" : ISODa

我需要在我的mongoDB中找到所有具有过期日期值的数据集。Expired表示最后一个数组元素时间戳比当前时间戳加上定义的间隔(由类别定义)旧

每个数据集都有这样一个
字段

{
  "field" : [
      {
        "category" : 1,
        "date" : ISODate("2019-03-01T12:00:00.464Z")
      },
      {
        "category" : 1,
        "date" : ISODate("2019-03-01T14:52:50.464Z")
      }
    ]
}
类别定义了一个时间间隔。例如,“类别1”代表90分钟,“类别2”代表120分钟

现在,我需要获取每个数据集的日期值已过期,这意味着最后一个数组元素的值比当前时间戳早90分钟

差不多

Content.find({ 'field.$.date': { $gt: new Date() } })
但通过这种尝试,我有两个问题:

  • 如何查询最后一个数组元素
  • 如何在查询中实现类别时间间隔

  • 让我们把这个问题分成几个部分

    查询“最后一个”(最近的)数组元素 第1部分:逻辑和快速

    快速阅读MongoDB应该会告诉您,事实上,您总是可以根据索引位置查询数组元素。这对于“第一个”数组元素非常简单,因为该位置始终为
    0

    { "field.0.date": { "$lt": new Date("2019-03-01T10:30:00.464Z") } }
    
    从逻辑上讲,“最后一个”位置应该是
    -1
    ,但您不能在MongoDB的这种形式的表示法中实际使用该值,因为它将被视为无效

    但是,您可以在这里改为向数组中添加新项,这样您就可以在数组的开头添加新项,而不是附加到数组的末尾。这意味着您的数组内容基本上是“反向”的,并且可以轻松访问,如上图所示。这是修改器为您所做的:

    collection.updateOne(
      { "_id": documentId },
      {
        "$push": {
          "field": { 
            "$each": [{ "category": 1, "date": new Date("2019-03-02") }],
            "$position": 0
          }
        }
      }
     )
    
    因此,这意味着新添加的项目要从头开始,而不是结束。这可能很实用,但它确实意味着您需要重新订购所有现有的数组项

    如果
    “date”
    是静态的,并且在写入数组项后基本上不会更改(即,您从不更新匹配数组项的日期),则可以使用修饰符在单个update语句中对该
    “date”
    属性重新排序:

    collection.updateMany(
      {},
      { "$push": { "field": { "$each": [], "$sort": { "date": -1 } } } }
    )
    
    当你没有向数组中添加任何东西时,使用它可能会觉得“奇怪”,但这就是修改器所在的位置。空数组
    “$each”:[]
    参数本质上意味着“不添加任何内容”,但该参数适用于数组的所有当前成员

    这可以选择像前面的示例一样完成,在该示例中,将在每次写入时应用。但是,只要
    “date”
    适用于“添加时的时间戳”(我怀疑是这样),那么使用
    “$position”:0
    方法可能会更有效,而不是在每次更改时进行排序。取决于您的实际实现以及您如何处理数据

    第2部分:蛮力与慢速

    但是,如果出于任何原因,您确实不认为能够“反转”数组的内容是一个实际的解决方案,那么唯一可用的方法是通过从支持的运算符投影此值来有效地“计算”“最后一个”数组元素

    唯一可行的方法通常是使用聚合框架,特别是运营商:

    collection.aggregate([
      { "$addFields": {
        "lastDate": { "$arrayElemAt": [ "$field.date", -1 ] }
      }}
    ])
    
    基本上,只需查看提供的数组内容(在本例中,仅查看每个元素的
    “date”
    属性值),然后在给定索引位置提取值。此运算符允许使用
    -1
    索引表示法,表示数组中的“最后一个”元素

    显然,这并不理想,因为提取与查询或过滤值所需的实际表达式是解耦的。这将在下一部分中介绍,但您需要意识到,在我们比较这些值以确定要保留哪些值之前,您只需在整个集合中迭代

    按“类别”列出的样本日期 第1部分:快速查询逻辑

    接下来,下一个标准基于
    “category”
    字段值,下一个主要问题是

    • 90分钟调整值1
    • 120分钟调整值2
    根据刚刚学到的相同逻辑,您应该得出结论,在处理数据时“计算”对性能来说是“坏消息”。因此,这里应用的技巧基本上是在查询表达式中包含逻辑,根据文档中匹配的
    “category”
    值,使用不同的
    “date”

    最简单的应用是使用表达式:

    var currentDateTime = new Date();
    
    var ninetyMinsBefore = new Date(currentDateTime.valueOf() - (1000 * 60 * 90));
    var oneTwentyMinsBefore = new Date(currentDateTime.valueOf() - (1000 * 60 * 120));
    
    
    collection.find({
      "$or": [
        {
          "field.0.category": 1,
          "field.0.date": { "$lt": ninetyMinsBefore }
        },
        {
          "field.0.category": 2,
          "field.0.date": { "$lt": oneTwentyMinsBefore }
        }
      ]
     })
    
    这里请注意,您不是计算存储的由变量间隔调整的
    “日期”
    ,而是计算与当前日期的差异,然后根据
    “类别”
    的值有条件地应用该差异

    这是一种快速有效的方法,因为您能够按照上述方式重新排序数组项,然后我们可以应用条件来查看“第一个”元素是否满足它们

    第2部分:较慢的强制计算

    collection.aggregate([
      { "$addFields": {
        "lastDate": { 
          "$arrayElemAt": [ "$field.date", -1 ]
        },
        "lastCategory": {
          "$arrayElemAt": [ "$field.category", -1 ]
        }
      }},
      { "$match": {
        "$or": [
          { "lastCategory": 1, "lastDate": { "$lt": ninetyMinsBefore } },
          { "lastCategory": 2, "lastDate": { "$lt": oneTwentyMinsBefore } }
        ]
      }}
    ])
    
    基本前提相同,即使您已经需要从“last”数组元素中投影值,也不需要用数学调整存储的
    “date”
    值,这只会使事情更加复杂

    最初的预测是主要的成本,所以这里的主要危害是底部的成本

    您可以选择与现代MongoDB版本一起使用,但基本上是一样的:

    collection.find({
      "$expr": {
        "$or": [
          {
            "$and": [
              { "$eq": [ { "$arrayElemAt": [ "$field.category", -1 ] }, 1 ] },
              { "$lt": [ { "$arrayElemAt": [ "$field.date", -1 ] }, ninetyMinsBefore ] }
            ]
          },
          {
            "$and": [
              { "$eq": [ { "$arrayElemAt": [ "$field.category", -1 ] }, 2 ] },
              { "$lt": [ { "$arrayElemAt": [ "$field.date", -1 ] }, oneTwentyMinsBefore ] }
            ]
          }
        ]
      }
    })
    
    值得注意的是和的特殊“聚合”形式,因为其中的所有内容都是聚合表达式,需要解析为
    true/false
    Boolean

    无论哪种方式,这都是同一个问题,因为最初的“仅查询”示例是本机处理的,并且确实可以使用索引