跳到主要内容

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) 复杂度大偏移量时性能差
数据一致性强一致性可能跳过或重复数据
随机访问不支持跳页支持跳转任意页
适用场景无限滚动、实时流传统分页导航

最佳实践

  1. 始终包含唯一字段排序:确保 orderBy 最后一个字段是唯一的
  2. 合理设置 limit:根据 UI 需求设置合适的页面大小(通常 20-100 条)
  3. 缓存游标数据:在前端缓存已加载的数据,避免重复请求
  4. 处理空结果:当返回空数组时,表示已到达末尾
  5. 双向加载:同时支持 beforeafter,提供更好的用户体验

参考