]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/XXXX.functions.metarecord-deleted-constituents.sql
7b9800934010f28d32bd0448f228eb2b97dafe59
[working/Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / XXXX.functions.metarecord-deleted-constituents.sql
1
2 CREATE OR REPLACE FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT, bib_is_deleted BOOL DEFAULT FALSE, retain_deleted BOOL DEFAULT FALSE ) RETURNS BIGINT AS $func$
3 DECLARE
4     new_mapping     BOOL := TRUE;
5     source_count    INT;
6     old_mr          BIGINT;
7     tmp_mr          metabib.metarecord%ROWTYPE;
8     deleted_mrs     BIGINT[];
9 BEGIN
10
11     -- We need to make sure we're not a deleted master record of an MR
12     IF bib_is_deleted THEN
13         FOR old_mr IN SELECT id FROM metabib.metarecord WHERE master_record = bib_id LOOP
14
15             IF NOT retain_deleted THEN -- Go away for any MR that we're master of, unless retained
16                 DELETE FROM metabib.metarecord_source_map WHERE source = bib_id;
17             END IF;
18
19             -- Now, are there any more sources on this MR?
20             SELECT COUNT(*) INTO source_count FROM metabib.metarecord_source_map WHERE metarecord = old_mr;
21
22             IF source_count = 0 AND NOT retain_deleted THEN -- No other records
23                 deleted_mrs := ARRAY_APPEND(deleted_mrs, old_mr); -- Just in case...
24                 DELETE FROM metabib.metarecord WHERE id = old_mr;
25
26             ELSE -- indeed there are. Update it with a null cache and recalcualated master record
27                 UPDATE  metabib.metarecord
28                   SET   mods = NULL,
29                         master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
30                   WHERE id = old_mr;
31             END IF;
32         END LOOP;
33
34     ELSE -- insert or update
35
36         FOR tmp_mr IN SELECT m.* FROM metabib.metarecord m JOIN metabib.metarecord_source_map s ON (s.metarecord = m.id) WHERE s.source = bib_id LOOP
37
38             -- Find the first fingerprint-matching
39             IF old_mr IS NULL AND fp = tmp_mr.fingerprint THEN
40                 old_mr := tmp_mr.id;
41                 new_mapping := FALSE;
42
43             ELSE -- Our fingerprint changed ... maybe remove the old MR
44                 DELETE FROM metabib.metarecord_source_map WHERE metarecord = old_mr AND source = bib_id; -- remove the old source mapping
45                 SELECT COUNT(*) INTO source_count FROM metabib.metarecord_source_map WHERE metarecord = tmp_mr.id;
46                 IF source_count = 0 THEN -- No other records
47                     deleted_mrs := ARRAY_APPEND(deleted_mrs, tmp_mr.id);
48                     DELETE FROM metabib.metarecord WHERE id = tmp_mr.id;
49                 END IF;
50             END IF;
51
52         END LOOP;
53
54         -- we found no suitable, preexisting MR based on old source maps
55         IF old_mr IS NULL THEN
56             SELECT id INTO old_mr FROM metabib.metarecord WHERE fingerprint = fp; -- is there one for our current fingerprint?
57
58             IF old_mr IS NULL THEN -- nope, create one and grab its id
59                 INSERT INTO metabib.metarecord ( fingerprint, master_record ) VALUES ( fp, bib_id );
60                 SELECT id INTO old_mr FROM metabib.metarecord WHERE fingerprint = fp;
61
62             ELSE -- indeed there is. update it with a null cache and recalcualated master record
63                 UPDATE  metabib.metarecord
64                   SET   mods = NULL,
65                         master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
66                   WHERE id = old_mr;
67             END IF;
68
69         ELSE -- there was one we already attached to, update its mods cache and master_record
70             UPDATE  metabib.metarecord
71               SET   mods = NULL,
72                     master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
73               WHERE id = old_mr;
74         END IF;
75
76         IF new_mapping THEN
77             INSERT INTO metabib.metarecord_source_map (metarecord, source) VALUES (old_mr, bib_id); -- new source mapping
78         END IF;
79
80     END IF;
81
82     IF ARRAY_UPPER(deleted_mrs,1) > 0 THEN
83         UPDATE action.hold_request SET target = old_mr WHERE target IN ( SELECT unnest(deleted_mrs) ) AND hold_type = 'M'; -- if we had to delete any MRs above, make sure their holds are moved
84     END IF;
85
86     RETURN old_mr;
87
88 END;
89 $func$ LANGUAGE PLPGSQL;
90
91 DROP FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT );
92
93 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
94 DECLARE
95     tmp_bool BOOL;
96 BEGIN
97
98     IF NEW.deleted THEN -- If this bib is deleted
99
100         PERFORM * FROM config.internal_flag WHERE
101             name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled;
102
103         tmp_bool := FOUND; -- Just in case this is changed by some other statement
104
105         PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint, TRUE, tmp_bool );
106
107         IF NOT tmp_bool THEN
108             -- One needs to keep these around to support searches
109             -- with the #deleted modifier, so one should turn on the named
110             -- internal flag for that functionality.
111             DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id;
112         END IF;
113
114         DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
115         DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
116         DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
117         RETURN NEW; -- and we're done
118     END IF;
119
120     IF TG_OP = 'UPDATE' THEN -- re-ingest?
121         PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
122
123         IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
124             RETURN NEW;
125         END IF;
126     END IF;
127
128     -- Record authority linking
129     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
130     IF NOT FOUND THEN
131         PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
132     END IF;
133
134     -- Flatten and insert the mfr data
135     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
136     IF NOT FOUND THEN
137         PERFORM metabib.reingest_metabib_full_rec(NEW.id);
138
139         -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
140         PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
141         IF NOT FOUND THEN
142             PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted);
143         END IF;
144     END IF;
145
146     -- Gather and insert the field entry data
147     PERFORM metabib.reingest_metabib_field_entries(NEW.id);
148
149     -- Located URI magic
150     IF TG_OP = 'INSERT' THEN
151         PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
152         IF NOT FOUND THEN
153             PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
154         END IF;
155     ELSE
156         PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
157         IF NOT FOUND THEN
158             PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
159         END IF;
160     END IF;
161
162     -- (re)map metarecord-bib linking
163     IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
164         PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
165         IF NOT FOUND THEN
166             PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
167         END IF;
168     ELSE -- we're doing an update, and we're not deleted, remap
169         PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
170         IF NOT FOUND THEN
171             PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
172         END IF;
173     END IF;
174
175     RETURN NEW;
176 END;
177 $func$ LANGUAGE PLPGSQL;
178
179 CREATE OR REPLACE FUNCTION unapi.mmr (
180     obj_id BIGINT,
181     format TEXT,
182     ename TEXT,
183     includes TEXT[],
184     org TEXT,
185     depth INT DEFAULT NULL,
186     slimit HSTORE DEFAULT NULL,
187     soffset HSTORE DEFAULT NULL,
188     include_xmlns BOOL DEFAULT TRUE,
189     pref_lib INT DEFAULT NULL
190 )
191 RETURNS XML AS $F$
192 DECLARE
193     mmrec   metabib.metarecord%ROWTYPE;
194     leadrec biblio.record_entry%ROWTYPE;
195     subrec biblio.record_entry%ROWTYPE;
196     layout  unapi.bre_output_layout%ROWTYPE;
197     xfrm    config.xml_transform%ROWTYPE;
198     ouid    INT;
199     xml_buf TEXT; -- growing XML document
200     tmp_xml TEXT; -- single-use XML string
201     xml_frag TEXT; -- single-use XML fragment
202     top_el  TEXT;
203     output  XML;
204     hxml    XML;
205     axml    XML;
206     subxml  XML; -- subordinate records elements
207     sub_xpath TEXT; 
208     parts   TEXT[]; 
209 BEGIN
210
211     -- xpath for extracting bre.marc values from subordinate records 
212     -- so they may be appended to the MARC of the master record prior
213     -- to XSLT processing.
214     -- subjects, isbn, issn, upc -- anything else?
215     sub_xpath := 
216       '//*[starts-with(@tag, "6") or @tag="020" or @tag="022" or @tag="024"]';
217
218     IF org = '-' OR org IS NULL THEN
219         SELECT shortname INTO org FROM evergreen.org_top();
220     END IF;
221
222     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
223
224     IF ouid IS NULL THEN
225         RETURN NULL::XML;
226     END IF;
227
228     SELECT INTO mmrec * FROM metabib.metarecord WHERE id = obj_id;
229     IF NOT FOUND THEN
230         RETURN NULL::XML;
231     END IF;
232
233     -- TODO: aggregate holdings from constituent records
234     IF format = 'holdings_xml' THEN -- the special case
235         output := unapi.mmr_holdings_xml(
236             obj_id, ouid, org, depth,
237             evergreen.array_remove_item_by_value(includes,'holdings_xml'),
238             slimit, soffset, include_xmlns, pref_lib);
239         RETURN output;
240     END IF;
241
242     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
243
244     IF layout.name IS NULL THEN
245         RETURN NULL::XML;
246     END IF;
247
248     SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
249
250     SELECT INTO leadrec * FROM biblio.record_entry WHERE id = mmrec.master_record;
251
252     -- Grab distinct MVF for all records if requested
253     IF ('mra' = ANY (includes)) THEN 
254         axml := unapi.mmr_mra(obj_id,NULL,NULL,NULL,org,depth,NULL,NULL,TRUE,pref_lib);
255     ELSE
256         axml := NULL::XML;
257     END IF;
258
259     xml_buf = leadrec.marc;
260
261     hxml := NULL::XML;
262     IF ('holdings_xml' = ANY (includes)) THEN
263         hxml := unapi.mmr_holdings_xml(
264                     obj_id, ouid, org, depth,
265                     evergreen.array_remove_item_by_value(includes,'holdings_xml'),
266                     slimit, soffset, include_xmlns, pref_lib);
267     END IF;
268
269     subxml := NULL::XML;
270     parts := '{}'::TEXT[];
271     FOR subrec IN SELECT bre.* FROM biblio.record_entry bre
272          JOIN metabib.metarecord_source_map mmsm ON (mmsm.source = bre.id)
273          JOIN metabib.metarecord mmr ON (mmr.id = mmsm.metarecord)
274          WHERE mmr.id = obj_id AND NOT bre.deleted
275          ORDER BY CASE WHEN bre.id = mmr.master_record THEN 0 ELSE bre.id END
276          LIMIT COALESCE((slimit->'bre')::INT, 5) LOOP
277
278         IF subrec.id = leadrec.id THEN CONTINUE; END IF;
279         -- Append choice data from the the non-lead records to the 
280         -- the lead record document
281
282         parts := parts || xpath(sub_xpath, subrec.marc::XML)::TEXT[];
283     END LOOP;
284
285     SELECT ARRAY_TO_STRING( ARRAY_AGG( DISTINCT p ), '' )::XML INTO subxml FROM UNNEST(parts) p;
286
287     -- append data from the subordinate records to the 
288     -- main record document before applying the XSLT
289
290     IF subxml IS NOT NULL THEN 
291         xml_buf := REGEXP_REPLACE(xml_buf, 
292             '</record>(.*?)$', subxml || '</record>' || E'\\1');
293     END IF;
294
295     IF format = 'marcxml' THEN
296          -- If we're not using the prefixed namespace in 
297          -- this record, then remove all declarations of it
298         IF xml_buf !~ E'<marc:' THEN
299            xml_buf := REGEXP_REPLACE(xml_buf, 
300             ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
301         END IF; 
302     ELSE
303         xml_buf := oils_xslt_process(xml_buf, xfrm.xslt)::XML;
304     END IF;
305
306     -- update top_el to reflect the change in xml_buf, which may
307     -- now be a different type of document (e.g. record -> mods)
308     top_el := REGEXP_REPLACE(xml_buf, E'^.*?<((?:\\S+:)?' || 
309         layout.holdings_element || ').*$', E'\\1');
310
311     IF axml IS NOT NULL THEN 
312         xml_buf := REGEXP_REPLACE(xml_buf, 
313             '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
314     END IF;
315
316     IF hxml IS NOT NULL THEN
317         xml_buf := REGEXP_REPLACE(xml_buf, 
318             '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
319     END IF;
320
321     IF ('mmr.unapi' = ANY (includes)) THEN 
322         output := REGEXP_REPLACE(
323             xml_buf,
324             '</' || top_el || '>(.*?)',
325             XMLELEMENT(
326                 name abbr,
327                 XMLATTRIBUTES(
328                     'http://www.w3.org/1999/xhtml' AS xmlns,
329                     'unapi-id' AS class,
330                     'tag:open-ils.org:U2@mmr/' || obj_id || '/' || org AS title
331                 )
332             )::TEXT || '</' || top_el || E'>\\1'
333         );
334     ELSE
335         output := xml_buf;
336     END IF;
337
338     -- remove ignorable whitesace
339     output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
340     RETURN output;
341 END;
342 $F$ LANGUAGE PLPGSQL STABLE;
343