【问题标题】:How to group MongoDB records by a certain date range?如何按特定日期范围对 MongoDB 记录进行分组?
【发布时间】:2021-01-03 14:56:35
【问题描述】:

我对 MongoDB 有点陌生,但在查询它时遇到了问题。

假设我有以下数据集,

    [
        {
            _id: '1',
            date: "2020-12-31T22:02:11.257Z",
        },
        {
            _id: '2',
            date: "2020-12-31T22:05:11.257Z",
        },
        {
            _id: '3',
            date: "2021-01-01T22:02:11.257Z",
        },
        {
            _id: '4',
            date: "2021-01-02T12:02:11.257Z",
        },
        {
            _id: '5',
            date: "2021-01-02T22:02:11.257Z",
        }
    ]

我正在尝试按天对所有记录进行分组。从我的前端,我发送了一个多月,然后我根据它运行查询。因此,如果用户选择一月,我将运行以下查询:

router.get('/', async (req, res) => {
    const {selectedMonth, selectedYear} = req.query; // january would be '1' here

    const data = await db.collection.find({"date": {
        "$gt": new Date(selectedYear, parseInt(selectedMonth) - 1, 1),
        "$lte": new Date(selectedYear, parseInt(selectedMonth), 1)
    }}).sort({ date: -1 })

在这里,我正在获取所选范围内的所有记录。因此,如果用户选择 2021 年 1 月,我将获取大于 2020 年 12 月 31 日且小于或等于 2021 年 1 月 31 日的所有记录。

这里的问题是我想统计每天的所有记录。我能够获取指定日期范围内的所有记录,但我正在寻找类似下面的内容,以便返回:

    [
        "2021-01-01": [
            { _id: '3', date: "2021-01-01T22:02:11.257Z" },
        ],
        "2021-01-02": [
            { _id: '4', date: "2021-01-02T12:02:11.257Z" },
            { _id: '5', date: "2021-01-02T22:02:11.257Z" },
        ]
    ]

我正在考虑遍历返回的数据并构建我自己的响应对象,但我想知道是否有更好的方法来做到这一点?这是我目前正在做的事情,

    const result = []
    let count = 0;
    data.forEach((record, index) => {
        // first record will always set the base
        if (index === 0) {
            result.push({
                date: record.date.toLocaleDateString(),
                count: 1
            })
        } else {
            // If the record is the same date, then increase counter
            if (record.date.toLocaleDateString() === result[count].date) {
                result[count].count = result[count].count + 1
            } else {
                // push a new record and increase count
                result.push({
                    date: record.date.toLocaleDateString(),
                    count: 1
                })
                count = count + 1
            }
        }
    });

产量,

result [
   { date: '1/2/2021', count: 2 },
   { date: '1/1/2021', count: 1 }
]

【问题讨论】:

    标签: javascript mongodb mongoose


    【解决方案1】:

    为此,您需要聚合管道:

    db.collection.aggregate([
      // First Stage: filter out dates
      {
        $match: {
          date: { $gte: new ISODate("2020-01-01"), $lt: new ISODate("2020-12-31") },
        },
      },
      // Second Stage: group by day of the year
      {
        $group: {
          _id: { $dateToString: { format: "%d-%m-%Y", date: "$date" } },
          count: { $sum: 1 },
        },
      },
      // Third Stage, reshape the output documents
      {
        $project: {
          _id: 0,
          date: "$_id",
          count: 1
        },
      },
    ]);
    

    【讨论】:

      【解决方案2】:

      您可以使用聚合框架来完成您需要的操作,该框架具有许多您可以使用的运算符 对于不同的管道。第一个管道步骤是过滤,您使用$match 管道阶段与 $expr 查询运算符和 $month$year 日期运算符:

      const pipeline = [
          // First pipeline step
          { '$match': {
              '$expr': {
                  '$and': [
                      { '$eq': [ { '$month': '$date' }, parseInt(selectedMonth) ] },
                      { '$eq': [ { '$year': '$date' }, parseInt(selectedYear) ] }
                  ]
              }
          } }
      ];
      

      下一步是将按天过滤后返回的所有文档与$dateToString 分组到$group 中,如下所示:

      const pipeline = [
          // First pipeline step
          { '$match': {
              '$expr': {
                  '$and': [
                      { '$eq': [ { '$month': '$date' }, parseInt(selectedMonth) ] },
                      { '$eq': [ { '$year': '$date' }, parseInt(selectedYear) ] }
                  ]
              }
          } },
      
          // Second pipeline step
          { '$group': {
              '_id': { '$dateToString': { 'format': '%Y-%m-%d', 'date': '$date' } },
              'data': { '$push': '$$ROOT' },
              'count': { '$sum': 1 }
          } }
      ];
      

      接下来的步骤是将文档重塑为所需的投影,您可以在其中利用$arrayToObject 运算符和$replaceRoot 管道来获得所需的结果。

      const pipeline = [
          // First pipeline step
          { '$match': {
              '$expr': {
                  '$and': [
                      { '$eq': [ { '$month': '$date' }, parseInt(selectedMonth) ] },
                      { '$eq': [ { '$year': '$date' }, parseInt(selectedYear) ] }
                  ]
              }
          } },
      
          // Second pipeline step
          { '$group': {
              '_id': { '$dateToString': { 'format': '%Y-%m-%d', 'date': '$date' } },
              'data': { '$push': '$$ROOT' },
              'count': { '$sum': 1 }
          } },
      
          // Third pipeline step
          {  '$group': {
              '_id': null,
              'counts': {
                  '$push': {
                      'k': '$_id',
                      'v': {
                          'data': '$data',
                          'count': '$count'
                      }
                  }
              }
          } },
      
          // Fourth pipeline step
          {  '$replaceRoot': {
              'newRoot': { '$arrayToObject': '$counts' }
          } }
      ];
      

      然后可以按如下方式组合和运行:

      router.get('/', async (req, res) => {
          const { selectedMonth, selectedYear } = req.query; // january would be '1' here
          const pipeline = [...]; // pipeline above
          const data = await db.collection.aggregate(pipeline).toArray();
          console.log(data);
      }
      

      表格的最终结果:

      [
          {
              "2021-01-01": [
                  { _id: '3', date: "2021-01-01T22:02:11.257Z" },
              ],
              "2021-01-02": [
                  { _id: '4', date: "2021-01-02T12:02:11.257Z" },
                  { _id: '5', date: "2021-01-02T22:02:11.257Z" },
              ]
          }
      ]
      

      将您的第三个管道步骤更新为:

      // Third pipeline step
      {  '$group': {
          '_id': null,
          'counts': {
              '$push': {
                  'k': '$_id',
                  'v': '$data'
              }
          }
      } },
      

      对于表格的最终结果:

      [
          {
              "2021-01-01": 1,
              "2021-01-02": 2
          }
      ]
      

      您的第三个管道步骤应该是:

      // Third pipeline step
      {  '$group': {
          '_id': null,
          'counts': {
              '$push': {
                  'k': '$_id',
                  'v': '$count'
              }
          }
      } },
      

      【讨论】:

      • 这是一个复杂的聚合管道。应该比较简单。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-03
      • 2022-01-19
      • 2013-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多