如何在 MongoDB 中对聚合查询结果进行分页并获得总文档数(Node.js + Mongoose)?
Posted
技术标签:
【中文标题】如何在 MongoDB 中对聚合查询结果进行分页并获得总文档数(Node.js + Mongoose)?【英文标题】:How to paginate aggregation query results in MongoDB and get a total document count (Node.js + Mongoose)? 【发布时间】:2021-01-23 05:41:49 【问题描述】:我想通过 $limit 和 $skip 对查询结果进行分页,并获取通过查询的文档总数。
我现在对结果进行分页是这样的:
const startIndex = (page - 1) * limit
const endIndex = page * limit
const resultObject =
resultObject.totalCount = await model.countDocuments(query).exec()
if (endIndex < results.totalCount)
resultObject.next = page: page + 1, limit
if (startIndex > 0)
results.prev = page: page - 1, limit
resultObject.results = await query.limit(limit).skip(startIndex).exec()
这会产生两个问题:
我执行了两次查询(您甚至可以在 1 中执行此操作吗?) 由于某种原因,当我的查询是一个聚合时,countDocuments
返回 0 即使我执行 query.exec()
我得到正确的结果 (> 0)
所以我的问题是如何在获取文档计数的同时,像现在只使用 1 个查询(并使其与聚合查询一起使用)对结果进行分页,以便我可以构建我的 resultObject
【问题讨论】:
【参考方案1】:“我执行了两次查询(你甚至可以在 1 中执行此操作吗?)”不,你不能,实现这一点的唯一方法是获取整个集合,然后在代码中返回所需的文档,这显然是一个漂亮的不好的解决方案。
现在这有点棘手,从技术上讲,countDocuments 是一个只执行管道的包装器:
该方法使用 $sum 表达式包装 $group 聚合阶段以执行计数
意义
db.collection.countDocuments();
等于:
db.collection.aggregate([
$group:
_id: null,
count: $sum: 1
]);
但是不同的驱动程序支持此命令的方式不同,例如 nodejs
Mongo 驱动程序不允许使用指定的某些表达式 here。您还使用了mongoose
,这可能会有额外的变化。
如果不查看您的确切查询,很难说出它失败的原因,但是如果您找不到我建议的原因,只需执行包装的管道即可。
【讨论】:
【参考方案2】:我找到了一种方法来获取查询的结果 AND 计数都在数据库的同一“行程”中(仍然是 2 个查询,但在数据库中,所以它很多 更快)
关键是使用$facet
(mongodb 3.4+):
const pipeline = [
YOUR_QUERY,
$facet:
paginatedResults: [$skip: startIndex, $limit: limit],
totalCount: [$count: 'count']
,
$addFields:
total:
$ifNull: [ $arrayElemAt: ['$totalCount.count', 0], 0]
,
$project:
paginatedResults: 1,
total: 1
]
说明:$facet
在同一个输入(我们的查询结果)上执行多个管道,我们可以有 1 个管道用于对结果进行分页,1 个用于计算总数。
由于$count
返回一个包含count
字段的对象,并且facet 包装在一个数组中,$addFields
管道将实际的count
值提取为total
字段。然后我只使用$project
删除不再需要的totalCount
,这就是结果:
paginatedResults: [RESULTS],
total: 34
【讨论】:
以上是关于如何在 MongoDB 中对聚合查询结果进行分页并获得总文档数(Node.js + Mongoose)?的主要内容,如果未能解决你的问题,请参考以下文章
是否有任何有效的方法可以以最新的一阶对给定的 mongodb 数据进行分页并避免排序超出大数据的内存限制?