【问题标题】:How to fetch the last n months record(consider only the last entry per month) in MongoDB如何在 MongoDB 中获取最近 n 个月的记录(仅考虑每月的最后一个条目)
【发布时间】:2021-11-20 23:42:04
【问题描述】:

我是 MongoDb 的新手。我需要帮助来获取最近 n 个月的记录,每个月可能有多个条目,但查询只需要返回每个月的最后一个条目。

例如,假设 n 是 3 并且 userId 是 userId1(这意味着返回 userId1 的最近 3 个月记录)。

集合中的示例输入:

[
 {
   "_id": objectId("aaaaaa"),
   "userId": "userId1",
   "processedAt": "2021-06-01T12:16:49.349Z"
 },
 {
   "_id": objectId("bbbbb"),
   "userId": "userId1",
   "processedAt": "2021-10-11T12:16:49.349Z"
 },
 {
   "_id": objectId("ccccc"),
   "userId": "userId1",
   "processedAt": "2021-10-25T12:16:49.349Z"
 },
 {
   "_id": objectId("eeeee"),
   "userId": "userId1",
   "processedAt": "2021-09-12T12:16:49.349Z"
 },
 {
   "_id": objectId("fffff"),
   "userId": "userId1",
   "processedAt": "2021-09-28T12:16:49.349Z"
 },
 {
   "_id": objectId("ggggg"),
   "userId": "userId1",
   "processedAt": "2021-09-23T12:16:49.349Z"
 },
 {
   "_id": objectId("hhhhh"),
   "userId": "userId1",
   "processedAt": "2021-07-23T12:16:49.349Z"
 },
 {
   "_id": objectId("iiiii"),
   "userId": "userId2",
   "processedAt": "2021-09-29T12:16:49.349Z"
 },
 {
   "_id": objectId("jjjjj"),
   "userId": "userId1",
   "processedAt": "2022-01-29T12:16:49.349Z"
 },
 {
   "_id": objectId("kkkkk"),
   "userId": "userId1",
   "processedAt": "2022-02-29T12:16:49.349Z"
 }, 
]

预期结果:应按userId返回,限制n个月(仅获取该月最后保存的条目)和processedAt月份的升序:

[{
    "_id": objectId("ccccc"),
    "userId": "userId1",
    "processedAt": "2021-10-25T12:16:49.349Z"
},
{
    "_id": objectId("jjjjj"),
    "userId": "userId1",
    "processedAt": "2022-01-29T12:16:49.349Z"
  },
  {
    "_id": objectId("kkkkk"),
    "userId": "userId1",
    "processedAt": "2022-02-29T12:16:49.349Z"
  }
]

我尝试了以下查询,但是它返回了所有记录。我希望查询只需要考虑每月的最后一个条目。我一直在用mongojs驱动v4.1.2

db.collection(collection_name)
                .find({ userId: userId }, { projection: { _id: 0 } })
                .sort({ processedAt: -1 })
                .limit(n)
                .toArray()

【问题讨论】:

    标签: node.js mongodb mongodb-query aggregation-framework mongojs


    【解决方案1】:

    从 MongoDB 5.0 开始

    您可以使用$setWindowFields 为“分区”/“组”(即您的示例中的月份)聚合一个“排名”,并且只选择排名最高的文档。

    排名可以定义为processedAt: -1,因为您只想保留排名最高的月份的最新记录。

    {
        "$setWindowFields": {
          "partitionBy": {
            "$dateToString": {
              "date": "$processedAt",
              "format": "%Y-%m"
            }
          },
          "sortBy": {
            "processedAt": -1
          },
          "output": {
            "rank": {
              $rank: {}
            }
          }
        }
      }
    

    这里是Mongo playground 供您参考。

    对于 MongoDB 3.6+,

    由于示例数据集使用 ISODate 格式,因此可以按最左边的 7 个字符(即 yyyy-MM)对字段进行排序和分组。只保留月份组内的第一个文档应该可以解决问题。

    {
        $sort: {
          processedAt: -1
        }
      },
      {
        "$addFields": {
          "month": {
            "$substrCP": [
              "$processedAt",
              0,
              7
            ]
          }
        }
      },
      {
        $group: {
          _id: "$month",
          last: {
            $first: "$$ROOT"
          }
        }
      }
    

    这里是Mongo playground

    【讨论】:

    • 我有 mongo 3.x。并使用 mongojs 驱动程序 v4.1.2
    • 您使用的 MongoDB 的确切版本是什么?
    • 我正在使用 MongoDb v3.6
    • @sham 我已经为旧版本更新了另一个解决方案。它应该与您正在使用的 MongoDB 3.6 兼容。
    • @sham 只需再添加 1 个 $project 即可删除帮助字段。更新了游乐场链接以反映这一点
    猜你喜欢
    • 1970-01-01
    • 2011-12-17
    • 2017-02-19
    • 1970-01-01
    • 2022-10-19
    • 1970-01-01
    • 1970-01-01
    • 2022-10-04
    • 1970-01-01
    相关资源
    最近更新 更多