summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-07-11 23:41:29 +0200
committererdgeist <erdgeist@erdgeist.org>2026-07-11 23:41:29 +0200
commit47beb3fe6ed6fdb75c2dd3a55362ad1aba3bbc3b (patch)
tree2d52eac9a127a8b002dfa951e9b31e677617662b
parent57588a423878016c9981751d50337e37cddea778 (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.
-rw-r--r--app/models/page.rb26
-rw-r--r--config/initializers/ensure_search_vector_trigger.rb7
2 files changed, 33 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
diff --git a/config/initializers/ensure_search_vector_trigger.rb b/config/initializers/ensure_search_vector_trigger.rb
new file mode 100644
index 0000000..a750e9c
--- /dev/null
+++ b/config/initializers/ensure_search_vector_trigger.rb
@@ -0,0 +1,7 @@
1Rails.application.config.after_initialize do
2 begin
3 Page.ensure_search_vector_trigger! if Page.connection.table_exists?(:page_translations)
4 rescue ActiveRecord::NoDatabaseError, ActiveRecord::ConnectionNotEstablished, PG::ConnectionBad
5 # Database doesn't exist yet -- e.g. mid `rails db:create`. Nothing to install yet.
6 end
7end