helpers.js
2.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* helpers —— 共享查询助手:纯函数,不持有连接,作用于传入的 query builder 或行对象。
*
* 零 IO、最易测。`paginate` / `notDeleted` 在 builder 上施加约束并返回 builder(链式)。
* 无任何领域语义。全链路统一 snake_case(plan/db-root-solution.md 决定 2),不存在任何大小写转换助手。
*/
/**
* 游标分页(keyset):在 builder 上施加 keyset 分页,返回 builder。
*
* 语义:按 `orderBy.column` 排序,取 `limit` 行;若给了 `cursor`(上一页最后一行的
* 排序列值),则只取该游标之后的行(升序 `>`,降序 `<`)。多取一行由调用方判断 hasMore
* 不在此处兜——本函数职责单一,只施加 where + orderBy + limit。
*
* @param {import('knex').Knex.QueryBuilder} qb Knex query builder
* @param {Object} [opts]
* @param {string|number|null} [opts.cursor] 上一页最后一行的排序列值;首页传 null/undefined
* @param {number} [opts.limit] 每页条数(默认 20)
* @param {{column?:string,direction?:('asc'|'desc')}} [opts.orderBy] 排序列与方向
* @returns {import('knex').Knex.QueryBuilder} 同一个 builder(链式)
*/
export function paginate(qb, opts = {}) {
const limit =
typeof opts.limit === 'number' && Number.isInteger(opts.limit) && opts.limit > 0
? opts.limit
: 20
const column = (opts.orderBy && opts.orderBy.column) || 'id'
const direction = (opts.orderBy && opts.orderBy.direction) === 'desc' ? 'desc' : 'asc'
if (opts.cursor !== undefined && opts.cursor !== null) {
const operator = direction === 'desc' ? '<' : '>'
qb.where(column, operator, opts.cursor)
}
return qb.orderBy(column, direction).limit(limit)
}
/**
* 软删除过滤:where deleted_at is null。
* @param {import('knex').Knex.QueryBuilder} qb
* @param {string} [column] 软删除列名(默认 deleted_at)
* @returns {import('knex').Knex.QueryBuilder}
*/
export function notDeleted(qb, column = 'deleted_at') {
return qb.whereNull(column)
}
/** 共享查询助手集合。 */
export const helpers = {
paginate,
notDeleted,
}