diff --git a/api/src/models/utils/keyset-paginate.ts b/api/src/models/utils/keyset-paginate.ts index 76714dce..44e96a89 100644 --- a/api/src/models/utils/keyset-paginate.ts +++ b/api/src/models/utils/keyset-paginate.ts @@ -29,33 +29,33 @@ export const keysetPaginate = < sort: Sort; }) => { if (!after) return undefined; - let cursor: After = JSON.parse( + const cursor: After = JSON.parse( Buffer.from(after, "base64").toString("utf-8"), ); const pkSort = { key: "pk" as const, desc: false }; + if (sort.random) { + return or( + gt( + sql`md5(${sort.random.seed} || ${table[pkSort.key]})`, + sql`md5(${sort.random.seed} || ${cursor[0]})`, + ), + and( + eq( + sql`md5(${sort.random.seed} || ${table[pkSort.key]})`, + sql`md5(${sort.random.seed} || ${cursor[0]})`, + ), + gt(table[pkSort.key], cursor[0]), + ), + ); + } + // TODO: Add an outer query >= for perf // PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic let where = undefined; let previous = undefined; - if (sort.random) { - const lastCursor = cursor.slice(-1)[0]; - where = or( - where, - and( - previous, - gt( - sql`md5(${sort.random.seed} || ${table[pkSort.key]})`, - sql`md5(${sort.random.seed} || ${lastCursor})`, - ), - ), - ); - - previous = and(previous, eq(table[pkSort.key], lastCursor)); - cursor = cursor.slice(1); - } for (const [i, by] of [...sort.sort, pkSort].entries()) { const cmp = by.desc ? lt : gt; where = or( @@ -79,7 +79,6 @@ export const keysetPaginate = < export const generateAfter = (cursor: any, sort: Sort) => { const ret = [ - ...(sort.random ? [`random:${sort.random.seed}`] : []), ...sort.sort.map((by) => cursor[by.remmapedKey ?? by.key]), cursor.pk, ]; diff --git a/api/tests/movies/get-all-movies.test.ts b/api/tests/movies/get-all-movies.test.ts index 9b25526f..c725d47e 100644 --- a/api/tests/movies/get-all-movies.test.ts +++ b/api/tests/movies/get-all-movies.test.ts @@ -7,7 +7,7 @@ import { bubble } from "~/models/examples"; import { dune1984 } from "~/models/examples/dune-1984"; import { dune } from "~/models/examples/dune-2021"; import { getMovies, movieApp } from "./movies-helper"; -import { Movie } from "~/models/movie"; +import type { Movie } from "~/models/movie"; beforeAll(async () => { await db.delete(shows); @@ -142,26 +142,6 @@ describe("Get all movies", () => { expect(items1Ids).toEqual(items2Ids); }); - it("No limit, compare order with different seeds", async () => { - // First query - let [resp1, body1] = await getMovies({ - sort: "random:100", - }); - expectStatus(resp1, body1).toBe(200); - const items1: Movie[] = body1.items; - const items1Ids = items1.map(({ id }) => id); - - // Second query - let [resp2, body2] = await getMovies({ - sort: "random:5", - }); - expectStatus(resp2, body2).toBe(200); - const items2: Movie[] = body2.items; - const items2Ids = items2.map(({ id }) => id); - - expect(items1Ids).not.toEqual(items2Ids); - }); - it("Limit 1, pages 1 and 2 ", async () => { // First query fetches all // use the result to know what is expected