You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
75 lines
3.3 KiB
75 lines
3.3 KiB
-- Make the formal proposals (yrkanden, <forslag> tags) first-class search targets. |
|
-- They are condensed and to-the-point, so they get a high FTS weight and their |
|
-- own embedding table for semantic search. |
|
-- Apply once: psql -U riksdagen -d riksdagen -f _postgres/migrations/add_motion_yrkanden.sql |
|
|
|
-- ── 1. Keyword: fold yrkanden text into motions.search_vector ────────────── |
|
ALTER TABLE motions ADD COLUMN IF NOT EXISTS forslag_text TEXT; -- concat of yrkande lydelser |
|
|
|
CREATE OR REPLACE FUNCTION motions_search_vector_update() RETURNS TRIGGER AS $$ |
|
BEGIN |
|
NEW.search_vector := |
|
setweight(to_tsvector('swedish', coalesce(NEW.titel, '')), 'A') || |
|
setweight(to_tsvector('swedish', coalesce(NEW.forslag_text, '')), 'B') || |
|
setweight(to_tsvector('swedish', coalesce(NEW.undertitel, '')), 'C') || |
|
setweight(to_tsvector('swedish', coalesce(NEW.text, '')), 'D'); |
|
RETURN NEW; |
|
END; |
|
$$ LANGUAGE plpgsql; |
|
|
|
DROP TRIGGER IF EXISTS motions_search_vector_trigger ON motions; |
|
CREATE TRIGGER motions_search_vector_trigger |
|
BEFORE INSERT OR UPDATE OF titel, undertitel, text, forslag_text |
|
ON motions |
|
FOR EACH ROW |
|
EXECUTE FUNCTION motions_search_vector_update(); |
|
|
|
-- ── 2. Semantic: one embeddable row per yrkande ──────────────────────────── |
|
CREATE TABLE IF NOT EXISTS motion_yrkanden ( |
|
-- Key: "{dok_id}:{ordinal}", ordinal = 0-based position in the forslag array |
|
id TEXT PRIMARY KEY, |
|
dok_id TEXT NOT NULL REFERENCES motions(dok_id) ON DELETE CASCADE, |
|
ordinal INTEGER NOT NULL, |
|
nummer TEXT, -- yrkande number as stated in the motion |
|
lydelse TEXT NOT NULL, |
|
utskottet TEXT, -- committee proposal (e.g. "Avslag") |
|
kammaren TEXT, -- chamber decision (e.g. "Avslag"/"Bifall") |
|
behandlas_i TEXT, -- committee report where handled |
|
embedding vector(384) |
|
); |
|
|
|
CREATE INDEX IF NOT EXISTS motion_yrkanden_dok_idx ON motion_yrkanden (dok_id); |
|
CREATE INDEX IF NOT EXISTS motion_yrkanden_embedding_idx ON motion_yrkanden |
|
USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64); |
|
|
|
-- ── 3. Backfill both from the existing forslag JSONB (no re-parse needed) ─── |
|
INSERT INTO motion_yrkanden (id, dok_id, ordinal, nummer, lydelse, utskottet, kammaren, behandlas_i) |
|
SELECT m.dok_id || ':' || (f.ord - 1), |
|
m.dok_id, |
|
(f.ord - 1)::int, |
|
f.val->>'nummer', |
|
f.val->>'lydelse', |
|
f.val->>'utskottet', |
|
f.val->>'kammaren', |
|
f.val->>'behandlas_i' |
|
FROM motions m, |
|
LATERAL jsonb_array_elements(m.forslag) WITH ORDINALITY AS f(val, ord) |
|
WHERE m.forslag IS NOT NULL |
|
AND jsonb_typeof(m.forslag) = 'array' |
|
AND coalesce(f.val->>'lydelse', '') <> '' |
|
ON CONFLICT (id) DO NOTHING; |
|
|
|
-- forslag_text last: this UPDATE fires the trigger and rebuilds search_vector |
|
-- (now including the yrkanden at weight B) for every existing motion. |
|
UPDATE motions m |
|
SET forslag_text = sub.txt |
|
FROM ( |
|
SELECT dok_id, string_agg(val->>'lydelse', ' ') AS txt |
|
FROM motions, LATERAL jsonb_array_elements(forslag) AS val |
|
WHERE forslag IS NOT NULL |
|
AND jsonb_typeof(forslag) = 'array' |
|
AND coalesce(val->>'lydelse', '') <> '' |
|
GROUP BY dok_id |
|
) sub |
|
WHERE m.dok_id = sub.dok_id |
|
AND m.forslag_text IS DISTINCT FROM sub.txt;
|
|
|