如何在 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 数据进行分页并避免排序超出大数据的内存限制?

在 laravel 中对数据数组进行分页并在 vuejs 中使用无限滚动的最佳方法

如何使用MongoDB聚合进行分页?

如何使用聚合在猫鼬中对文档数组进行分页?

EF 利用PagedList进行分页并结合查询 方法2

如何根据 ComosDb 中的聚合函数结果对查询结果进行排序?