Files
ProjectE/drizzle/0005_search_vector_trigger.sql
T

94 lines
4.3 KiB
PL/PgSQL
Raw Normal View History

-- T14/Bug #3 HIGH: Populate tsvector search_vector on every INSERT / UPDATE.
--
-- The schema defines searchVector columns on domains / projects / tasks /
-- habits / notes but the columns are never populated, so /api/search
-- always returns zero results. This migration:
-- 1. Creates a single plpgsql trigger function that builds the tsvector
-- from the table's title-like and content-like text columns.
-- 2. Installs BEFORE INSERT OR UPDATE triggers on each searchable table.
-- 3. Backfills existing rows so they appear in search immediately.
-- ─── Per-table tsvector field lists ─────────────────────────────────────
-- We can't share one trigger function across tables with different column
-- names, so each table gets its own function. PostgreSQL's CREATE OR
-- REPLACE FUNCTION is idempotent — re-running this migration is safe.
-- domains: name only (no description column)
CREATE OR REPLACE FUNCTION project_e_search_vector_domains_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector := to_tsvector('english', coalesce(NEW.name, ''));
RETURN NEW;
END
$$ LANGUAGE plpgsql;
-- projects: name + description
CREATE OR REPLACE FUNCTION project_e_search_vector_projects_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector := to_tsvector('english',
coalesce(NEW.name, '') || ' ' || coalesce(NEW.description, ''));
RETURN NEW;
END
$$ LANGUAGE plpgsql;
-- tasks: title + description
CREATE OR REPLACE FUNCTION project_e_search_vector_tasks_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector := to_tsvector('english',
coalesce(NEW.title, '') || ' ' || coalesce(NEW.description, ''));
RETURN NEW;
END
$$ LANGUAGE plpgsql;
-- habits: name + description
CREATE OR REPLACE FUNCTION project_e_search_vector_habits_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector := to_tsvector('english',
coalesce(NEW.name, '') || ' ' || coalesce(NEW.description, ''));
RETURN NEW;
END
$$ LANGUAGE plpgsql;
-- notes: title + content
CREATE OR REPLACE FUNCTION project_e_search_vector_notes_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector := to_tsvector('english',
coalesce(NEW.title, '') || ' ' || coalesce(NEW.content, ''));
RETURN NEW;
END
$$ LANGUAGE plpgsql;
-- ─── Per-table triggers (BEFORE INSERT OR UPDATE) ───────────────────────
DROP TRIGGER IF EXISTS domains_search_vector_trigger ON domains;
CREATE TRIGGER domains_search_vector_trigger
BEFORE INSERT OR UPDATE ON domains
FOR EACH ROW EXECUTE FUNCTION project_e_search_vector_domains_update();
DROP TRIGGER IF EXISTS projects_search_vector_trigger ON projects;
CREATE TRIGGER projects_search_vector_trigger
BEFORE INSERT OR UPDATE ON projects
FOR EACH ROW EXECUTE FUNCTION project_e_search_vector_projects_update();
DROP TRIGGER IF EXISTS tasks_search_vector_trigger ON tasks;
CREATE TRIGGER tasks_search_vector_trigger
BEFORE INSERT OR UPDATE ON tasks
FOR EACH ROW EXECUTE FUNCTION project_e_search_vector_tasks_update();
DROP TRIGGER IF EXISTS habits_search_vector_trigger ON habits;
CREATE TRIGGER habits_search_vector_trigger
BEFORE INSERT OR UPDATE ON habits
FOR EACH ROW EXECUTE FUNCTION project_e_search_vector_habits_update();
DROP TRIGGER IF EXISTS notes_search_vector_trigger ON notes;
CREATE TRIGGER notes_search_vector_trigger
BEFORE INSERT OR UPDATE ON notes
FOR EACH ROW EXECUTE FUNCTION project_e_search_vector_notes_update();
-- ─── Backfill existing rows ─────────────────────────────────────────────
-- Use IS NOT NULL guard on text columns so blank rows are skipped silently.
-- to_tsvector returns an empty tsvector ('') for blank input — harmless.
UPDATE domains SET search_vector = to_tsvector('english', coalesce(name, ''));
UPDATE projects SET search_vector = to_tsvector('english', coalesce(name,'') || ' ' || coalesce(description,''));
UPDATE tasks SET search_vector = to_tsvector('english', coalesce(title,'') || ' ' || coalesce(description,''));
UPDATE habits SET search_vector = to_tsvector('english', coalesce(name,'') || ' ' || coalesce(description,''));
UPDATE notes SET search_vector = to_tsvector('english', coalesce(title,'') || ' ' || coalesce(content,''));