I have a tsv column on my campaign table and I also added a trigger to update it every time the row changes. Tsv is currently constructed from a few table columns. The table also has multiple tags via many-to-many relation. My question is, is it possible to write such a trigger that would also pull in all the tags on row INSERT and update the tsv column when tags are added or removed from a campaign? I already know how to construct a concat document in a query (eg: Full text search on multiple joined tables). I imagine this could work if I wrote another trigger on the many-to-many intermediate table that would update campaigns tsv or something like that.
Edit:
This is what I came up with so far and it seems to work but I would like to get an opinion about the whole thing. I have triggers for campaign INSERT, campaign column name UPDATE, INSERT/UPDATE on campaign_tags and one for campaign_tags DELETE. Each one updates the campaign.tsv but in slightly different way.
ALTER TABLE campaigns ADD COLUMN tsv tsvector;
CREATE INDEX campaigns_tsv_idx ON newsletters USING GIN (tsv);
campaigns_tsv_update
CREATE OR REPLACE FUNCTION campaigns_tsv_update() RETURNS trigger AS $$
begin
SELECT setweight(to_tsvector(unaccent(coalesce(name, ' '))), 'A') ||
setweight(to_tsvector(coalesce((string_agg(tags.value, ' ')), '')), 'D')
INTO new.tsv
FROM campaigns
LEFT JOIN campaign_tags ON campaign_tags.campaign_id = campaigns.id
LEFT JOIN tags ON tags.id = campaign_tags.campaign_tags_id
WHERE campaigns.id=new.id
GROUP BY campaigns.id;
RAISE NOTICE 'campaigns_tsv_trigger new is %', new;
return new;
end
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS campaigns_tsv_trigger_update ON campaigns;
CREATE TRIGGER campaigns_tsv_trigger_update BEFORE UPDATE OF name
ON campaigns FOR EACH ROW EXECUTE PROCEDURE campaigns_tsv_update();
campaigns_tsv_insert
CREATE OR REPLACE FUNCTION campaigns_tsv_insert() RETURNS trigger AS $$
begin
new.tsv := setweight(to_tsvector(unaccent(coalesce(new.name, ' '))), 'A');
return new;
end
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS campaigns_tsv_trigger_insert ON campaigns;
CREATE TRIGGER campaigns_tsv_trigger_insert BEFORE INSERT
ON campaigns FOR EACH ROW EXECUTE PROCEDURE campaigns_tsv_insert();
campaigns_tags_tsv_update
CREATE OR REPLACE FUNCTION campaign_tags_tsv_update() RETURNS trigger AS $$
begin
UPDATE campaigns SET tsv=subquery.document FROM (
SELECT setweight(to_tsvector(unaccent(coalesce(name, ' '))), 'A') ||
setweight(to_tsvector(coalesce((string_agg(tags.value, ' ')), '')), 'D') AS document
FROM campaigns
LEFT JOIN campaign_tags ON campaign_tags.campaign_id = campaigns.id
LEFT JOIN tags ON tags.id = campaign_tags.campaign_tags_id
WHERE campaigns.id=new.campaign_id
GROUP BY campaigns.id
) AS subquery WHERE campaigns.id=new.campaign_id;
RAISE NOTICE 'campaign_tags_tsv_update new is %', new;
return new;
end;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS campaigns_tags_tsv_trigger ON campaign_tags;
CREATE TRIGGER campaigns_tags_tsv_trigger AFTER INSERT OR UPDATE
ON campaign_tags FOR EACH ROW EXECUTE PROCEDURE campaign_tags_tsv_update();
campaigns_tags_tsv_update_del
CREATE OR REPLACE FUNCTION campaign_tags_tsv_update_del() RETURNS trigger AS $$
begin
UPDATE campaigns SET tsv=subquery.document FROM (
SELECT setweight(to_tsvector(unaccent(coalesce(name, ' '))), 'A') ||
setweight(to_tsvector(coalesce((string_agg(tags.value, ' ')), '')), 'D') AS document
FROM campaigns
LEFT JOIN campaign_tags ON campaign_tags.campaign_id = campaigns.id
LEFT JOIN tags ON tags.id = campaign_tags.campaign_tags_id
WHERE campaigns.id=old.campaign_id
GROUP BY campaigns.id
) AS subquery WHERE campaigns.id=old.campaign_id;
RAISE NOTICE 'campaign_tags_tsv_update_del old is %', old;
return old;
end;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS campaigns_tags_tsv_trigger_old ON campaign_tags;
CREATE TRIGGER campaigns_tags_tsv_trigger_old AFTER DELETE
ON campaign_tags FOR EACH ROW EXECUTE PROCEDURE campaign_tags_tsv_update_del();