在Mongodb中只包含起始日期的数组中,如何获取时间段中与条件匹配的所有元素
我收集了以下物品:在Mongodb中只包含起始日期的数组中,如何获取时间段中与条件匹配的所有元素,mongodb,date,mongodb-query,aggregation-framework,event-sourcing,Mongodb,Date,Mongodb Query,Aggregation Framework,Event Sourcing,我收集了以下物品: { id:1, 状态历史记录:[ { 状态:“活动”, 日期:ISODate(“2020-04-26T22:02:26.000Z”) }, { 状态:“已禁用”, 日期:ISODate(“2020-05-20T22:02:26.000Z”) } ] } { id:2, 状态历史记录:[ { 状态:“活动”, 日期:ISODate(“2020-05-26T22:02:26.000Z”) } ] } { id:3, 状态历史记录:[ { 状态:“活动”, 日期:ISODate(“
{
id:1,
状态历史记录:[
{
状态:“活动”,
日期:ISODate(“2020-04-26T22:02:26.000Z”)
},
{
状态:“已禁用”,
日期:ISODate(“2020-05-20T22:02:26.000Z”)
}
]
}
{
id:2,
状态历史记录:[
{
状态:“活动”,
日期:ISODate(“2020-05-26T22:02:26.000Z”)
}
]
}
{
id:3,
状态历史记录:[
{
状态:“活动”,
日期:ISODate(“2020-04-26T22:02:26.000Z”)
},
{
状态:“已禁用”,
日期:ISODate(“2020-04-27T22:02:26.000Z”)
}
]
}
现在我需要找到所有在2020年5月处于活动状态的项目。数组statusHistory
仅包含状态更改的日期。我需要以某种方式将这些数组聚合到一个表单中,其中的项也包含到目前为止的内容。比如:
{
状态:“活动”,
日期:ISODate(“2020-04-26T22:02:26.000Z”),//起始日期
dateTo:ISODate(“2020-05-20T22:02:26.000Z”)//它是从数组中下一项的日期开始的
}
然后我想删除该期间之外的所有项目,因此我希望得到以下结果:
{
id:1,
状态历史记录:[
{
状态:“活动”,
日期:ISODate(“2020-04-26T22:02:26.000Z”)
}
]
}
{
id:2,
状态历史记录:[
{
状态:“活动”,
日期:ISODate(“2020-05-26T22:02:26.000Z”)
}
]
}
我想用
$reduce
,但没有找到解决办法。在我看来,这是事件源模式中的一个常见问题,但我无法找到解决方法。以下管道可能不是最好的,但值得尝试一下。将使用一些解释对其进行更新,但为了帮助理解管道或在获得意外结果时对其进行调试,只需在第一个管道步骤中运行聚合。例如,在mongo shell中以以下方式运行聚合:
db.collection.aggregate([
{ '$addFields': { .... } }
])
检查结果,查看新的statusHistory
数组是否使用新字段dateTo
正确构造。如果得到预期结果,则添加下一个:
db.collection.aggregate([
{ '$addFields': { .... } },
{ '$match': { ... } }
])
因此,总体而言,运行该操作
db.collection.aggregate([
{ '$addFields': {
'statusHistory': {
'$map': {
'input': '$statusHistory',
'in': {
'$mergeObjects': [
'$$this',
{ 'dateTo': {
'$arrayElemAt': [
'$statusHistory',
{ '$indexOfArray': [
'$statusHistory.status',
'DISABLED'
] }
]
} }
]
}
}
}
} },
{ '$match': {
'$expr': {
'$gt': [
{ '$size': {
'$filter': {
'input': '$statusHistory',
'cond': {
'$and': [
{ '$eq': ['$$this.status', 'ACTIVE'] },
{ '$gte': ['$$this.dateTo.date', new Date('2020-05-01')] }
]
}
}
} },
0
]
}
} },
{ '$addFields': {
'statusHistory': {
'$map': {
'input': {
'$filter': {
'input': '$statusHistory',
'as': 'item',
'cond': { '$eq': ['$$item.status', 'ACTIVE'] }
}
},
'in': {
'status': '$$this.status',
'date': '$$this.date'
}
}
}
} },
])
您可以使用
$filter
仅选择与条件匹配的数组元素。在这种情况下,您希望包含所选元素的附加信息,也可以使用$map
。这两个运算符都用于在聚合查询中处理数组。您可以使用$reduce
(正如您所说,您已经尝试过的),但是$filter
是合适的运算符。您可以提供如何操作吗?因为要进行筛选,我仍然需要数组中下一项的日期。我真的不知道如何使用$map
并从下一项中获取日期。当元素同时包含date from和date to时,很容易做到这一点,但我需要知道如何在元素中不包含to date。若要从数组中的下一个元素获取日期,请首先查找当前元素的索引。将1添加到该索引中,就可以将索引添加到下一个元素。另外,请参见arrayElemAt
和indexOfArray
。感谢它帮助我更新了使用的索引:{'$add':[{'$indexOfArray':['$statusHistory.date','$$this.date']},1]}
。因此,我首先按日期找到当前元素的索引,然后添加+1。@Saljack很棒的东西,很高兴这有帮助!