首页 > 解决方案 > 使用 MongoDB/Mongoose.js 按月分组标签

问题描述

我在 mongodb 中的集合如下所示:

邮政:

// ...
 tags: [
        {
            id: {
                type: mongoose.Schema.Types.ObjectId,
                ref: 'Tag',
                required: true,
            }
            // ... 
        }
 ],

 date: {
    type: Date,
 }
// ...

我想编写一个查询,结果如下:

[
    {
      "month": "Jan",
      "tag1": 5,
      "tag2": 80,
      // ...
     }, 
    {
      "month": "Feb",
      "tag1": 30,
      "tag2": 95,
      // ...
    },
    // ...
]

我想我需要使用聚合。这样对吗?

我写了这个,但结果不是我想要的。

  const monthStrings = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  Posst.aggregate([
    {
      $match: {
        $expr: {
          $and: [
            { $gt: ["$created_at", oneYearFromNow] },
            { $lt: ["$created_at", dateNow] }
          ],
        }
      }
    },
    {
      $group: {
        _id: {
          month: { $month: "$date" },
          year: { $year: "$date" },
        },
        count: {
          $sum: 1
        }
      }
    },
    {
      $project: {
        _id: {
          $concat: [
            {
              $arrayElemAt: [
                monthStrings,
                "$_id.month"
              ]
            },
            "-",
            "$_id.year"
          ]
        },
        count: 1,
      }
    }
  ])

我怎样才能得到我想要的结果?

(返回的格式并不重要,但我想要实现的是在单个查询中检索同一分组的多个计数(每月一个)。)

标签: node.jsmongodbmongooseaggregation-framework

解决方案


为了计算每个标签的文档,您需要按标签标识符进行分组。由于标签在数组中,最简单的方法是在分组之前先展开数组。最后,为了在单个文档中获取同一月份的所有标签,您需要执行第二次分组操作。例如(假设您要使用的标签名称在“ref”字段中):

Posst.aggregate([
  {
    $match: ... 
  },
  {
    // Unwind the tags array, produces one document for each element in the array
    $unwind: '$tags'
  },
  {
    // Group by month, year and tag reference
    $group: {
      _id: {
        month: { $month: '$date' },
        year: { $year: '$date' },
        tag: '$tag.ref'
      },
      count: { $sum: 1 }
    }
  },
  {
    // Group again by month and year to aggregate all tags in one document
    $group: {
      _id: {
        month: '$_id.month',
        year: '$_id.year'
      },
      // Collect tag counts into an array
      tagCounts: {
        $push: {
          k: '$_id.tag',
          v: '$count'
        }
      }
    }
  },
  {
    // Reshape document to have each tag in a separate field
    $replaceRoot: {
      newRoot: {
        $mergeObjects: [ 
          { month: '$_id.month', year: '$_id.year' },
          { $arrayToObject: '$tagCounts' }
        ]
      }
    }
  }
])

推荐阅读