MongoDB聚合查询如何实现数组字段去重及skip分页
问题背景
当前使用的club集合单文档结构如下:
_id: ObjectId('62bbe17d8fececa06b91873d') clubName: 'test' staff:[ '62bbe47f8fececa06b9187d8', '624f4b56ab4f5170570cdba3' // 存储关联的员工成员ID ]
单个staff可被分配到多个club,需求是获取所有至少被分配到1个俱乐部的员工ID,支持前端表格分页展示。由于distinct语法无法在普通查询中搭配skip、limit使用,最初编写的聚合查询如下:
const query = this.clubModel.aggregate( [{ $group: { _id: '$staff' } }, { $skip: 0}, { $limit: 10}], (err, results) => { console.log(results); }, );
该查询未做有效去重,返回格式如下:
[ { _id: [ '624f5054ab4f5170570cdd16', '624f5054ab4f5170570cdd16' ] }, // 来自club1的staff { _id: [ '624f5054ab4f5170570cdd16', '624f9194ab4f5170570cded1' ] }, // 来自club2的staff { _id: [ '624f4b56ab4f5170570cdba3' ]} // 来自club3的staff ]
预期输出为去重后的员工ID集合,后续新增createdAt字段后调整的聚合查询存在分页失效问题,核心疑问为:MongoDB中是否存在可对数组内值做去重计算的聚合操作符,能够实现数组值去重后搭配skip、limit完成分页的需求?
解决方案
MongoDB原生提供数组成员去重能力,常用操作符包括$addToSet、$setUnion,但要实现正确分页,核心是要注意skip和limit是文档级别的操作符,不能在把所有去重ID合并到单个数组(单条文档)之后再做分页,否则分页逻辑完全不生效。
正确实现的聚合逻辑如下:
- 先用
$unwind拆分每个club文档下的staff数组,将每个员工ID拆分为独立的单层文档 - 按单个员工ID做
$group分组,天然实现跨club的员工ID去重 - 按需对去重后的员工ID做排序
- 此时每个文档对应一个唯一员工ID,直接调用
$skip、$limit即可实现正确的分页效果 - 可选:如果需要把当前页的ID组装为单个数组返回,可在分页完成后再做一次分组聚合
可直接运行的代码示例:
const pageSize = 10; const skip = 0; const query = this.clubModel.aggregate([ // 拆分staff数组为单条文档 { $unwind: '$staff' }, // 按单个staff ID分组去重 { $group: { _id: '$staff' } }, // 按需排序,示例为按员工ID升序排列 { $sort: { _id: 1 } }, // 对去重后的员工ID维度做分页 { $skip: skip }, { $limit: pageSize }, // 可选步骤:将当前页结果组装为单个数组返回 { $group: { _id: null, staffIds: { $push: '$_id' } } }, { $project: { _id: 0, staffIds: 1 } } ])
之前写法的问题说明
- 初始聚合查询缺少
$unwind步骤,直接将每个club的staff数组作为分组键,不同club的staff数组哪怕存在重复成员,也会被判定为不同分组值,无法实现跨club的ID去重。 - 后续调整的查询逻辑顺序错误:提前将所有去重ID通过
$addToSet合并到单条文档的数组中,此时全量结果只有1条文档,后续$skip只要传入大于0的值就会返回空结果,$limit也只会返回全量ID数组,完全无法实现分页效果。
如果需要计算分页总页数,可在去重分组步骤之后、$skip步骤之前增加$count操作,获取去重后的员工总数量。
内容的提问来源于stack exchange,提问作者Jam
相关产品推荐
相关产品推荐

