Laf 云数据库分页查询实现指南:基于 skip/limit 的完整实践
【免费下载链接】lafLaf is a vibrant cloud development platform that provides essential tools like cloud functions, databases, and storage solutions. It enables developers to quickly unleash their creativity and bring innovative ideas to life with ease.项目地址: https://gitcode.com/GitHub_Trending/la/laf
本文基于 Laf(docs/zh/cloud-database/query/pagination.md)讲解云数据库中分页查询的核心机制:通过 MongoDB 驱动原生的skip()与limit()方法将查询结果按页返回,并结合仓库源码剖析cloud.mongo.db实例的注入链路、FunctionContext中分页参数的来源,以及页码计算与总数统计的完整实现方案。读完后你可以直接在云函数中实现"列表 + 总数 + 页码"的标准分页接口,并理解其底层的数据访问链路。
一、分页查询概述
分页查询是指将查询结果分页返回,以便于客户端进行分页展示。在 Laf 云数据库中,分页查询通过skip和limit两个游标方法实现:
skip(n):跳过前 n 条文档,即指定查询的起始偏移位置;limit(n):限制返回的文档数量,即指定每页的文档数量(页大小)。
两者组合即可完成任意一页的读取。以下是最基本的分页查询写法(继承自官方文档示例):
import cloud from '@lafjs/cloud' const db = cloud.mongo.db export default async function () { const docs = await db.collection('users') .find({}) .skip(0) .limit(10) .toArray() console.log(docs) }其中skip(0)表示跳过 0 个文档,limit(10)表示返回 10 个文档,即返回第 1 页的文档。cloud.mongo.db是 MongoDB Node.js 驱动的原生Db实例,其类型在 cloud.interface.ts 中被定义为mongodb.Db,因此find、skip、limit、toArray等游标方法的语义与 MongoDB 驱动的 FindCursor 完全一致,可以直接按官方驱动文档使用。
二、页码计算与完整分页接口示例
在实际业务中,分页参数通常来自 HTTP 请求的 query。Laf 文档给出的完整示例如下,该云函数接收pageSize(每页文档数量,默认 10)和pageIndex(当前页码,默认第 1 页)两个参数:
import cloud from '@lafjs/cloud' const db = cloud.mongo.db export default async function (ctx: FunctionContext) { // 每页的文档数量,默认为 10 const pageSize = ctx.query.pageSize || 10 // 当前页码,默认为第一页 const pageIndex = ctx.query.pageIndex || 1 // 计算查询的起始位置,即跳过的文档数量 const skip = pageSize * (pageIndex - 1) // 查询文档 const docs = await db.collection('users') .find({}) .skip(skip) .limit(pageSize) .toArray() // 查询总数 const total = await db.collection('users').countDocuments() return { list: docs, total, pageSize, pageIndex } }2.1 分页参数说明
| 参数 | 来源 | 默认值 | 作用 |
|---|---|---|---|
pageSize | ctx.query.pageSize | 10 | 每页返回的文档数量,对应limit()入参 |
pageIndex | ctx.query.pageIndex | 1 | 当前页码,从 1 开始计数 |
skip | 计算值 | 0 | 跳过的文档数量,计算式为pageSize * (pageIndex - 1) |
total | countDocuments() | — | 满足查询条件的文档总数,用于前端渲染总页数 |
页码与偏移量的换算关系是skip = pageSize * (pageIndex - 1):第 1 页时skip = 0;第 2 页时skip = pageSize;依此类推。该公式保证了各页之间既不重叠也不遗漏。
2.2 ctx.query 的来源:FunctionContext
示例中的ctx是 Laf 云函数的执行上下文,其类型定义位于 function.interface.ts。从源码可以看到,FunctionContext是一个开放式的接口,除了files、headers、body、params、user、request、response等标准字段外,还带有query?: any与[key: string]: any索引签名,因此ctx.query.pageSize这类访问方式可以直接读取 HTTP 请求的查询参数,无需额外解构。
2.3 返回结构
云函数返回的{ list, total, pageSize, pageIndex }是一个可直接被前端消费的分页契约:list为当前页文档数组,total供前端计算总页数(Math.ceil(total / pageSize)),pageSize与pageIndex则回显当前页状态,便于前端翻页时还原请求参数。
三、源码链路:cloud.mongo.db 是如何被注入的
为了理解文档示例为何在云函数内可以直接import cloud from '@lafjs/cloud'并使用cloud.mongo.db,需要看 Laf 运行时对 SDK 的装配过程。
SDK 的懒加载设计。cloud.ts 中
Cloud类通过私有 gettercloud持有真实实现:首次访问时从globalThis.createCloudSdk取出运行时注册的工厂函数,并缓存到_cloud。mongo属性则通过get mongo()透传给底层实例(cloud.ts)。运行时注册工厂。Node.js 运行时的入口 index.ts 将
createCloudSdk挂到globalThis,使@lafjs/cloud包能在云函数沙箱内拿到真实实例。mongo 实例的构造与热更新。cloud-sdk.ts 的
createCloudSdk()中,mongo: { client: DatabaseAgent.client, db: DatabaseAgent.db }先以初始值注入;随后DatabaseAgent.ready.then(...)会在数据库连接就绪后,把cloud.mongo.db替换为DatabaseAgent.accessor.db。accessor是 database-proxy 包提供的MongoAccessor包装,从源码结构看,云函数发起的查询最终经过这一访问器进入 MongoDB,这也意味着数据库的安全策略(policy)校验就发生在这层。连接建立与重试机制。db.ts 中
DatabaseAgent.initialize()使用Config.DB_URI创建MongoClient,连接失败时采用指数退避重试(初始 1s,封顶 30s,超过封顶后重置为 1s 并告警)。这解释了为什么文档示例不需要在云函数里做任何连接管理——连接的生命周期由运行时托管,开发者拿到的是已就绪的Db实例。
值得注意的一个细节:cloud.interface.ts中同时保留了已标记@deprecated的database()方法(返回 database-proxy 的 QL 查询实例Db)。新代码应如文档示例所示,直接使用mongo.db这一原生驱动实例,skip()/limit()等游标方法正是该路径下的能力。
四、分页查询的实践建议
在文档示例的基础上,结合仓库内其他查询文档与 MongoDB 驱动的行为,补充以下工程实践要点:
保证排序稳定性。
find({})不带排序条件时,文档返回顺序不保证稳定。在数据有增删的情况下,"相同skip/limit参数两次请求可能返回不同文档",导致翻页出现重复或丢失。建议配合 排序查询 在游标链上加入.sort({ createdAt: -1 })之类的确定序,保证分页结果可复现。注意大偏移量场景。
skip(n)需要数据库端先定位并跳过前 n 条记录,当pageIndex很大时skip随之线性增长,查询开销也随之上升。对于深翻页场景,可以改用基于游标值的"下一页"方案(例如以上一页最后一条记录的排序字段作为下页查询条件),避免每次都从头跳过;这是 MongoDB 分页的通用工程经验。总数与列表的查询条件应一致。文档示例中
countDocuments()不带过滤条件,适用于全集合分页。若列表查询带了find(filter),countDocuments(filter)必须传入相同条件,否则total与list不匹配,前端总页数会失真。分页参数的防御性处理。
ctx.query.pageSize || 10的写法对缺失参数给了默认值,但来自客户端的pageSize/pageIndex仍建议在业务层做合法性校验(如pageSize上限、pageIndex >= 1),防止异常值导致超大skip或非法页码。
五、总结与延伸阅读
分页查询是 Laf 云数据库最基础也最常用的能力:skip()控制偏移、limit()控制页大小、countDocuments()提供总数,三者组合即可支撑标准的列表分页接口。其底层依托 MongoDB 原生驱动的游标 API,并由运行时统一托管数据库连接与访问策略层,开发者无需关心连接细节。
- 分页查询官方文档
- 排序查询
- 查询条件
- Cloud SDK 接口定义
- 运行时 SDK 装配
- 数据库连接代理
【免费下载链接】lafLaf is a vibrant cloud development platform that provides essential tools like cloud functions, databases, and storage solutions. It enables developers to quickly unleash their creativity and bring innovative ideas to life with ease.项目地址: https://gitcode.com/GitHub_Trending/la/laf
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考