如何获取仅包含 Mongodb 中日期的数组中时间段内匹配条件的所有元素

How get all elements which match condition in time period in array which contains only from date in Mongodb

我有一个 collection,其中包含以下项目:

{
  id: 1,
  statusHistory: [
    {
      status: "ACTIVE",
      date: ISODate("2020-04-26T22:02:26.000Z")
    },
    {
      status: "DISABLED",
      date: ISODate("2020-05-20T22:02:26.000Z")
    }
  ]
}

{
  id: 2,
  statusHistory: [
    {
      status: "ACTIVE",
      date: ISODate("2020-05-26T22:02:26.000Z")
    }
  ]
}

{
  id: 3,
  statusHistory: [
    {
      status: "ACTIVE",
      date: ISODate("2020-04-26T22:02:26.000Z")
    },
    {
      status: "DISABLED",
      date: ISODate("2020-04-27T22:02:26.000Z")
    }
  ]
}

现在我需要找到 2020 年 5 月状态为 ACTIVE 的所有项目。数组 statusHistory 仅包含状态更改的日期。我需要以某种方式将这些数组聚合成一种形式,其中的项目也包含最新信息。类似于:

{ 
  status: "ACTIVE",
  date: ISODate("2020-04-26T22:02:26.000Z"), // from
  dateTo: ISODate("2020-05-20T22:02:26.000Z") // it is from the date of the next item in the array
}

然后我想删除该期间内的所有项目,所以我想要这个结果:

{
  id: 1,
  statusHistory: [
    {
      status: "ACTIVE",
      date: ISODate("2020-04-26T22:02:26.000Z")
    }
  ]
}

{
  id: 2,
  statusHistory: [
    {
      status: "ACTIVE",
      date: 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'
                }
            }
        }
    } },
])