( orderBy: OrderBy, values: Array<unknown>, )
| 19 | * @returns A filter expression for rows after the cursor position, or undefined if empty |
| 20 | */ |
| 21 | export function buildCursor( |
| 22 | orderBy: OrderBy, |
| 23 | values: Array<unknown>, |
| 24 | ): BasicExpression<boolean> | undefined { |
| 25 | if (values.length === 0 || orderBy.length === 0) { |
| 26 | return undefined |
| 27 | } |
| 28 | |
| 29 | // For single column, just use simple gt/lt |
| 30 | if (orderBy.length === 1) { |
| 31 | const { expression, compareOptions } = orderBy[0]! |
| 32 | const operator = compareOptions.direction === `asc` ? gt : lt |
| 33 | return operator(expression, new Value(values[0])) |
| 34 | } |
| 35 | |
| 36 | // For multi-column, build the composite cursor: |
| 37 | // or( |
| 38 | // gt(col1, v1), |
| 39 | // and(eq(col1, v1), gt(col2, v2)), |
| 40 | // and(eq(col1, v1), eq(col2, v2), gt(col3, v3)), |
| 41 | // ... |
| 42 | // ) |
| 43 | const clauses: Array<BasicExpression<boolean>> = [] |
| 44 | |
| 45 | for (let i = 0; i < orderBy.length && i < values.length; i++) { |
| 46 | const clause = orderBy[i]! |
| 47 | const value = values[i] |
| 48 | |
| 49 | // Build equality conditions for all previous columns |
| 50 | const eqConditions: Array<BasicExpression<boolean>> = [] |
| 51 | for (let j = 0; j < i; j++) { |
| 52 | const prevClause = orderBy[j]! |
| 53 | const prevValue = values[j] |
| 54 | eqConditions.push(eq(prevClause.expression, new Value(prevValue))) |
| 55 | } |
| 56 | |
| 57 | // Add the comparison for the current column (respecting direction) |
| 58 | const operator = clause.compareOptions.direction === `asc` ? gt : lt |
| 59 | const comparison = operator(clause.expression, new Value(value)) |
| 60 | |
| 61 | if (eqConditions.length === 0) { |
| 62 | // First column: just the comparison |
| 63 | clauses.push(comparison) |
| 64 | } else { |
| 65 | // Subsequent columns: and(eq(prev...), comparison) |
| 66 | // We need to spread into and() which expects at least 2 args |
| 67 | const allConditions = [...eqConditions, comparison] |
| 68 | clauses.push(allConditions.reduce((acc, cond) => and(acc, cond))) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // Combine all clauses with OR |
| 73 | if (clauses.length === 1) { |
| 74 | return clauses[0]! |
| 75 | } |
| 76 | // Use reduce to combine with or() which expects exactly 2 args |
| 77 | return clauses.reduce((acc, clause) => or(acc, clause)) |
| 78 | } |
no test coverage detected