如何用MongoDB聚合查询筛选索引可被5整除的记录?
需求:MongoDB聚合查询筛选指定索引的记录
场景说明
我的usages集合数据结构如下(实际有40000条记录,以下为示例):
{ _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675122960, usage: null, limit: 2.7 }, { _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675123020, usage: 2.74, limit: 2.7 }, { _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675123080, usage: 2.62, limit: 2.7 }, { _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675123140, usage: 4.32, limit: 2.7 }, { _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675123200, usage: null, limit: 2.7 }, { _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675123260, usage: 5.38, limit: 2.7 }, { _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675123320, usage: 5.08, limit: 2.7 }, { _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675123380, usage: 1.02, limit: 2.7 }, { _id: AUTOGENERATED_MONGO_OBJECT_ID, timeStamp: 1675123440, usage: 2.35, limit: 2.7 },
原本我用Mongoose查询获取最后60条数据:
const count = await Usage.count() Usage.find({}).sort({ timeStamp: 1 }).skip(count - 60);
现在需要从这60条数据里筛选出索引可被5整除的12条记录(用于前端折线图展示)。当前我是先查所有60条再在客户端过滤:
const count = await Usage.count(); const data = await Usage .find({}) .sort({ timeStamp: 1 }) .skip(count - start) .exec(); const requiredData = data.filter((item, index) => index % 5 === 0); console.log(requiredData);
但客户端过滤速度太慢,希望用MongoDB聚合查询在数据库层面完成筛选。
解决方案
可以通过聚合管道的$sort、$limit、$setWindowFields等操作,在数据库层面完成排序、截取目标数据、添加索引、筛选的全流程,具体实现如下:
Mongoose聚合代码
const requiredData = await Usage.aggregate([ // 1. 倒序排序,直接取最新的60条数据 { $sort: { timeStamp: -1 } }, { $limit: 60 }, // 2. 转回升序,和原查询的顺序保持一致 { $sort: { timeStamp: 1 } }, // 3. 为每条记录添加从1开始的序号 { $setWindowFields: { sortBy: { timeStamp: 1 }, output: { index: { $documentNumber: {} } } } }, // 4. 将序号转为从0开始,和客户端过滤的索引逻辑对齐 { $addFields: { index: { $subtract: ["$index", 1] } } }, // 5. 筛选索引能被5整除的记录 { $match: { index: { $mod: [5, 0] } } }, // 6. 移除临时添加的index字段,返回原数据结构 { $project: { index: 0 } } ]); console.log(requiredData);
代码说明
$sort+$limit:先倒序取最新60条,再转回升序,避免了先查总条数再跳过的两次查询,性能更优且不会出现数据不一致的问题。$setWindowFields+$documentNumber:为每条记录生成自增序号,作为筛选的索引依据。$addFields:把从1开始的序号转为0开始,和客户端过滤的索引逻辑完全匹配。$match:用$mod操作符筛选索引能被5整除的记录,直接在数据库完成过滤。$project:移除临时添加的index字段,返回和原查询结构一致的数据。
性能优化提示
- 确保
timeStamp字段创建了升序索引:await Usage.createIndex({ timeStamp: 1 }),这样排序操作会走索引,避免全表扫描,大幅提升查询速度。
内容的提问来源于stack exchange,提问作者Vishal
相关产品推荐
相关产品推荐

