MongoDB查询额外的转换字段



我不知道如何清楚地表达我的问题,但让我试着举个例子来解释我想做什么。

Btw,我是MongoDB的新手,来自SQL。

我需要查询一个集合,并检查expiryDate在今天、1个月前或3个月前的特定日期字段。

我可以创建一个过滤器,通过使用{ 'expiryDate':'2023-03-30' }来检查那些今天过期的记录,这个过滤器返回那些在2023-03-30过期的记录。

但是,我不知道如何获得那些在1个月前(2023-02-28)和3个月前(2022-12-30)过期的记录,因为存储在DB中的实际expiryDate2023-03-30)。

如果我使用MySQL,我可以为expire1Monthexpire3Months创建DATE_ADD()附加字段。然后我可以过滤这些新字段,如果它落在今天(或我指定的日期,如2023-03-30)。

我知道这是可能的MongoDB,但我不确定如何在查询代码中表达这一点。

顺便说一下,expiryDate存储在这种格式(如字符串)2023-03-30,但我仍然可以使用gte, lte, gt, lt, etc。如果没有弄错的话,我的MongoDB版本是4.4。

我尝试使用$dateAdd(aggregate),但它不工作,因为这需要5.0版本以上。

我还尝试了带日期的$add (aggregate),但将天数/月份计算为毫秒可能会出现每月天数(28-31天)的问题。

是否有一个添加功能,我可以在几个月前直接添加,而不需要计算毫秒,并将与4.4版本一起工作?

经过一番研究,我能够创建一个满足我需要的查询,但我知道这不是最好或最有效的查询,但这对我来说很有效。

db.getCollection('torjak_requests').aggregate(
[
{
$match: {
/* Add your filters here */
}
},
// Add temporary field to convert String date to ISO Date
{
$addFields: {
"expiryDate" : {
$dateFromString: {
dateString: "$expiryDateS"
}
}
}
},
// Add temporary fields for 1 month and 3 months before expiry date in ISO Date format
{
$addFields: {
"expiryDate1MonthBefore" : {
$dateFromParts: {
year: { $year: "$expiryDate" },
month: { $add: [{ $month: "$expiryDate" }, -1] },
day: { $dayOfMonth: "$expiryDate" },
hour: { $hour: "$expiryDate" },
minute: { $minute: "$expiryDate" },
second: { $second: "$expiryDate" },
millisecond: { $millisecond: "$expiryDate" }
}
},
"expiryDate3MonthsBefore" : {
$dateFromParts: {
year: { $year: "$expiryDate" },
month: { $add: [{ $month: "$expiryDate" }, -3] },
day: { $dayOfMonth: "$expiryDate" },
hour: { $hour: "$expiryDate" },
minute: { $minute: "$expiryDate" },
second: { $second: "$expiryDate" },
millisecond: { $millisecond: "$expiryDate" }
}  
}         
}
},
// Add fields for 1 month and 3 months before expiry in String format
{
$addFields: {
"expiryDate1MonthBeforeS" : {
$dateToString: { format: "%Y-%m-%d", date: "$expiryDate1MonthBefore" }
},
"expiryDate3MonthsBeforeS" : {
$dateToString: { format: "%Y-%m-%d", date: "$expiryDate3MonthsBefore" }
},
}
},
// Remove temporary added fields since I don't need these fields anymore
{
$projection: {
"expiryDate" : 0,
"expiryDate1MonthBefore" : 0,
"expiryDate3MonthsBefore" : 0
}
}

]
)

最简单、可能也是性能最好的方法是在客户端计算日期。这将要求每个查询执行一次计算,而不是每个文档执行一次。

查询就变成了字符串的简单范围比较。

使用您喜欢的日期库定义比较日期字符串:

DateNow = YourChosenDateLibrary.todayAsString()
Date1MAgo = YourChoseDateLibrary.dateSubtract(now, "1 month")
Date3MAgo = YourChoseDateLibrary.dateSubtract(now, "3 month")

那么今天的查询过期:

db.getCollection('torjak_requests').find({expiryDate: DateNow})

1个月前(但不是今天):

db.getCollection('torjak_requests').find({expiryDate:{$gte: Date1MAgo, $lt: DateNow})

1个月以上至3个月以前:

db.getCollection('torjak_requests').find({expiryDate:{$gte: Date3MAgo, $lt: Date1MAgo})