如何使用Java在MongoDB上进行分页是最佳方式?

4
我正在尝试使用以下代码在MongoDB中创建简单的分页:

我正在尝试使用以下代码在MongoDB中创建简单的分页。

collection.find().skip(n).limit(n);

但是,如果我们按照Java的术语来看,首先查找将返回所有记录,假设我有200万条记录,那么它将传递给skip方法,然后传递给limit方法。这意味着每次查询时都会获取所有的数据库记录,或者说MongoDB驱动程序工作方式不同,我错过了什么吗?


你有查看文档吗? - Roman
1
是的,它没有像这样的任何信息。 - john cena
2个回答

14

当谈到MongoDB的分页时,很容易写出以下代码:

collection.find().skip(pageSize*(pageNum-1)).limit(pageSize);

上面是MongoDB支持的本地解决方案,但如果集合中有大量文档,则效率不高。假设您有一亿个文档,并且想要从中间偏移(第五千万个文档)获取数据。MongoDB必须建立完整数据集并从开头遍历到指定的偏移量,这会导致性能较差。随着偏移量的增加,性能将继续降低。

根本原因是skip()命令效率不高,无法从索引中获得大的好处。


以下是另一种在大型数据分页上提高性能的方法:

分页的典型用法场景是有一个表格或列表用于显示指定页面的数据,还有一个“上一页”“下一页”按钮用于加载上一页或下一页的数据。

如果您获得了当前页面中最后一个文档的'_id',则可以使用find()而不是skip()。使用_id > currentPage_LastDocument._id作为查找下一页数据的条件之一。以下是伪代码:

//Page 1
collection.find().limit(pageSize);
//Get the _id of the last document in this page
last_id = ...

//Page 2
users = collection.find({'_id': {$gt: last_id}}).limit(pageSize);
//Update the last id with the _id of the last document in this page
last_id = ...

使用skip()时,这将避免MongoDB在遍历大数据集。


1
使用我展示的第二种方法,如何让页面数字显示为1、2、3、4、5,并且用户可以轻松地在它们之间移动? - john cena
@johncena,系统设计取决于您的系统使用和场景,但您没有谈论任何相关内容。如果您想要为用户显示页面编号以供点击,我认为您只能显示(current-2,current-1,current,current+1,current+2),而不是所有页面编号,然后您可以从方法2中获益。实际上,我猜用户不想在页面上看到1〜9999页的所有页面编号 :) - yellowB
如果我想按“双三角形按钮”获取10页或更多页面(这是常见的UI需求),并直接跳转到最后一页怎么办?在这种情况下,我认为没有比跳过/限制更好的方法。 - WesternGun
实际上,对于100万条记录,我发现一个find(Criteria).skip().limit().sort()和一个find(Criteria).count()将在任意页码下以5秒(第一个查询)/ 3秒(第二个查询)的速度返回正确的总数和分页。虽然不是非常好,但没有其他选择。 - WesternGun
这个解决方案将防止我们对集合进行排序,有没有一种方法可以在仍然能够对其他列/字段进行排序的情况下使用这种技术? - Miko Chu

0
另一种方法可以是:
  1. 将文档的 _id 保存为整数,而不是 ObjectId。
{_id : 1, title : ''}  //first document
{_id : 2, title : ''}  //second document

使用最后一个文档的 _id 来查询下一页。
collection.find({ _id: {$gt: last_id} }).limit(10);

使用int来查询特定页面。
//find document 11 to 20
collection.find({ _id: {$gt: 10} }).limit(10);

//find document 21 to 30
collection.find({ _id: {$gt: 20} }).limit(10);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接