我不知道如何清楚地表达我的问题,但让我试着举个例子来解释我想做什么。
Btw,我是MongoDB的新手,来自SQL。
我需要查询一个集合,并检查expiryDate
在今天、1个月前或3个月前的特定日期字段。
我可以创建一个过滤器,通过使用{ 'expiryDate':'2023-03-30' }
来检查那些今天过期的记录,这个过滤器返回那些在2023-03-30
过期的记录。
但是,我不知道如何获得那些在1个月前(2023-02-28
)和3个月前(2022-12-30
)过期的记录,因为存储在DB中的实际expiryDate
是2023-03-30
)。
如果我使用MySQL,我可以为expire1Month
和expire3Months
创建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})