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

时间:2012-12-18 15:03:46

标签: mongodb mongoose

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

所以,我总共有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});
    });
});

6 个答案:

答案 0 :(得分:99)

我建议您使用2个查询:

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

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

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

答案 1 :(得分:2)

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

总计“ $ facet” 可以更快地获取, 总计数 带有跳过&限制

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

 ]);

输出如下:-

[{
     count: 50,
     data: [
        {...},
        {...}
      ]
 }]

答案 2 :(得分:1)

在我自己解决这个问题之后,我想建立在user854301的答案之上。

Mongoose ^ 4.13.8我能够使用一个名为toConstructor()的函数,它允许我在应用过滤器时避免多次构建查询。我知道这个功能在旧版本中也可用,但您必须检查Mongoose文档以确认这一点。

以下使用Bluebird承诺:

let schema = Query.find({ name: 'bloggs', age: { $gt: 30 } });

// save the query as a 'template'
let query = schema.toConstructor();

return Promise.join(
    schema.count().exec(),
    query().limit(limit).skip(skip).exec(),

    function (total, data) {
        return { data: data, total: total }
    }
);

现在,count查询将返回它匹配的总记录数,返回的数据将是总记录的一部分。

请注意构建查询的query()周围的()

答案 3 :(得分:1)

您不必使用两个查询或一个带有聚合等的复杂查询。

您可以使用一个查询

示例:

const getNames = async (queryParams) => {

  const cursor = db.collection.find(queryParams).skip(20).limit(10);
  return {
    count: await cursor.count(),
    data: await cursor.toArray()
  }
  
}

mongo 返回一个具有预定义函数的游标,例如count,无论skip和limit,都会返回查询结果的完整计数

因此,在 count 属性中,您将获得集合的完整长度,而在数据中,您将仅获得偏移量为 20 且限制为 10 个文档的块

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

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

  1. 匹配记录
  2. 找到total_count
  3. 跳过记录
  4. 还可以根据查询中的需求重塑数据。