patternsqlMinor
PostgreSQL full text search tsv column trigger with many-to-many
Viewed 0 times
postgresqlfulltriggersearchcolumnwithtextmanytsv
Problem
I have a tsv column on my
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
campaigns_tsv_update
campaigns_tsv_insert
```
CREATE OR REPLACE FUN
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 FUN
Solution
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.
Incoming.. opinion. Don't use triggers for this. It's wayyy to complex. You have three piece of functionality that will make life easier.
You may also want to restructure how you're doing tags. In PG, you can easily represent them on the table itself with an array type which is easily indexed.
Update
This code just strikes me as awkward. We call this a
Instead
Incoming.. opinion. Don't use triggers for this. It's wayyy to complex. You have three piece of functionality that will make life easier.
- Function indexes.
- Views.
- Materialized views: write a query that generates the output table that you want to query simply issue
REFRESH MATERIALIZED VIEW mymatview;to update it. How often do you have to reflect tag updates anyway? A materialized view can itself use function indexes, or any other kind of index. For instance, you can create a Materialized View of the tsvectors of multiple columns from multiple tables, and then write a function index that uses native functionality over those multiple tsvector columns. Then when you want, periodically runREFRESH MATERIALIZED VIEW mymatview.
You may also want to restructure how you're doing tags. In PG, you can easily represent them on the table itself with an array type which is easily indexed.
Update
This code just strikes me as awkward. We call this a
DEFAULT. Don't implement that as a trigger.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();Instead
ALTER TABLE campaigns ADD COLUMN tsv tsvector DEFAULT setweight(to_tsvector(unaccent(coalesce(name, ' '))), 'A');Code Snippets
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();ALTER TABLE campaigns ADD COLUMN tsv tsvector DEFAULT setweight(to_tsvector(unaccent(coalesce(name, ' '))), 'A');Context
StackExchange Database Administrators Q#154011, answer score: 3
Revisions (0)
No revisions yet.