关于mongodb的分页问题(Nodejs版)

关于mongodb的分页问题(Nodejs版)

好多人说mongodb分页不要用skip,但是网上搜了好久也没搜到解决办法 以前学过java,他们基于Mysql数据库的分页好像是取得符合查询的全部数据放在list里面 然后fetch带两个参数去取数据,实现分页 我就想着能不能也使用这个办法,然后自己写了个简单的实现类 代码在http://blog.csdn.net/chengscau/article/details/40188087 求大家告诉我好点的解决方案,有代码看是最好了~

7 回复

当然可以。MongoDB 的分页确实存在一些限制,特别是当使用 skip 方法时,对于大数据量的查询会变得非常低效。这是因为 MongoDB 在跳过指定数量的文档时需要先找到这些文档,然后再返回你需要的那部分数据。

解决方案

一个更好的方法是使用 limitsort 结合 cursor 来实现高效的分页。我们可以利用索引来提高性能,避免使用 skip 方法。

示例代码

假设我们有一个用户集合 users,并且我们希望根据用户的注册时间进行分页显示。

安装必要的依赖

首先确保你已经安装了 mongoose 这个库来操作 MongoDB 数据:

npm install mongoose

创建 Mongoose 模型

创建一个简单的 Mongoose 模型来表示用户数据:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const userSchema = new Schema({
    name: String,
    email: String,
    createdAt: { type: Date, default: Date.now }
});

module.exports = mongoose.model('User', userSchema);

分页查询函数

接下来,我们编写一个用于分页查询的函数:

const User = require('./models/User');

async function getUsers(page, limit) {
    const skip = (page - 1) * limit;
    const users = await User.find()
        .sort({ createdAt: -1 }) // 按创建时间降序排列
        .skip(skip)
        .limit(limit)
        .exec();

    return users;
}

// 调用函数示例
getUsers(1, 10).then(users => console.log(users));

更好的优化方法

上面的代码虽然有效,但为了进一步优化,我们可以考虑使用 aggregate 查询结合 $skip$limit,或者使用 find 方法配合索引。如果你的数据量很大,强烈建议使用 aggregate 方法,因为它可以更好地处理大数据集。

async function getUsers(page, limit) {
    const skip = (page - 1) * limit;
    const users = await User.aggregate([
        { $sort: { createdAt: -1 } }, // 先排序
        { $skip: skip },
        { $limit: limit }
    ]);

    return users;
}

总结

使用 skip 方法可以实现基本的分页功能,但对于大数据量的场景,这种方法效率较低。更高效的方法是使用 aggregate 查询结合索引,以提高性能并减少资源消耗。希望这能帮助你更好地理解和解决 MongoDB 的分页问题。


先缩小范围,然后再分页。 你用sql也是一样。

卧槽。。。你这个方法是把所有可能的数据都取到服务端来,然后在服务端实现分页。这样的效率绝逼比 mongodb 做 skip 要低得多啊。

不是一次性读取所有的数据,应该是:

  • 确定分页依据的记录域(field),建立索引;
  • 每次执行一个区间查询(range query),对这次查询结果分页。注意:要根据上一次区间查询的结果,修改本次区间查询的条件。

参考代码:MongoDB数据库,使用区间查询分页

MongoDB cursor.skip文档也是推荐使用区间查询,实现高效地分页。

还可以参考这个演示文稿(可能需要科学上网):应该这么分页,内容是使用PostgresSQL数据库,利用区间查询而不是SQL OFFSET实现分页功能,比较了两种方法的查询性能。

查询比较耗时的操作也可以考虑缓存起来啊,改数据结构多麻烦。

目前我个人所知的分页方式有两种: 1)skip + limit 的方式 2)$gt($get) + $lt($lte) + limit 的方式

第一种方式更适合pc网页的数字分页 屏幕快照 2014-10-19 07.56.50.png 第二种方式更适合mobile网页的上一页和下一页的分页 屏幕快照 2014-10-19 07.54.56.png

关于性能问题, 能确定的是:尽量配合一个能大幅度缩小mongodb查询范围的查询条件 举个例子: 如果 topic 列表按照最后更新时间字段 last_update_date 倒序排列, 除了limit和确保击中索引 { last_update_date:-1 } 之外, 实际查询条件可以加入一天的查询: { last_update_date:{$gte:new Date(‘2015-01-01 00:00:00’), $lte:new Date(‘2015-01-02 23:59:59’)}}

这种方式在能有效控制mongodb查询数据的时间成本。

MongoDB 的分页查询确实不推荐使用 skip 方法,因为它在大数据集上性能较差。更好的方法是使用覆盖索引(Covered Index)和 find 方法结合 limitsort 来实现高效的分页。

以下是一个简单的 Node.js 示例代码,展示了如何使用 MongoDB 的 aggregate 方法进行分页查询:

const { MongoClient } = require('mongodb');

async function main() {
    const uri = "your_mongodb_connection_string";
    const client = new MongoClient(uri);

    try {
        await client.connect();
        const database = client.db('your_database_name');
        const collection = database.collection('your_collection_name');

        const pageSize = 10; // 每页条目数量
        const page = 1; // 当前页码
        const skip = (page - 1) * pageSize;

        const pipeline = [
            {
                $match: {
                    someField: 'someValue'
                }
            },
            {
                $sort: {
                    _id: 1 // 假设按_id升序排序
                }
            },
            {
                $skip: skip
            },
            {
                $limit: pageSize
            }
        ];

        const result = await collection.aggregate(pipeline).toArray();
        console.log(result);
    } finally {
        await client.close();
    }
}

main().catch(console.error);

解释

  1. 连接数据库:使用 MongoClient 连接到 MongoDB 数据库。
  2. 定义管道:创建一个聚合管道 (pipeline),包含 $match$sort$skip$limit 阶段。
    • $match:用于过滤文档。
    • $sort:按某个字段排序。
    • $skip:跳过指定数量的文档。
    • $limit:限制返回文档的数量。
  3. 执行聚合查询:使用 collection.aggregate(pipeline).toArray() 执行查询并获取结果。
  4. 关闭连接:确保在操作完成后关闭数据库连接。

这种方式通过减少 skip 的使用,提高了查询效率,尤其是在大数据集上。

回到顶部