猫鼬限制/偏移和计数查询

lee*_*ell 68 mongoose mongodb

在查询性能方面有点奇怪...我需要运行一个查询来完成文档的总计数,并且还可以返回一个可以限制和偏移的结果集.

所以,我总共有57个文档,用户希望10个文档偏移20个.

我可以想到两种方法,首先查询所有57个文档(作为数组返回),然后使用array.slice返回他们想要的文档.第二个选项是运行2个查询,第一个使用mongo的本机'count'方法,然后使用mongo的本机$ limit和$ skip聚合器运行第二个查询.

您认为哪个会更好地扩展?在一个查询中完成所有操作,还是运行两个单独的查询?

编辑:

// 1 query
var limit = 10;
var offset = 20;

Animals.find({}, function (err, animals) {
    if (err) {
        return next(err);
    }

    res.send({count: animals.length, animals: animals.slice(offset, limit + offset)});
});


// 2 queries
Animals.find({}, {limit:10, skip:20} function (err, animals) {            
    if (err) {
        return next(err);
    }

    Animals.count({}, function (err, count) {
        if (err) {
            return next(err);
        }

        res.send({count: count, animals: animals});
    });
});
Run Code Online (Sandbox Code Playgroud)

use*_*301 108

我建议你使用2个查询:

  1. db.collection.count()将返回项目总数.此值存储在Mongo中的某个位置,并且不会计算.

  2. db.collection.find().skip(20).limit(10)在这里我假设您可以使用某个字段排序,所以不要忘记在此字段上添加索引.这个查询也很快.

我认为你不应该查询所有项目而不是执行跳过和获取,因为稍后当你有大数据时你会遇到数据传输和处理问题.

  • @Stuffix 我听说过对使用`.skip()` 的同样担忧。这个 [answer](http://stackoverflow.com/questions/5539955/how-to-paginate-with-mongoose-in-node-js/23640287#23640287) 对此进行了改进,并建议在日期字段。可以将其与 `.skip()` 和 `.take()` 方法一起使用。这似乎是个好主意。但是,我在这个 OP 关于如何获取文档总数的问题上遇到了麻烦。如果使用过滤器来对抗`.skip()` 的性能影响,我们如何获得准确的计数?存储在数据库中的计数不会反映我们过滤后的数据集。 (2认同)

Dhi*_*Yes 7

而不是使用2个查询,而是在单个查询中使用聚合

总结“$面”可以更快地获取的总数具有跳跃和限制数据

    db.collection.aggregate([

      //{$sort: {...}}

      //{$match:{...}}

      {$facet:{

        "stage1" : [ $group:{_id:null, count:{$sum:1}} ],

        "stage2" : [ { "$skip": 0}, {"$limit": 2} ]

      }},


     {$unwind: "$stage1"},

      //output projection
     {$project:{
        count: "$stage1.count",
        data: "$stage2"
     }}

 ]);
Run Code Online (Sandbox Code Playgroud)

输出如下:

[{
     count: 50,
     data: [
        {...},
        {...}
      ]
 }]
Run Code Online (Sandbox Code Playgroud)


SAN*_*AVI 7

db.collection_name.aggregate([
    { '$match'    : { } },
    { '$sort'     : { '_id' : -1 } },
    { '$facet'    : {
        metadata: [ { $count: "total" } ],
        data: [ { $skip: 1 }, { $limit: 10 },{ '$project' : {"_id":0} } ] // add projection here wish you re-shape the docs
    } }
] )
Run Code Online (Sandbox Code Playgroud)

而不是使用两个查询来查找总计数并跳过匹配的记录。
$facet 是最好且优化的方式。

  1. 匹配记录
  2. 查找total_count
  3. 跳过记录
  4. 并且还可以根据我们在查询中的需要来重塑数据。


Dev*_*v01 6

有一个库可以为您完成所有这些工作,请查看mongoose-paginate-v2