]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/XXXX.schema.metabib-display-field.sql
9d20e593c943702206215ee732682352d7cae4fa
[working/Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / XXXX.schema.metabib-display-field.sql
1
2 BEGIN;
3
4 ALTER TABLE config.metabib_field ADD COLUMN display_xpath TEXT, display_field BOOL NOT NULL DEFAULT TRUE;
5 UPDATE config.metabib_field SET display_field = FALSE WHERE field_class = 'keyword' OR name = 'complete';
6
7 CREATE TABLE metabib.display_entry (
8     id      BIGSERIAL  PRIMARY KEY,
9     source  BIGINT     NOT NULL,
10     field   INT        NOT NULL,
11     value   TEXT       NOT NULL
12 );
13
14 CREATE INDEX metabib_display_entry_field_idx 
15     ON metabib.display_entry (field);
16 CREATE INDEX metabib_display_entry_source_idx 
17     ON metabib.display_entry (source);
18
19 CREATE OR REPLACE FUNCTION metabib.display_field_normalize_trigger () 
20     RETURNS TRIGGER AS $$
21 DECLARE
22     normalizer  RECORD;
23     display_field_text  TEXT;
24 BEGIN
25     display_field_text := NEW.value;
26
27     FOR normalizer IN
28         SELECT  n.func AS func,
29                 n.param_count AS param_count,
30                 m.params AS params
31           FROM  config.index_normalizer n
32                 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
33           WHERE m.field = NEW.field AND m.pos < 0
34           ORDER BY m.pos LOOP
35
36             EXECUTE 'SELECT ' || normalizer.func || '(' ||
37                 quote_literal( display_field_text ) ||
38                 CASE
39                     WHEN normalizer.param_count > 0
40                         THEN ',' || REPLACE(REPLACE(BTRIM(
41                             normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
42                         ELSE ''
43                     END ||
44                 ')' INTO display_field_text;
45
46     END LOOP;
47
48     NEW.value = display_field_text;
49
50     RETURN NEW;
51 END;
52 $$ LANGUAGE PLPGSQL;
53
54 CREATE TRIGGER display_field_normalize_tgr
55         BEFORE UPDATE OR INSERT ON metabib.display_entry
56         FOR EACH ROW EXECUTE PROCEDURE metabib.display_field_normalize_trigger();
57
58 CREATE OR REPLACE FUNCTION evergreen.display_field_force_nfc() 
59     RETURNS TRIGGER AS $$
60 BEGIN
61     NEW.value := force_unicode_normal_form(NEW.value,'NFC');
62     RETURN NEW;
63 END;
64 $$ LANGUAGE PLPGSQL;
65
66 CREATE TRIGGER display_field_force_nfc_tgr
67         BEFORE UPDATE OR INSERT ON metabib.display_entry
68         FOR EACH ROW EXECUTE PROCEDURE evergreen.display_field_force_nfc();
69
70 ALTER TABLE config.metabib_field
71     ADD COLUMN display_field BOOL NOT NULL DEFAULT TRUE;
72
73 ALTER TYPE metabib.field_entry_template ADD ATTRIBUTE display_field BOOL;
74
75 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
76 DECLARE
77     bib     biblio.record_entry%ROWTYPE;
78     idx     config.metabib_field%ROWTYPE;
79     xfrm        config.xml_transform%ROWTYPE;
80     prev_xfrm   TEXT;
81     transformed_xml TEXT;
82     xml_node    TEXT;
83     xml_node_list   TEXT[];
84     facet_text  TEXT;
85     display_text TEXT;
86     browse_text TEXT;
87     sort_value  TEXT;
88     raw_text    TEXT;
89     curr_text   TEXT;
90     joiner      TEXT := default_joiner; -- XXX will index defs supply a joiner?
91     authority_text TEXT;
92     authority_link BIGINT;
93     output_row  metabib.field_entry_template%ROWTYPE;
94 BEGIN
95
96     -- Start out with no field-use bools set
97     output_row.browse_field = FALSE;
98     output_row.facet_field = FALSE;
99     output_row.display_field = FALSE;
100     output_row.search_field = FALSE;
101
102     -- Get the record
103     SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
104
105     -- Loop over the indexing entries
106     FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
107
108         joiner := COALESCE(idx.joiner, default_joiner);
109
110         SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
111
112         -- See if we can skip the XSLT ... it's expensive
113         IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
114             -- Can't skip the transform
115             IF xfrm.xslt <> '---' THEN
116                 transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
117             ELSE
118                 transformed_xml := bib.marc;
119             END IF;
120
121             prev_xfrm := xfrm.name;
122         END IF;
123
124         xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
125
126         raw_text := NULL;
127         FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
128             CONTINUE WHEN xml_node !~ E'^\\s*<';
129
130             -- XXX much of this should be moved into oils_xpath_string...
131             curr_text := ARRAY_TO_STRING(evergreen.array_remove_item_by_value(evergreen.array_remove_item_by_value(
132                 oils_xpath( '//text()',
133                     REGEXP_REPLACE(
134                         REGEXP_REPLACE( -- This escapes all &s not followed by "amp;".  Data ise returned from oils_xpath (above) in UTF-8, not entity encoded
135                             REGEXP_REPLACE( -- This escapes embeded <s
136                                 xml_node,
137                                 $re$(>[^<]+)(<)([^>]+<)$re$,
138                                 E'\\1&lt;\\3',
139                                 'g'
140                             ),
141                             '&(?!amp;)',
142                             '&amp;',
143                             'g'
144                         ),
145                         E'\\s+',
146                         ' ',
147                         'g'
148                     )
149                 ), ' '), ''),
150                 joiner
151             );
152
153             CONTINUE WHEN curr_text IS NULL OR curr_text = '';
154
155             IF raw_text IS NOT NULL THEN
156                 raw_text := raw_text || joiner;
157             END IF;
158
159             raw_text := COALESCE(raw_text,'') || curr_text;
160
161             -- autosuggest/metabib.browse_entry
162             IF idx.browse_field THEN
163
164                 IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
165                     browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
166                 ELSE
167                     browse_text := curr_text;
168                 END IF;
169
170                 IF idx.browse_sort_xpath IS NOT NULL AND
171                     idx.browse_sort_xpath <> '' THEN
172
173                     sort_value := oils_xpath_string(
174                         idx.browse_sort_xpath, xml_node, joiner,
175                         ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
176                     );
177                 ELSE
178                     sort_value := browse_text;
179                 END IF;
180
181                 output_row.field_class = idx.field_class;
182                 output_row.field = idx.id;
183                 output_row.source = rid;
184                 output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
185                 output_row.sort_value :=
186                     public.naco_normalize(sort_value);
187
188                 output_row.authority := NULL;
189
190                 IF idx.authority_xpath IS NOT NULL AND idx.authority_xpath <> '' THEN
191                     authority_text := oils_xpath_string(
192                         idx.authority_xpath, xml_node, joiner,
193                         ARRAY[
194                             ARRAY[xfrm.prefix, xfrm.namespace_uri],
195                             ARRAY['xlink','http://www.w3.org/1999/xlink']
196                         ]
197                     );
198
199                     IF authority_text ~ '^\d+$' THEN
200                         authority_link := authority_text::BIGINT;
201                         PERFORM * FROM authority.record_entry WHERE id = authority_link;
202                         IF FOUND THEN
203                             output_row.authority := authority_link;
204                         END IF;
205                     END IF;
206
207                 END IF;
208
209                 output_row.browse_field = TRUE;
210                 -- Returning browse rows with search_field = true for search+browse
211                 -- configs allows us to retain granularity of being able to search
212                 -- browse fields with "starts with" type operators (for example, for
213                 -- titles of songs in music albums)
214                 IF idx.search_field THEN
215                     output_row.search_field = TRUE;
216                 END IF;
217                 RETURN NEXT output_row;
218                 output_row.browse_field = FALSE;
219                 output_row.search_field = FALSE;
220                 output_row.sort_value := NULL;
221             END IF;
222
223             -- insert raw node text for faceting
224             IF idx.facet_field THEN
225
226                 IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
227                     facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
228                 ELSE
229                     facet_text := curr_text;
230                 END IF;
231
232                 output_row.field_class = idx.field_class;
233                 output_row.field = -1 * idx.id;
234                 output_row.source = rid;
235                 output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
236
237                 output_row.facet_field = TRUE;
238                 RETURN NEXT output_row;
239                 output_row.facet_field = FALSE;
240             END IF;
241
242             -- insert raw node text for display
243             IF idx.display_field THEN
244
245                 IF idx.display_xpath IS NOT NULL AND idx.display_xpath <> '' THEN
246                     display_text := oils_xpath_string( idx.display_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
247                 ELSE
248                     display_text := curr_text;
249                 END IF;
250
251                 output_row.field_class = idx.field_class;
252                 output_row.field = -1 * idx.id;
253                 output_row.source = rid;
254                 output_row.value = BTRIM(REGEXP_REPLACE(display_text, E'\\s+', ' ', 'g'));
255
256                 output_row.display_field = TRUE;
257                 RETURN NEXT output_row;
258                 output_row.display_field = FALSE;
259             END IF;
260
261         END LOOP;
262
263         CONTINUE WHEN raw_text IS NULL OR raw_text = '';
264
265         -- insert combined node text for searching
266         IF idx.search_field THEN
267             output_row.field_class = idx.field_class;
268             output_row.field = idx.id;
269             output_row.source = rid;
270             output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
271
272             output_row.search_field = TRUE;
273             RETURN NEXT output_row;
274             output_row.search_field = FALSE;
275         END IF;
276
277     END LOOP;
278
279 END;
280
281 $func$ LANGUAGE PLPGSQL;
282
283 DROP FUNCTION metabib.reingest_metabib_field_entries(BIGINT, BOOL, BOOL, BOOL);
284
285 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( 
286     bib_id BIGINT, skip_facet BOOL DEFAULT FALSE, 
287     skip_display BOOL DEFAULT FALSE, skip_browse BOOL DEFAULT FALSE, 
288     skip_search BOOL DEFAULT FALSE ) RETURNS VOID AS $func$
289 DECLARE
290     fclass          RECORD;
291     ind_data        metabib.field_entry_template%ROWTYPE;
292     mbe_row         metabib.browse_entry%ROWTYPE;
293     mbe_id          BIGINT;
294     b_skip_facet    BOOL;
295     b_skip_display    BOOL;
296     b_skip_browse   BOOL;
297     b_skip_search   BOOL;
298     value_prepped   TEXT;
299 BEGIN
300
301     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;
302     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;
303     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;
304     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;
305
306     PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
307     IF NOT FOUND THEN
308         IF NOT b_skip_search THEN
309             FOR fclass IN SELECT * FROM config.metabib_class LOOP
310                 -- RAISE NOTICE 'Emptying out %', fclass.name;
311                 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
312             END LOOP;
313         END IF;
314         IF NOT b_skip_facet THEN
315             DELETE FROM metabib.facet_entry WHERE source = bib_id;
316         END IF;
317         IF NOT b_skip_display THEN
318             DELETE FROM metabib.display_entry WHERE source = bib_id;
319         END IF;
320         IF NOT b_skip_browse THEN
321             DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
322         END IF;
323     END IF;
324
325     FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
326         IF ind_data.field < 0 THEN
327             ind_data.field = -1 * ind_data.field;
328         END IF;
329
330         IF ind_data.facet_field AND NOT b_skip_facet THEN
331             INSERT INTO metabib.facet_entry (field, source, value)
332                 VALUES (ind_data.field, ind_data.source, ind_data.value);
333         END IF;
334
335         IF ind_data.display_field AND NOT b_skip_display THEN
336             INSERT INTO metabib.display_entry (field, source, value)
337                 VALUES (ind_data.field, ind_data.source, ind_data.value);
338         END IF;
339
340
341         IF ind_data.browse_field AND NOT b_skip_browse THEN
342             -- A caveat about this SELECT: this should take care of replacing
343             -- old mbe rows when data changes, but not if normalization (by
344             -- which I mean specifically the output of
345             -- evergreen.oils_tsearch2()) changes.  It may or may not be
346             -- expensive to add a comparison of index_vector to index_vector
347             -- to the WHERE clause below.
348
349             value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field);
350             SELECT INTO mbe_row * FROM metabib.browse_entry
351                 WHERE value = value_prepped AND sort_value = ind_data.sort_value;
352
353             IF FOUND THEN
354                 mbe_id := mbe_row.id;
355             ELSE
356                 INSERT INTO metabib.browse_entry
357                     ( value, sort_value ) VALUES
358                     ( value_prepped, ind_data.sort_value );
359
360                 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
361             END IF;
362
363             INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority)
364                 VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority);
365         END IF;
366
367         IF ind_data.search_field AND NOT b_skip_search THEN
368             -- Avoid inserting duplicate rows
369             EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class ||
370                 '_field_entry WHERE field = $1 AND source = $2 AND value = $3'
371                 INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value;
372                 -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id;
373             IF mbe_id IS NULL THEN
374                 EXECUTE $$
375                 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
376                     VALUES ($$ ||
377                         quote_literal(ind_data.field) || $$, $$ ||
378                         quote_literal(ind_data.source) || $$, $$ ||
379                         quote_literal(ind_data.value) ||
380                     $$);$$;
381             END IF;
382         END IF;
383
384     END LOOP;
385
386     IF NOT b_skip_search THEN
387         PERFORM metabib.update_combined_index_vectors(bib_id);
388     END IF;
389
390     RETURN;
391 END;
392 $func$ LANGUAGE PLPGSQL;
393
394
395 -- DATA -------------------
396
397 -- "General Keywords" and "All Subjects"
398 -- more?
399 UPDATE config.metabib_field SET display_field = FALSE WHERE id IN (15, 16);
400
401 INSERT INTO config.internal_flag (name, enabled) 
402     VALUES ('ingest.skip_display_indexing', FALSE);
403
404 -- TODO: targeted ingest?
405
406 -- Dumb Reingest ---
407 --UPDATE config.internal_flag SET enabled = TRUE 
408 --    WHERE name = 'ingest.reingest.force_on_same_marc';
409 --UPDATE biblio.record_entry SET marc = marc;
410 --UPDATE config.internal_flag SET enabled = FALSE
411 --    WHERE name = 'ingest.reingest.force_on_same_marc';
412
413 COMMIT;
414 --ROLLBACK;