]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/WWWW.schema.dym_update_and_reify.sql
LP#1931737: Allow the delay of symspell updates
[Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / WWWW.schema.dym_update_and_reify.sql
1 BEGIN;
2
3 -- INSERT-only table that catches dictionary updates to be reconciled
4 CREATE UNLOGGED TABLE search.symspell_dictionary_updates (
5     transaction_id          BIGINT,
6     keyword_count           INT     NOT NULL DEFAULT 0,
7     title_count             INT     NOT NULL DEFAULT 0,
8     author_count            INT     NOT NULL DEFAULT 0,
9     subject_count           INT     NOT NULL DEFAULT 0,
10     series_count            INT     NOT NULL DEFAULT 0,
11     identifier_count        INT     NOT NULL DEFAULT 0,
12
13     prefix_key              TEXT    NOT NULL,
14
15     keyword_suggestions     TEXT[],
16     title_suggestions       TEXT[],
17     author_suggestions      TEXT[],
18     subject_suggestions     TEXT[],
19     series_suggestions      TEXT[],
20     identifier_suggestions  TEXT[]
21 );
22 CREATE INDEX symspell_dictionary_updates_tid_idx ON search.symspell_dictionary_updates (transaction_id);
23
24 -- Function that collects this transactions additions to the unlogged update table
25 CREATE OR REPLACE FUNCTION search.symspell_dictionary_reify () RETURNS SETOF search.symspell_dictionary AS $f$
26  WITH new_rows AS (
27     DELETE FROM search.symspell_dictionary_updates WHERE transaction_id = txid_current() RETURNING *
28  ), computed_rows AS ( -- this collapses the rows deleted into the format we need for UPSERT
29     SELECT  SUM(keyword_count)    AS keyword_count,
30             SUM(title_count)      AS title_count,
31             SUM(author_count)     AS author_count,
32             SUM(subject_count)    AS subject_count,
33             SUM(series_count)     AS series_count,
34             SUM(identifier_count) AS identifier_count,
35
36             prefix_key,
37
38             ARRAY_REMOVE(ARRAY_AGG(DISTINCT keyword_suggestions[1]), NULL)    AS keyword_suggestions,
39             ARRAY_REMOVE(ARRAY_AGG(DISTINCT title_suggestions[1]), NULL)      AS title_suggestions,
40             ARRAY_REMOVE(ARRAY_AGG(DISTINCT author_suggestions[1]), NULL)     AS author_suggestions,
41             ARRAY_REMOVE(ARRAY_AGG(DISTINCT subject_suggestions[1]), NULL)    AS subject_suggestions,
42             ARRAY_REMOVE(ARRAY_AGG(DISTINCT series_suggestions[1]), NULL)     AS series_suggestions,
43             ARRAY_REMOVE(ARRAY_AGG(DISTINCT identifier_suggestions[1]), NULL) AS identifier_suggestions
44       FROM  new_rows
45       GROUP BY prefix_key
46  )
47  INSERT INTO search.symspell_dictionary AS d SELECT * FROM computed_rows
48  ON CONFLICT (prefix_key) DO UPDATE SET
49     keyword_count = GREATEST(0, d.keyword_count + EXCLUDED.keyword_count),
50     keyword_suggestions = evergreen.text_array_merge_unique(EXCLUDED.keyword_suggestions,d.keyword_suggestions),
51
52     title_count = GREATEST(0, d.title_count + EXCLUDED.title_count),
53     title_suggestions = evergreen.text_array_merge_unique(EXCLUDED.title_suggestions,d.title_suggestions),
54
55     author_count = GREATEST(0, d.author_count + EXCLUDED.author_count),
56     author_suggestions = evergreen.text_array_merge_unique(EXCLUDED.author_suggestions,d.author_suggestions),
57
58     subject_count = GREATEST(0, d.subject_count + EXCLUDED.subject_count),
59     subject_suggestions = evergreen.text_array_merge_unique(EXCLUDED.subject_suggestions,d.subject_suggestions),
60
61     series_count = GREATEST(0, d.series_count + EXCLUDED.series_count),
62     series_suggestions = evergreen.text_array_merge_unique(EXCLUDED.series_suggestions,d.series_suggestions),
63
64     identifier_count = GREATEST(0, d.identifier_count + EXCLUDED.identifier_count),
65     identifier_suggestions = evergreen.text_array_merge_unique(EXCLUDED.identifier_suggestions,d.identifier_suggestions)
66  RETURNING *;
67 $f$ LANGUAGE SQL;
68
69 -- simplified metabib.*_field_entry trigger that stages updates for reification in one go
70 CREATE OR REPLACE FUNCTION search.symspell_maintain_entries () RETURNS TRIGGER AS $f$
71 DECLARE
72     search_class    TEXT;
73     new_value       TEXT := NULL;
74     old_value       TEXT := NULL;
75 BEGIN
76     search_class := COALESCE(TG_ARGV[0], SPLIT_PART(TG_TABLE_NAME,'_',1));
77
78     IF TG_OP IN ('INSERT', 'UPDATE') THEN
79         new_value := NEW.value;
80     END IF;
81
82     IF TG_OP IN ('DELETE', 'UPDATE') THEN
83         old_value := OLD.value;
84     END IF;
85
86     IF new_value = old_value THEN
87         -- same, move along
88     ELSE
89         INSERT INTO search.symspell_dictionary_updates
90             SELECT  txid_current(), *
91               FROM  search.symspell_build_entries(
92                         new_value,
93                         search_class,
94                         old_value
95                     );
96     END IF;
97
98     RETURN NULL; -- always fired AFTER
99 END;
100 $f$ LANGUAGE PLPGSQL;
101
102 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries(
103     bib_id BIGINT,
104     skip_facet BOOL DEFAULT FALSE,
105     skip_display BOOL DEFAULT FALSE,
106     skip_browse BOOL DEFAULT FALSE,
107     skip_search BOOL DEFAULT FALSE,
108     only_fields INT[] DEFAULT '{}'::INT[]
109 ) RETURNS VOID AS $func$
110 DECLARE
111     fclass          RECORD;
112     ind_data        metabib.field_entry_template%ROWTYPE;
113     mbe_row         metabib.browse_entry%ROWTYPE;
114     mbe_id          BIGINT;
115     b_skip_facet    BOOL;
116     b_skip_display    BOOL;
117     b_skip_browse   BOOL;
118     b_skip_search   BOOL;
119     value_prepped   TEXT;
120     field_list      INT[] := only_fields;
121     field_types     TEXT[] := '{}'::TEXT[];
122 BEGIN
123
124     IF field_list = '{}'::INT[] THEN
125         SELECT ARRAY_AGG(id) INTO field_list FROM config.metabib_field;
126     END IF;
127
128     SELECT COALESCE(NULLIF(skip_facet, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_facet_indexing' AND enabled)) INTO b_skip_facet;
129     SELECT COALESCE(NULLIF(skip_display, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_display_indexing' AND enabled)) INTO b_skip_display;
130     SELECT COALESCE(NULLIF(skip_browse, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_browse_indexing' AND enabled)) INTO b_skip_browse;
131     SELECT COALESCE(NULLIF(skip_search, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_search_indexing' AND enabled)) INTO b_skip_search;
132
133     IF NOT b_skip_facet THEN field_types := field_types || '{facet}'; END IF;
134     IF NOT b_skip_display THEN field_types := field_types || '{display}'; END IF;
135     IF NOT b_skip_browse THEN field_types := field_types || '{browse}'; END IF;
136     IF NOT b_skip_search THEN field_types := field_types || '{search}'; END IF;
137
138     PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
139     IF NOT FOUND THEN
140         IF NOT b_skip_search THEN
141             FOR fclass IN SELECT * FROM config.metabib_class LOOP
142                 -- RAISE NOTICE 'Emptying out %', fclass.name;
143                 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
144             END LOOP;
145         END IF;
146         IF NOT b_skip_facet THEN
147             DELETE FROM metabib.facet_entry WHERE source = bib_id;
148         END IF;
149         IF NOT b_skip_display THEN
150             DELETE FROM metabib.display_entry WHERE source = bib_id;
151         END IF;
152         IF NOT b_skip_browse THEN
153             DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
154         END IF;
155     END IF;
156
157     FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id, ' ', field_types, field_list ) LOOP
158
159     -- don't store what has been normalized away
160         CONTINUE WHEN ind_data.value IS NULL;
161
162         IF ind_data.field < 0 THEN
163             ind_data.field = -1 * ind_data.field;
164         END IF;
165
166         IF ind_data.facet_field AND NOT b_skip_facet THEN
167             INSERT INTO metabib.facet_entry (field, source, value)
168                 VALUES (ind_data.field, ind_data.source, ind_data.value);
169         END IF;
170
171         IF ind_data.display_field AND NOT b_skip_display THEN
172             INSERT INTO metabib.display_entry (field, source, value)
173                 VALUES (ind_data.field, ind_data.source, ind_data.value);
174         END IF;
175
176
177         IF ind_data.browse_field AND NOT b_skip_browse THEN
178             -- A caveat about this SELECT: this should take care of replacing
179             -- old mbe rows when data changes, but not if normalization (by
180             -- which I mean specifically the output of
181             -- evergreen.oils_tsearch2()) changes.  It may or may not be
182             -- expensive to add a comparison of index_vector to index_vector
183             -- to the WHERE clause below.
184
185             CONTINUE WHEN ind_data.sort_value IS NULL;
186
187             value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field);
188             IF ind_data.browse_nocase THEN
189                 SELECT INTO mbe_row * FROM metabib.browse_entry
190                     WHERE evergreen.lowercase(value) = evergreen.lowercase(value_prepped) AND sort_value = ind_data.sort_value
191                     ORDER BY sort_value, value LIMIT 1; -- gotta pick something, I guess
192             ELSE
193                 SELECT INTO mbe_row * FROM metabib.browse_entry
194                     WHERE value = value_prepped AND sort_value = ind_data.sort_value;
195             END IF;
196
197             IF FOUND THEN
198                 mbe_id := mbe_row.id;
199             ELSE
200                 INSERT INTO metabib.browse_entry
201                     ( value, sort_value ) VALUES
202                     ( value_prepped, ind_data.sort_value );
203
204                 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
205             END IF;
206
207             INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority)
208                 VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority);
209         END IF;
210
211         IF ind_data.search_field AND NOT b_skip_search THEN
212             -- Avoid inserting duplicate rows
213             EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class ||
214                 '_field_entry WHERE field = $1 AND source = $2 AND value = $3'
215                 INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value;
216                 -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id;
217             IF mbe_id IS NULL THEN
218                 EXECUTE $$
219                 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
220                     VALUES ($$ ||
221                         quote_literal(ind_data.field) || $$, $$ ||
222                         quote_literal(ind_data.source) || $$, $$ ||
223                         quote_literal(ind_data.value) ||
224                     $$);$$;
225             END IF;
226         END IF;
227
228     END LOOP;
229
230     IF NOT b_skip_search THEN
231         PERFORM metabib.update_combined_index_vectors(bib_id);
232         PERFORM search.symspell_dictionary_reify();
233     END IF;
234
235     RETURN;
236 END;
237 $func$ LANGUAGE PLPGSQL;
238
239 COMMIT;
240