From dfb398131d49999cacce59442cb22eeab0c687ee Mon Sep 17 00:00:00 2001 From: Velik <69037559+hongochai10@users.noreply.github.com> Date: Fri, 24 Apr 2026 01:04:22 +0700 Subject: [PATCH] feat(db): add FTS GIN + savedSearch partial indexes (GOO-118) (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../migration.sql | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 prisma/migrations/20260423130000_goo118_query_optimization_indexes/migration.sql diff --git a/prisma/migrations/20260423130000_goo118_query_optimization_indexes/migration.sql b/prisma/migrations/20260423130000_goo118_query_optimization_indexes/migration.sql new file mode 100644 index 0000000..61affa6 --- /dev/null +++ b/prisma/migrations/20260423130000_goo118_query_optimization_indexes/migration.sql @@ -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;