Javascript 查询数组中的最后匹配日期
我需要在我的mongoDB中找到所有具有过期日期值的数据集。Expired表示最后一个数组元素时间戳比当前时间戳加上定义的间隔(由类别定义)旧 每个数据集都有这样一个Javascript 查询数组中的最后匹配日期,javascript,mongodb,Javascript,Mongodb,我需要在我的mongoDB中找到所有具有过期日期值的数据集。Expired表示最后一个数组元素时间戳比当前时间戳加上定义的间隔(由类别定义)旧 每个数据集都有这样一个字段 { "field" : [ { "category" : 1, "date" : ISODate("2019-03-01T12:00:00.464Z") }, { "category" : 1, "date" : ISODa
字段
{
"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
值
无论哪种方式,这都是同一个问题,因为最初的“仅查询”示例是本机处理的,并且确实可以使用索引