MongoDB 范围分页

据说在拥有大量记录的 MongoDB 集合中使用 Skip ()进行分页是很慢的,因此不推荐使用。

可以使用范围分页(基于 > _ id 比较)

db.items.find({_id: {$gt: ObjectId('4f4a3ba2751e88780b000000')}});

可以用来显示前情提要。& next 按钮-但是当你想要显示实际的页码1... 567... 124-你需要预先计算每个页面从哪个“ _ id”开始。

我有两个问题:

1)我什么时候该开始担心这个问题?当有“太多的记录”与跳过()的明显减速?一千?100万?

2)使用范围分页时,显示带有实际页码的链接的最佳方法是什么?

31039 次浏览

问得好!

“多少才算太多?”当然,这取决于您的数据大小和性能要求。就我个人而言,当我跳过500-1000条以上的记录时,我会感到不舒服。

实际的答案取决于你的需求。

首先,导航栏是这样的:

1 2 3 ... 457

他们从总记录计数和页面大小中得到最终的页码。让我们跳到第三页。这需要跳过第一张唱片。当结果到达时,您知道第3页上第一条记录的 id。

1 2 3 4 5 ... 457

让我们跳过一些内容,翻到第五页。

1 ... 3 4 5 6 7 ... 457

你懂的。在每一点上,您都会看到第一页、最后一页和当前页,以及从当前页向前和向后的两页。

查询

var current_id; // id of first record on current page.


// go to page current+N
db.collection.find({_id: {$gte: current_id}}).
skip(N * page_size).
limit(page_size).
sort({_id: 1});


// go to page current-N
// note that due to the nature of skipping back,
// this query will get you records in reverse order
// (last records on the page being first in the resultset)
// You should reverse them in the app.
db.collection.find({_id: {$lt: current_id}}).
skip((N-1)*page_size).
limit(page_size).
sort({_id: -1});

很难给出一个一般性的答案,因为这在很大程度上取决于您使用什么样的查询(或查询)来构造要显示的结果集。如果只能使用索引找到结果并按索引顺序显示,那么 db.datet.find ()。限制()。即使有大量的跳过,也可以很好地执行。这可能是编写代码最简单的方法。但是即使在这种情况下,如果您可以缓存页码并将它们与索引值绑定,那么对于第二个和第三个想要查看第71页的用户来说,可以提高速度。

在一个非常动态的数据集中,文档将被添加和删除,而其他人正在对数据进行分页,这样的缓存将很快变得过时,限制和跳过方法可能是唯一一个足够可靠的方法,以提供良好的结果。

最近,我在使用非唯一字段(例如“ FirstName”)对请求进行分页时遇到了同样的问题。此查询的思想是能够在非唯一字段上实现分页,而无需使用 Skip ()

这里的主要问题是能够查询一个不是唯一的“ FirstName”字段,因为会发生以下情况:

  1. $gt: {“ FirstName”: “ Carlos”}-> 这将跳过所有名字为“ Carlos”的记录
  2. $gte: {“ FirstName”: “ Carlos”}-> 将始终返回相同的数据集

因此,我想出的解决方案是,通过将目标搜索字段与辅助字段相结合,使查询的 $match 部分成为唯一的,从而使其成为唯一的搜索。

升序:

db.customers.aggregate([
{$match: { $or: [ {$and: [{'FirstName': 'Carlos'}, {'_id': {$gt: ObjectId("some-object-id")}}]}, {'FirstName': {$gt: 'Carlos'}}]}},
{$sort: {'FirstName': 1, '_id': 1}},
{$limit: 10}
])

降序:

db.customers.aggregate([
{$match: { $or: [ {$and: [{'FirstName': 'Carlos'}, {'_id': {$gt: ObjectId("some-object-id")}}]}, {'FirstName': {$lt: 'Carlos'}}]}},
{$sort: {'FirstName': -1, '_id': 1}},
{$limit: 10}
])

这个查询的 $match 部分基本上表现为 if 语句: 如果 firstName 是“ Carlos”,那么它也需要大于这个 id 如果 firstName 不等于“ Carlos”那么它必须大于“ Carlos”

唯一的问题是,你不能导航到一个特定的页码(这可能可以通过一些代码操作来完成) ,但除此之外,它解决了我的问题,非唯一字段的分页,而不必使用跳过,耗费了大量的内存和处理能力,当到达任何数据集的结尾时,你正在查询。