You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Express.js中使用Mongoose Aggregate统计子数组数量(解决空数组问题)

Mongoose聚合统计子数组hitshistory数量的示例代码

嘿,我来帮你解决这个Mongoose聚合统计子数组数量的问题!首先得先明确你的数据结构,假设你的文档大概是这样的(如果结构不一样,你可以调整代码里的字段名):

{
  _id: ObjectId("60d21b4667d0d8992e610c85"),
  title: "前端性能优化技巧",
  hitshistory: [
    { userId: ObjectId("60d21b4667d0d8992e610c86"), hitTime: ISODate("2024-05-20T14:30:00Z") },
    { userId: ObjectId("60d21b4667d0d8992e610c87"), hitTime: ISODate("2024-05-21T09:15:00Z") }
  ]
}

第一步:确认你的Model定义(参考)

假设你的Model是Article,结构大概如下,确保hitshistory字段是数组类型:

// models/Article.js
const mongoose = require('mongoose');

// 子数组的Schema
const hitHistoryItemSchema = new mongoose.Schema({
  userId: { 
    type: mongoose.Schema.Types.ObjectId, 
    ref: 'User', 
    required: true 
  },
  hitTime: { 
    type: Date, 
    default: Date.now 
  }
});

// 主文档Schema
const articleSchema = new mongoose.Schema({
  title: { type: String, required: true },
  hitshistory: [hitHistoryItemSchema] // 这里就是你要统计的子数组
});

module.exports = mongoose.model('Article', articleSchema);

第二步:Controller中的聚合实现

根据你的需求,分两种场景给出示例:

场景1:统计每个文档的hitshistory数量(每个文档返回对应的hits值)

这种场景适合你要查看每篇文章的访问记录数,用$size操作符直接获取子数组长度,简单高效:

// controllers/articleController.js
const Article = require('../models/Article');
const mongoose = require('mongoose');

async function getEachArticleHitCount(req, res) {
  try {
    // 如果你需要过滤特定文档,比如按ID查询,就加上$match阶段
    const matchCondition = req.params.id ? 
      { $match: { _id: new mongoose.Types.ObjectId(req.params.id) } } : 
      {};

    const hitCounts = await Article.aggregate([
      matchCondition, // 可选:过滤特定文档
      {
        $project: {
          _id: 1,
          title: 1,
          hits: { $size: "$hitshistory" } // 核心:用$size统计子数组长度,命名为hits
        }
      }
    ]);

    if (hitCounts.length === 0) {
      return res.status(404).json({ message: "未找到匹配的文档" });
    }

    res.status(200).json(hitCounts);
  } catch (err) {
    console.error('统计访问数失败:', err);
    res.status(500).json({ message: '服务器内部错误', error: err.message });
  }
}

module.exports = { getEachArticleHitCount };

场景2:统计所有文档的hitshistory总数量(总访问次数)

如果要统计所有文章的访问记录总和,需要用$unwind展开子数组,再用$group求和:

async function getTotalHitCount(req, res) {
  try {
    const totalResult = await Article.aggregate([
      { $unwind: "$hitshistory" }, // 展开每个hitshistory元素
      {
        $group: {
          _id: null, // 按null分组表示统计所有数据
          totalHits: { $sum: 1 } // 每展开一个元素加1
        }
      },
      {
        $project: {
          _id: 0,
          hits: "$totalHits" // 把结果命名为hits
        }
      }
    ]);

    // 处理没有任何访问记录的情况
    const totalHits = totalResult.length > 0 ? totalResult[0].hits : 0;
    res.status(200).json({ hits: totalHits });
  } catch (err) {
    console.error('统计总访问数失败:', err);
    res.status(500).json({ message: '服务器内部错误', error: err.message });
  }
}

为啥你之前返回空数组?可能的原因:

  1. 字段名拼写错误:MongoDB区分大小写,确认你的子数组字段是hitshistory(不是hitHistory或其他拼写);
  2. $match条件太严格:如果加了过滤条件,可能没有匹配到任何文档,先去掉$match测试;
  3. 数据库无数据:检查你的集合里是否真的有包含hitshistory的文档;
  4. 空数组不影响结果:即使hitshistory是空数组,$size会返回0,不会导致结果为空数组,除非没有匹配的文档。

内容的提问来源于stack exchange,提问作者Jones Stephen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:54:19