Node.js Mongoose如何按固定间隔查询MongoDB文档子集?
嘿,这个需求完全可以实现,我给你分享两种靠谱的方案,分别适合不同的MongoDB版本和性能需求:
方案一:用聚合管道高效筛选(推荐MongoDB 5.0+)
如果你的MongoDB版本在5.0及以上,$setWindowFields是最优解——它能给每个文档生成连续的行号,然后我们只需要筛选符合你要求的行号即可,性能比循环skip好太多。
步骤很清晰:
- 先给文档排序(必须!不然「第N个文档」的顺序是不确定的,这里用
_id排序,对应默认的插入顺序); - 用
$setWindowFields给每个文档添加行号; - 筛选行号为1,或者行号是300的倍数的文档(对应你要的第1、300、600...个文档);
- 可选:移除行号字段,只保留原文档内容。
代码示例:
const targetDocs = await YourModel.aggregate([ // 按插入顺序排序(如果需要其他顺序,改这里的sort条件即可) { $sort: { _id: 1 } }, // 生成从1开始的连续行号 { $setWindowFields: { partitionBy: null, // 整个集合作为一个分区 sortBy: { _id: 1 }, output: { rowNumber: { $rowNumber: {} } } } }, // 筛选符合条件的行号:第1个,或者300的倍数 { $match: { $or: [ { rowNumber: 1 }, { $expr: { $eq: [{ $mod: ["$rowNumber", 300] }, 0] } } ] } }, // 移除行号字段,返回原文档结构 { $unset: "rowNumber" } ]);
方案二:循环Skip+Limit(兼容低版本,但性能一般)
如果你的MongoDB版本低于5.0,只能用传统的skip+limit循环来实现。不过要注意:当skip的数值很大时,MongoDB需要扫描前面所有的文档,性能会明显下降,所以这个方案适合数据量不是特别大的场景,或者你能接受稍慢的查询速度。
代码示例:
const batchStep = 300; const results = []; // 先取第一个文档 const firstDoc = await YourModel.findOne().sort({ _id: 1 }); if (firstDoc) { results.push(firstDoc); } // 从第300个开始,每隔300个取一个(注意skip是从0计数的,第300个的索引是299) let currentSkip = batchStep - 1; while (true) { const doc = await YourModel.findOne() .sort({ _id: 1 }) .skip(currentSkip) .limit(1); if (!doc) break; // 没有更多文档时退出循环 results.push(doc); currentSkip += batchStep; }
重要提醒
- 一定要指定排序条件:如果不排序,MongoDB返回的文档顺序是不稳定的,你取到的「第N个文档」可能每次都不一样。优先用有索引的字段排序(比如
_id默认有索引),能大幅提升查询速度。 - 聚合方案的性能远优于循环Skip:对于10万+文档的场景,强烈推荐用方案一,循环Skip在后期会有明显的性能瓶颈。
内容的提问来源于stack exchange,提问作者Hữu Linh
相关产品推荐
相关产品推荐

