findByCursor
基于游标的分页查询,适用于需要高效分页和无限滚动的场景。
方法签名
findByCursor(options: FindByCursorOptions<T>): Observable<InstanceType<T>[]>
参数说明
interface FindByCursorOptions<T> {
// 查询条件
where: RuleGroup<InstanceType<T>>;
// 排序条件(必需)
// 必须包含唯一值字段(如 id)作为最后一个排序参数
orderBy: OrderBy[];
// 获取此实体之前的数据
before?: InstanceType<T>;
// 获取此实体之后的数据
after?: InstanceType<T>;
// 获取数据量
// 默认值: 100
limit?: number;
}
使用场景
1. 基础游标分页
import { rxdb } from '@aiao/rxdb';
// 第一页数据
const firstPage = await rxdb.pipe(
switchMap(() =>
Todo.findByCursor({
where: {
combinator: 'and',
rules: []
},
orderBy: [
{ field: 'createdAt', sort: 'desc' },
{ field: 'id', sort: 'asc' } // 必须包含唯一字段
],
limit: 20
})
),
firstValueFrom
);
// 下一页数据(使用 after 游标)
if (firstPage.length > 0) {
const lastItem = firstPage[firstPage.length - 1];
const nextPage = await rxdb.pipe(
switchMap(() =>
Todo.findByCursor({
where: {
combinator: 'and',
rules: []
},
orderBy: [
{ field: 'createdAt', sort: 'desc' },
{ field: 'id', sort: 'asc' }
],
after: lastItem,
limit: 20
})
),
firstValueFrom
);
}
2. 向前翻页
// 上一页数据(使用 before 游标)
const firstItem = currentPage[0];
const prevPage = await rxdb.pipe(
switchMap(() =>
Todo.findByCursor({
where: {
combinator: 'and',
rules: []
},
orderBy: [
{ field: 'createdAt', sort: 'desc' },
{ field: 'id', sort: 'asc' }
],
before: firstItem,
limit: 20
})
),
firstValueFrom
);
3. 带过滤条件的游标分页
const completedTodos = await rxdb.pipe(
switchMap(() =>
Todo.findByCursor({
where: {
combinator: 'and',
rules: [
{
field: 'completed',
operator: '=',
value: true
}
]
},
orderBy: [
{ field: 'completedAt', sort: 'desc' },
{ field: 'id', sort: 'asc' }
],
limit: 50
})
),
firstValueFrom
);
4. 无限滚动实现
重要注意事项
orderBy 必须包含唯一字段
游标分页需要通过排序字段来定位数据位置,因此 orderBy 数组的最后一个字段必须是唯一值字段(通常是 id),否则无法准确定位游标位置。
// ✅ 正确:包含唯一字段 id
orderBy: [
{ field: 'createdAt', sort: 'desc' },
{ field: 'id', sort: 'asc' }
];
// ❌ 错误:没有唯一字段
orderBy: [{ field: 'createdAt', sort: 'desc' }];
游标分页 vs 偏移分页
| 特性 | 游标分页 (findByCursor) | 偏移分页 (find + offset) |
|---|---|---|
| 性能 | 高性能,O(1) 复杂度 | 大偏移量时性能差 |
| 数据一致性 | 强一致性 | 可能跳过或重复数据 |
| 随机访问 | 不支持跳页 | 支持跳转任意页 |
| 适用场景 | 无限滚动、实时流 | 传统分页导航 |
最佳实践
- 始终包含唯一字段排序:确保
orderBy最后一个字段是唯一的 - 合理设置 limit:根据 UI 需求设置合适的页面大小(通常 20-100 条)
- 缓存游标数据:在前端缓存已加载的数据,避免重复请求
- 处理空结果:当返回空数组时,表示已到达末尾
- 双向加载:同时支持
before和after,提供更好的用户体验