单个MongoDB聚合查询同时获取分页记录与总记录数
实现方案
通过MongoDB聚合的$facet阶段即可实现单次请求同时返回分页数据和总记录数,不需要拆分两次查询。$facet支持在同一聚合管道中运行多个独立的子管道分支,共享前置阶段的筛选结果,相比两次查询没有额外性能损耗。
调整后的聚合管道逻辑
公共前置阶段和原有逻辑完全一致,仅在筛选完成后通过$facet分叉处理计数和分页逻辑,步骤如下:
- 阶段1:
$unwind展开sections数组 - 阶段2:
$match筛选category = "ABC"且sections.section_name = "SECTION A"的记录 - 阶段3:
$facet定义两个并行子管道:totalCount分支:直接通过$count统计匹配结果的总条数documents分支:依次执行字段投影、$skip、$limit,获取当前页的结构化数据
- 阶段4:
$project整理返回结构,将totalCount从子管道返回的数组中提取为数值类型,空结果时默认返回0
具体聚合代码示例
// 分页参数示例:第1页,每页10条,可根据实际请求动态传值 const page = 1; const pageSize = 10; const skip = (page - 1) * pageSize; db.getSiblingDB("departments").DepartmentDetails.aggregate([ // 1. 展开sections数组,与原有逻辑一致 { $unwind: "$sections" }, // 2. 条件筛选,与原有逻辑一致 { $match: { category: "ABC", "sections.section_name": "SECTION A" } }, // 3. 分叉并行处理计数和分页逻辑 { $facet: { // 子管道1:统计符合条件的总记录数 totalCount: [ { $count: "count" } ], // 子管道2:查询当前页的结构化数据 documents: [ // 字段映射,规则与原有投影逻辑一致 { $project: { _id: 0, name: "$sections.section_name", hod: "$sections.section_hod", staff_count: "$sections.section_staff_count", id: "$sections.section_id" } }, { $skip: skip }, { $limit: pageSize } ] } }, // 4. 整理为目标返回结构 { $project: { _id: 0, // 提取总数值,无匹配结果时默认返回0 totalCount: { $ifNull: [ { $arrayElemAt: ["$totalCount.count", 0] }, 0 ] }, documents: 1 } } ])
返回结果示例
聚合执行后返回长度为1的数组,取第一个元素即为需要的结构:
[ { "totalCount": 35, "documents": [ { "name": "SECTION A", "hod": "张三", "staff_count": 12, "id": "SEC001" } // 其余当前页数据 ] } ]
优化提示:不要将
$unwind、$match这类公共筛选逻辑重复写在两个$facet子管道中,放在$facet之前的公共阶段只会执行一次,能有效减少重复计算开销。
内容的提问来源于stack exchange,提问作者Alex Man
相关产品推荐
相关产品推荐

