FEAT: Video Search In Discovery
Build, Push and Deploy Discovery / build-push-deploy (push) Successful in 1m57s
Build, Push and Deploy Discovery / build-push-deploy (push) Successful in 1m57s
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
DROP INDEX IF EXISTS videos_search_vector_idx;
|
||||
ALTER TABLE videos DROP COLUMN IF EXISTS search_vector;
|
||||
@@ -0,0 +1,25 @@
|
||||
-- Lexical search over video titles.
|
||||
--
|
||||
-- The vector is a generated column rather than something the consumer writes:
|
||||
-- the catalogue's only writer is SaveVideo, and a title that arrives in an
|
||||
-- announcement should be searchable because it is stored, not because a second
|
||||
-- statement remembered to index it. STORED because a GIN index needs the value
|
||||
-- on disk to index it at all.
|
||||
--
|
||||
-- 'arabic' is the text search configuration, not 'english' or 'simple': it
|
||||
-- stems Arabic (الوثائقي -> وثايق) while leaving Latin-script tokens as written,
|
||||
-- which is the right trade for a mixed catalogue. The cost is that English
|
||||
-- words are not stemmed, so "documentaries" does not find "documentary".
|
||||
--
|
||||
-- The configuration is named explicitly rather than left to default_text_search_config,
|
||||
-- which is a per-session GUC — an expression that reads it is only STABLE, and
|
||||
-- a generated column requires IMMUTABLE. Naming it is also what keeps the
|
||||
-- column and every query in step: a query built with a different
|
||||
-- configuration would stem its terms differently and silently match nothing.
|
||||
ALTER TABLE videos
|
||||
ADD COLUMN search_vector tsvector
|
||||
GENERATED ALWAYS AS (to_tsvector('arabic', title)) STORED;
|
||||
|
||||
-- GIN, not GiST: the catalogue is read far more than it is written, and GIN
|
||||
-- answers @@ faster at the cost of a slower update — the right way round here.
|
||||
CREATE INDEX videos_search_vector_idx ON videos USING GIN (search_vector);
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS videos_categories_idx;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Narrowing a search by category.
|
||||
--
|
||||
-- categories is a TEXT[] of names, and the search overlaps it against the
|
||||
-- names a reader asked for (&&). GIN is the index type that operator can use;
|
||||
-- without it the overlap is a filter applied after the rows are read, which is
|
||||
-- the whole table once the title term is broad.
|
||||
CREATE INDEX videos_categories_idx ON videos USING GIN (categories);
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS videos_recent_idx;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- Browsing the catalogue, and paging through it.
|
||||
--
|
||||
-- Every search orders by (rank, created_at, id). When there is no title term
|
||||
-- the rank is a constant, and Postgres folds a constant sort key away — so the
|
||||
-- order that remains is exactly this index, and a browse becomes an index-only
|
||||
-- scan that stops as soon as the page is full instead of sorting the table.
|
||||
--
|
||||
-- Measured at 200k rows: a first page of a browse goes from a 15 ms parallel
|
||||
-- sequential scan with a top-N sort to 0.03 ms. It matters more for the cursor
|
||||
-- than for the first page — the keyset comparison (created_at, id) < (…)
|
||||
-- becomes an index condition rather than a filter, so a deep page is a seek
|
||||
-- to the right place in the index rather than a walk through everything
|
||||
-- before it.
|
||||
--
|
||||
-- DESC on both columns to match the ORDER BY: a btree can be read backwards,
|
||||
-- but only as a whole, so a mixed-direction ordering could not use it.
|
||||
CREATE INDEX videos_recent_idx ON videos (created_at DESC, id DESC);
|
||||
Reference in New Issue
Block a user