diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-07-11 23:41:29 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-07-11 23:41:29 +0200 |
| commit | 47beb3fe6ed6fdb75c2dd3a55362ad1aba3bbc3b (patch) | |
| tree | 2d52eac9a127a8b002dfa951e9b31e677617662b /app/models | |
| parent | 57588a423878016c9981751d50337e37cddea778 (diff) | |
Make page_translations' search trigger self-installing
search_vector is populated by a raw Postgres trigger created via
execute() in a migration -- invisible to schema.rb, which only
represents structure Rails understands. Any database rebuilt from
schema.rb rather than replayed migrations (test, a fresh db:setup,
disaster recovery) silently lost full-text search entirely, with no
error -- NULL @@ anything is never true in Postgres.
Page.ensure_search_vector_trigger!, called from an after_initialize
initializer, reinstalls the trigger via CREATE OR REPLACE on every
boot, in every environment. Idempotent and cheap.
Diffstat (limited to 'app/models')
| -rw-r--r-- | app/models/page.rb | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/app/models/page.rb b/app/models/page.rb index 740d42e..db5b688 100644 --- a/app/models/page.rb +++ b/app/models/page.rb | |||
| @@ -254,6 +254,32 @@ class Page < ApplicationRecord | |||
| 254 | 254 | ||
| 255 | end | 255 | end |
| 256 | 256 | ||
| 257 | # Installs (or re-installs) the trigger that keeps page_translations' | ||
| 258 | # search_vector in sync. Idempotent, safe to call on every boot. | ||
| 259 | # search_vector is populated by a raw Postgres trigger, not anything | ||
| 260 | # Rails' schema dumper can represent -- a database rebuilt from | ||
| 261 | # schema.rb rather than replayed migrations silently loses it. | ||
| 262 | def self.ensure_search_vector_trigger! | ||
| 263 | connection.execute(<<~SQL) | ||
| 264 | CREATE OR REPLACE FUNCTION page_translations_search_vector_update() | ||
| 265 | RETURNS trigger AS $$ | ||
| 266 | BEGIN | ||
| 267 | NEW.search_vector := to_tsvector( | ||
| 268 | 'simple', | ||
| 269 | coalesce(NEW.title, '') || ' ' || | ||
| 270 | coalesce(NEW.abstract, '') || ' ' || | ||
| 271 | coalesce(NEW.body, '') | ||
| 272 | ); | ||
| 273 | RETURN NEW; | ||
| 274 | END; | ||
| 275 | $$ LANGUAGE plpgsql; | ||
| 276 | |||
| 277 | CREATE OR REPLACE TRIGGER page_translations_search_vector_trigger | ||
| 278 | BEFORE INSERT OR UPDATE ON page_translations | ||
| 279 | FOR EACH ROW EXECUTE PROCEDURE page_translations_search_vector_update(); | ||
| 280 | SQL | ||
| 281 | end | ||
| 282 | |||
| 257 | private | 283 | private |
| 258 | 284 | ||
| 259 | def set_page_title | 285 | def set_page_title |
