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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21803290/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 13:36:15  来源:igfitidea点击:

Get a count of total documents with MongoDB when using limit

mongodbpaginationmongodb-queryaggregation-frameworkcasbah

提问by randombits

I am interested in optimizing a "pagination" solution I'm working on with MongoDB. My problem is straight forward. I usually limit the number of documents returned using the limit()functionality. This forces me to issue a redundant query without the limit()function in order for me to also capture the total number of documents in the query so I can pass to that to the client letting them know they'll have to issue an additional request(s) to retrieve the rest of the documents.

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

Is there a way to condense this into 1 query? Get the total number of documents but at the same time only retrieve a subset using limit()? Is there a different way to think about this problem than I am approaching it?

有没有办法将其压缩为 1 个查询?获取文档总数,但同时仅使用limit()? 考虑这个问题的方式与我正在处理的方式不同吗?

采纳答案by heinob

No, there is no other way. Two queries - one for count - one with limit. Or you have to use a different database. Apache Solr for instance works like you want. Every query there is limited andreturns totalCount.

,没有别的办法。两个查询 - 一个用于计数 - 一个带有限制。或者您必须使用不同的数据库。例如,Apache Solr 可以像您想要的那样工作。每个查询都有限制返回 totalCount。

回答by Ashh

Mongodb 3.4has introduced $facetaggregation

Mongodb 3.4引入了$facet聚合

which processes multiple aggregation pipelines within a single stage on the same set of input documents.

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

Using $facetand $groupyou can find documents with $limitand can get total count.

使用$facet$group你可以找到文件,$limit可以得到总数。

You can use below aggregation in mongodb 3.4

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

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

Even you can use $countaggregation which has been introduced in mongodb 3.6.

甚至你可以使用 $count在 mongodb 3.6 中引入的聚合。

You can use below aggregation in mongodb 3.6

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

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

回答by TestWell

Times have changed, and I believe you can achieve what the OP is asking by using aggregation with $sort, $groupand $project. For my system, I needed to also grab some user info from my userscollection. Hopefully this can answer any questions around that as well. Below is an aggregation pipe. The last three objects (sort, group and project) are what handle getting the total count, then providing pagination capabilities.

时代变了,我相信您可以通过将聚合与$sort,$group和一起使用来实现 OP 的要求$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
      ]
    }
  } }
])

回答by Matthew

there is a way in Mongodb 3.4: $facet

Mongodb 3.4 中有一种方法:$facet

you can do

你可以做

db.collection.aggregate([
  {
    $facet: {
      data: [{ $match: {} }],
      total: { $count: 'total' }
    }
  }
])

then you will be able to run two aggregate at the same time

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

回答by Suraj Chandola

By default, the count() method ignores the effects of the cursor.skip() and cursor.limit() (MongoDB docs)

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

As the count method excludes the effects of limit and skip, you can use cursor.count()to get the total count

由于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()
 };

回答by Ross

It all depends on the pagination experience you need as to whether or not you need to do two queries.

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

Do you need to list every single page or even a range of pages? Does anyone even go to page 1051 - conceptually what does that actually mean?

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

Theres been lots of UX on patterns of pagination - Avoid the pains of paginationcovers various types of pagination and their scenarios and many don't need a count query to know if theres a next page. For example if you display 10 items on a page and you limit to 13 - you'll know if theres another page..

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

回答by Daniel Varela

MongoDB allows you to use cursor.count()even when you pass limit()or skip().

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

Lets say you have a db.collectionwith 10 items.

假设您有db.collection10 个项目。

You can do:

你可以做:

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 } 
}

回答by Vibhu Tewary

You can do this in one query. First you run a count and within that run the limit() function.

您可以在一个查询中完成此操作。首先,您运行一个计数,并在其中运行 limit() 函数。

In Node.js and Express.js, you will have to use it like this to be able to use the "count" function along with the toArray's "result".

在 Node.js 和 Express.js 中,您必须像这样使用它才能使用“count”函数和 toArray 的“result”。

var curFind = db.collection('tasks').find({query});

Then you can run two functions after it like this (one nested in the other)

然后你可以像这样在它之后运行两个函数(一个嵌套在另一个中)

curFind.count(function (e, count) {

// Use count here

    curFind.skip(0).limit(10).toArray(function(err, result) {

    // Use result here and count here

    });

});

回答by mrechtien

It ispossible to get the total result size without the effect of limit()using count()as answered here: Limiting results in MongoDB but still getting the full count?

可能得到的总结果大小而不影响limit()使用count()如下回答: 限制MongoDB中的结果,但仍然得到完整的计数?

According to the documentation you can even control whether limit/pagination is taken into account when calling count(): https://docs.mongodb.com/manual/reference/method/cursor.count/#cursor.count

根据文档,您甚至可以控制调用时是否考虑限制/分页count()https: //docs.mongodb.com/manual/reference/method/cursor.count/#cursor.count

Edit: in contrast to what is written elsewhere - the docs clearly state that "The operation does not perform the query but instead counts the results that would be returned by the query". Which - from my understanding - means that only one query is executed.

编辑:与其他地方写的相反 - 文档明确指出“该操作不执行查询,而是计算查询将返回的结果”。根据我的理解,这意味着只执行一个查询。

Example:

例子:

> db.createCollection("test")
{ "ok" : 1 }

> db.test.insert([{name: "first"}, {name: "second"}, {name: "third"}, 
{name: "forth"}, {name: "fifth"}])
BulkWriteResult({
    "writeErrors" : [ ],
    "writeConcernErrors" : [ ],
    "nInserted" : 5,
    "nUpserted" : 0,
    "nMatched" : 0,
    "nModified" : 0,
    "nRemoved" : 0,
    "upserted" : [ ]
})

> db.test.find()
{ "_id" : ObjectId("58ff00918f5e60ff211521c5"), "name" : "first" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c6"), "name" : "second" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c7"), "name" : "third" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c8"), "name" : "forth" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c9"), "name" : "fifth" }

> db.test.count()
5

> var result = db.test.find().limit(3)
> result
{ "_id" : ObjectId("58ff00918f5e60ff211521c5"), "name" : "first" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c6"), "name" : "second" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c7"), "name" : "third" }

> result.count()
5 (total result size of the query without limit)

> result.count(1)
3 (result size with limit(3) taken into account)

回答by surinder singh

Try as bellow:

尝试如下:

cursor.count(false, function(err, total){ console.log("total", total) })

cursor.count(false, function(err, total){ console.log("total", total) })

core.db.users.find(query, {}, {skip:0, limit:1}, function(err, cursor){
    if(err)
        return callback(err);

    cursor.toArray(function(err, items){
        if(err)
            return callback(err);

        cursor.count(false, function(err, total){
            if(err)
                return callback(err);

            console.log("cursor", total)

            callback(null, {items: items, total:total})
        })
    })
 })