Fix null sorting

This commit is contained in:
Zoe Roux 2025-01-10 12:15:31 +01:00
parent 3547799079
commit fe6f4fd43b
No known key found for this signature in database
3 changed files with 29 additions and 13 deletions

View File

@ -1,5 +1,5 @@
import type { NonEmptyArray, Sort } from "./sort";
import { eq, or, type Column, and, gt, lt } from "drizzle-orm";
import { eq, or, type Column, and, gt, lt, isNull } from "drizzle-orm";
type Table<Name extends string> = Record<Name, Column>;
@ -41,8 +41,20 @@ export const keysetPaginate = <
let previous = undefined;
for (const [i, by] of [...sort, pkSort].entries()) {
const cmp = by.desc ? lt : gt;
where = or(where, and(previous, cmp(table[by.key], cursor[i])));
previous = and(previous, eq(table[by.key], cursor[i]));
where = or(
where,
and(
previous,
or(
cmp(table[by.key], cursor[i]),
!table[by.key].notNull ? isNull(table[by.key]) : undefined,
),
),
);
previous = and(
previous,
cursor[i] === null ? isNull(table[by.key]) : eq(table[by.key], cursor[i]),
);
}
return where;

View File

@ -19,6 +19,9 @@ export const createPage = <T>(
) => {
let next: string | null = null;
// we can't know for sure if there's a next page when the current page is full.
// maybe the next page is empty, this is a bit weird but it allows us to handle pages
// without making a new request to the db so it's fine.
if (items.length === limit && limit > 0) {
const uri = new URL(url);
uri.searchParams.set("after", generateAfter(items[items.length - 1], sort));

View File

@ -52,14 +52,6 @@ describe("with a null value", () => {
});
it("sort by dates desc with a null value", async () => {
console.log(
(
await getMovies({
sort: "-airDate",
langs: "en",
})
)[1].items,
);
let [resp, body] = await getMovies({
limit: 2,
sort: "-airDate",
@ -67,6 +59,11 @@ describe("with a null value", () => {
});
expectStatus(resp, body).toBe(200);
expect(body.items.map((x: any) => x.slug)).toMatchObject([
bubble.slug,
dune.slug,
]);
// we copy this due to https://github.com/oven-sh/bun/issues/3521
const next = body.next;
expect(body).toMatchObject({
@ -84,6 +81,10 @@ describe("with a null value", () => {
body = await resp.json();
expectStatus(resp, body).toBe(200);
expect(body.items.map((x: any) => x.slug)).toMatchObject([
dune1984.slug,
"no-air-date",
]);
expect(body).toMatchObject({
items: [
expect.objectContaining({
@ -96,7 +97,7 @@ describe("with a null value", () => {
}),
],
this: next,
next: null,
next: expect.anything(),
});
});
it("sort by dates asc with a null value", async () => {
@ -139,7 +140,7 @@ describe("with a null value", () => {
}),
],
this: next,
next: null,
next: expect.anything(),
});
});
});