From 5ba0cef60d9e92b593b48915dcf99e8b79be0d12 Mon Sep 17 00:00:00 2001 From: Devin Zuczek Date: Tue, 22 Sep 2026 17:01:29 -0400 Subject: [PATCH] [api] improve invention query --- .../migrations/0027_invention_feed_newest.sql | 17 +++++++++++++++++ apps/api/src/inventions-db.ts | 10 ++++++++++ 2 files changed, 27 insertions(+) create mode 100644 apps/api/migrations/0027_invention_feed_newest.sql diff --git a/apps/api/migrations/0027_invention_feed_newest.sql b/apps/api/migrations/0027_invention_feed_newest.sql new file mode 100644 index 0000000..bb4442d --- /dev/null +++ b/apps/api/migrations/0027_invention_feed_newest.sql @@ -0,0 +1,17 @@ +-- Index the browse/search feed (`searchInventions`) in the order it pages: newest first. +-- Without it every page — however small — parsed the JSON of every published invention, +-- sorted the lot in a temp B-tree and threw all but one page away. With it the query walks +-- this index in order and stops after OFFSET + LIMIT rows. +-- +-- PARTIAL, over exactly the rows `VISIBLE_IN_FEEDS` admits, so it holds only what a feed can +-- show. SQLite uses a partial index only when the query's WHERE contains each of these terms +-- as written, so this WHERE must track `VISIBLE_IN_FEEDS` in src/inventions-db.ts term for +-- term — change one and the index silently stops being used. The key is the same +-- `json_extract` expression the query orders by, which is what lets it replace the sort. +-- Generated from src/inventions-db.ts (SCHEMA_DDL) — keep in sync. + +CREATE INDEX IF NOT EXISTS idx_invention_feed_newest + ON invention (json_extract(data, '$.CreatedAt') DESC, id DESC) + WHERE is_published = 1 + AND hide_from_player = 0 + AND COALESCE(json_extract(data, '$.Accessibility'), 0) <> 2; diff --git a/apps/api/src/inventions-db.ts b/apps/api/src/inventions-db.ts index d3cf2d9..4a099fc 100644 --- a/apps/api/src/inventions-db.ts +++ b/apps/api/src/inventions-db.ts @@ -39,6 +39,13 @@ export const SCHEMA_DDL: string[] = [ `CREATE UNIQUE INDEX IF NOT EXISTS idx_invention_id ON invention (id)`, `CREATE INDEX IF NOT EXISTS idx_invention_creator ON invention (creator_player_id)`, `CREATE INDEX IF NOT EXISTS idx_invention_featured ON invention (is_featured)`, + // Backs `searchInventions`' newest-first paging (migrations/0027). Its WHERE must match + // `VISIBLE_IN_FEEDS` term for term or SQLite won't use it. + `CREATE INDEX IF NOT EXISTS idx_invention_feed_newest + ON invention (json_extract(data, '$.CreatedAt') DESC, id DESC) + WHERE is_published = 1 + AND hide_from_player = 0 + AND COALESCE(json_extract(data, '$.Accessibility'), 0) <> 2`, `CREATE TABLE IF NOT EXISTS invention_interaction ( player_id INTEGER NOT NULL, invention_id INTEGER NOT NULL, @@ -873,6 +880,9 @@ export const INVENTION_ACCESSIBILITY = { * The "anyone may come across this" test the browse feeds and search share: published, not * hidden, and not unlisted. An unlisted invention is still reachable BY ID — that is what * unlisted means — so the by-id reads deliberately don't apply it. + * + * `idx_invention_feed_newest` (SCHEMA_DDL, migrations/0027) is a partial index over exactly + * these terms — edit one here and edit it there, or the feed goes back to a full scan. */ const VISIBLE_IN_FEEDS = [ 'is_published = 1',