feat(db): add FTS GIN + savedSearch partial indexes (GOO-118)
Convert the query-optimization recommendations from GOO-57 audit plan
document into concrete Prisma migration changes. Neither index can be
expressed in Prisma schema (expression index / partial WHERE), so both
land as raw SQL in a single migration.
Indexes added:
- idx_property_fts — GIN expression index on Property matching the
search-query-builder FTS_COLUMNS expression exactly:
to_tsvector('simple', coalesce(title,'') || ' ' || coalesce(description,'')
|| ' ' || coalesce(address,'') || ' ' || coalesce(district,'')
|| ' ' || coalesce(city,''))
Addresses GOO-57 M-3 (missing GIN index for FTS).
- idx_savedsearch_alert_enabled — partial btree on SavedSearch(createdAt)
WHERE alertEnabled = true, used by the residential alert listeners and
the saved-search cron (supports GOO-57 H-1 / H-2 follow-up work —
eliminating the seq scan is the prerequisite for cursor batching).
Benchmarks (local PG16, synthetic data):
Property FTS with a selective term (50k rows, ~10 matching):
with idx_property_fts: 3.97 ms (Bitmap Heap Scan, 338 buffers)
without index: 242.56 ms (Parallel Seq Scan, 1784 buffers)
→ ~61x faster.
SavedSearch alert scan (100k rows, 5% alertEnabled, LIMIT 500 ORDER BY
createdAt DESC):
with idx_savedsearch_alert_enabled: 0.48 ms (Index Scan Backward)
without index: 6.05 ms (Seq Scan + top-N sort)
→ ~12x faster, seq scan eliminated.
Hook-up verified: pnpm db:generate clean; raw migration applies via
prisma migrate deploy; post-migration \d confirms both indexes are
present with the expected definitions.
Refs: GOO-118, GOO-57
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
-- GOO-118 — DB query optimization migration
|
||||
-- Source: GOO-57 audit (plan document: /GOO/issues/GOO-57#document-plan)
|
||||
--
|
||||
-- Changes:
|
||||
-- 1. GIN expression index for Property full-text search (addresses M-3)
|
||||
-- Matches the exact expression used by
|
||||
-- apps/api/src/modules/search/infrastructure/services/search-query-builder.ts (FTS_COLUMNS).
|
||||
--
|
||||
-- 2. Partial index on SavedSearch (createdAt) WHERE alertEnabled = true (supports H-1 / H-2)
|
||||
-- Listener / cron loads rows filtered by alertEnabled = true; partial index
|
||||
-- lets the query skip the seq scan and stays small (only ~alertEnabled rows).
|
||||
|
||||
-- 1) GIN FTS index on Property
|
||||
CREATE INDEX IF NOT EXISTS "idx_property_fts"
|
||||
ON "Property"
|
||||
USING GIN (
|
||||
to_tsvector(
|
||||
'simple',
|
||||
coalesce("title", '') || ' ' ||
|
||||
coalesce("description", '') || ' ' ||
|
||||
coalesce("address", '') || ' ' ||
|
||||
coalesce("district", '') || ' ' ||
|
||||
coalesce("city", '')
|
||||
)
|
||||
);
|
||||
|
||||
-- 2) Partial index: only alert-enabled saved searches
|
||||
CREATE INDEX IF NOT EXISTS "idx_savedsearch_alert_enabled"
|
||||
ON "SavedSearch" ("createdAt")
|
||||
WHERE "alertEnabled" = true;
|
||||
Reference in New Issue
Block a user