使用限制时,使用MongoDB获取总文档数

ran*_*its 33 pagination mongodb casbah mongodb-query aggregation-framework

我有兴趣优化我正在使用MongoDB的"分页"解决方案.我的问题很直接.我通常会限制使用该limit()功能返回的文档数量.这迫使我发出一个没有该limit()函数的冗余查询,以便我也可以捕获查询中的文档总数,这样我就可以将其传递给客户端,让他们知道他们必须发出额外的请求.检索其余文件.

有没有办法将其压缩为1个查询?获取文档总数但同时仅使用limit()?检索子集?是否有不同的方式来思考这个问题,而不是接近它?

Ash*_*shh 23

Mongodb 3.4引入了$facet聚合

它在同一组输入文档的单个阶段中处理多个聚合管道。

使用$facet$group可以找到的文档,$limit并可以获得总数。

您可以在mongodb 3.4中使用以下聚合

db.collection.aggregate([
  { "$facet": {
    "totalData": [
      { "$match": { }},
      { "$skip": 10 },
      { "$limit": 10 }
    ],
    "totalCount": [
      { "$group": {
        "_id": null,
        "count": { "$sum": 1 }
      }}
    ]
  }}
])
Run Code Online (Sandbox Code Playgroud)

即使您可以使用 $countmongodb 3.6中引入的聚合。

您可以在mongodb 3.6中使用以下聚合

db.collection.aggregate([
  { "$facet": {
    "totalData": [
      { "$match": { }},
      { "$skip": 10 },
      { "$limit": 10 }
    ],
    "totalCount": [
      { "$count": "count" }
    ]
  }}
])
Run Code Online (Sandbox Code Playgroud)

  • 如果您希望在 $match 发生后获取数据的总数,请将 $match 放在 $facet 之前即可实现此目的。 (7认同)
  • [这个答案](/sf/answers/3463874361/)解释了同样的事情,但更清楚。 (3认同)
  • @randombits这应该是公认的答案! (2认同)

hei*_*nob 22

,没有其他办法.两个查询 - 一个用于计数 - 一个用于限制.或者您必须使用其他数据库.例如Apache Solr就像您想要的那样工作.每个查询都有限制返回totalCount.

  • 既然我们有 mongoDb 3.4,我不确定“不”是否已经是正确的答案。见 /sf/answers/2784939601/ (5认同)

dev*_*dev 12

MongoDB 允许您使用,cursor.count()即使您通过limit()skip().

假设您有db.collection10 个项目。

你可以做:

async function getQuery() {
  let query = await db.collection.find({}).skip(5).limit(5); // returns last 5 items in db
  let countTotal = await query.count() // returns 10-- will not take `skip` or `limit` into consideration
  let countWithConstraints = await query.count(true) // returns 5 -- will take into consideration `skip` and `limit`
  return { query, countTotal } 
}
Run Code Online (Sandbox Code Playgroud)

  • 对我来说最好的一个,我讨厌聚合^^。我发现这种方式更简单且可读。 (2认同)
  • `.skip(5).limit(5)` 不会返回数据库中的最后 5 项。它返回第二组 5 个项目。不管有多少个项目,只要至少有 10 个,count() 总是返回 10。 (2认同)
  • Mongo 版本 4.4 和 mongo 节点客户端版本 4 不显示总项目数。 (2认同)

Tes*_*ell 7

时代变了,我相信你能达到什么OP是通过聚合与要求$sort,$group$project.对于我的系统,我还需要从我的users收藏中获取一些用户信息.希望这也可以回答任何问题.下面是一个聚合管道.最后三个对象(排序,组和项目)处理总计数,然后提供分页功能.

db.posts.aggregate([
  { $match: { public: true },
  { $lookup: {
    from: 'users',
    localField: 'userId',
    foreignField: 'userId',
    as: 'userInfo'
  } },
  { $project: {
    postId: 1,
    title: 1,
    description: 1
    updated: 1,
    userInfo: {
      $let: {
        vars: {
          firstUser: {
            $arrayElemAt: ['$userInfo', 0]
          }
        },
        in: {
          username: '$$firstUser.username'
        }
      }
    }
  } },
  { $sort: { updated: -1 } },
  { $group: {
    _id: null,
    postCount: { $sum: 1 },
    posts: {
      $push: '$$ROOT'
    }
  } },
  { $project: {
    _id: 0,
    postCount: 1,
    posts: {
      $slice: [
        '$posts',
        currentPage ? (currentPage - 1) * RESULTS_PER_PAGE : 0,
        RESULTS_PER_PAGE
      ]
    }
  } }
])
Run Code Online (Sandbox Code Playgroud)


小智 7

在Mongodb 3.4中有一种方法:$ facet

你可以做

db.collection.aggregate([
  {
    $facet: {
      data: [{ $match: {} }],
      total: { $count: 'total' }
    }
  }
])
Run Code Online (Sandbox Code Playgroud)

那么你将能够同时运行两个聚合

  • 只需要很少的更新总数就应该是一个像total这样的数组:[{$ count:'total'}] (3认同)

Hed*_*ith 7

以下是使用MongoDB 3.4+(使用 Mongoose)执行此操作的方法$facets。此示例$count根据匹配的文档返回 a 。

const facetedPipeline = [{
    "$match": { "dateCreated": { $gte: new Date('2021-01-01') } },
    "$project": { 'exclude.some.field': 0 },
  },
  {
    "$facet": {
      "data": [
        { "$skip": 10 },
        { "$limit": 10 }
      ],
      "pagination": [
        { "$count": "total" }
      ]
    }
  }
];

const results = await Model.aggregate(facetedPipeline);

Run Code Online (Sandbox Code Playgroud)

此模式对于获取从 REST API 返回的分页信息很有用。

参考:MongoDB $facet


小智 6

默认情况下,count() 方法会忽略 cursor.skip() 和 cursor.limit() 的影响(MongoDB 文档

由于count方法排除了limit和skip的影响,可以使用cursor.count()得到总计数

 const cursor = await database.collection(collectionName).find(query).skip(offset).limit(limit)
 return {
    data: await cursor.toArray(),
    count: await cursor.count() // this will give count of all the documents before .skip() and limit()
 };
Run Code Online (Sandbox Code Playgroud)


Ros*_*oss 5

这完全取决于您是否需要进行两次查询所需的分页经验。

您是否需要列出每一页甚至一系列页面?有没有人甚至转到第 1051 页 - 从概念上讲,这实际上意味着什么?

有很多关于分页模式的 UX -避免分页的痛苦涵盖了各种类型的分页及其场景,许多不需要计数查询来知道是否有下一页。例如,如果您在一个页面上显示 10 个项目并且您限制为 13 个 - 您就会知道是否还有另一个页面。