Use an array as a backing store for after

This commit is contained in:
Zoe Roux 2025-01-07 16:28:37 +01:00
parent 879d2959d5
commit 641ce4237e
No known key found for this signature in database

View File

@ -3,9 +3,7 @@ import { eq, or, type Column, and, gt, lt } from "drizzle-orm";
type Table<Name extends string> = Record<Name, Column>; type Table<Name extends string> = Record<Name, Column>;
type After = Record<string, string | number | boolean | undefined> & { type After = [boolean, ...[string | number | boolean | undefined]];
reverse?: boolean;
};
// Create a filter (where) expression on the query to skip everything before/after the referenceID. // Create a filter (where) expression on the query to skip everything before/after the referenceID.
// The generalized expression for this in pseudocode is: // The generalized expression for this in pseudocode is:
@ -31,18 +29,20 @@ export const keysetPaginate = <
sort: Sort<T, Remap>; sort: Sort<T, Remap>;
}) => { }) => {
if (!after) return undefined; if (!after) return undefined;
const { reverse, ...cursor }: After = JSON.parse( const [reverse, ...cursor]: After = JSON.parse(
Buffer.from(after, "base64").toString("utf-8"), Buffer.from(after, "base64").toString("utf-8"),
); );
const pkSort = { key: "pk" as const, desc: false };
// TODO: Add an outer query >= for perf // TODO: Add an outer query >= for perf
// PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic // PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic
let where = undefined; let where = undefined;
let previous = undefined; let previous = undefined;
for (const by of [...sort, { key: "pk" as const, desc: false }]) { for (const [i, by] of [...sort, pkSort].entries()) {
const cmp = by.desc !== reverse ? lt : gt; const cmp = by.desc !== reverse ? lt : gt;
where = or(where, and(previous, cmp(table[by.key], cursor[by.key]))); where = or(where, and(previous, cmp(table[by.key], cursor[i])));
previous = and(previous, eq(table[by.key], cursor[by.key])); previous = and(previous, eq(table[by.key], cursor[i]));
} }
return where; return where;
@ -53,9 +53,10 @@ export const generateAfter = (
sort: Sort<any, any>, sort: Sort<any, any>,
reverse?: boolean, reverse?: boolean,
) => { ) => {
const ret: After = { reverse }; const ret = [
for (const by of sort) { reverse ?? false,
ret[by.key] = cursor[by.key]; ...sort.map((by) => cursor[by.key]),
} cursor.pk,
];
return Buffer.from(JSON.stringify(ret), "utf-8").toString("base64"); return Buffer.from(JSON.stringify(ret), "utf-8").toString("base64");
}; };