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$
4 new_mapping BOOL := TRUE;
7 tmp_mr metabib.metarecord%ROWTYPE;
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
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;
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;
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;
26 ELSE -- indeed there are. Update it with a null cache and recalcualated master record
27 UPDATE metabib.metarecord
29 master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
34 ELSE -- insert or update
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
38 -- Find the first fingerprint-matching
39 IF old_mr IS NULL AND fp = tmp_mr.fingerprint THEN
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;
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?
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;
62 ELSE -- indeed there is. update it with a null cache and recalcualated master record
63 UPDATE metabib.metarecord
65 master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
69 ELSE -- there was one we already attached to, update its mods cache and master_record
70 UPDATE metabib.metarecord
72 master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
77 INSERT INTO metabib.metarecord_source_map (metarecord, source) VALUES (old_mr, bib_id); -- new source mapping
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
89 $func$ LANGUAGE PLPGSQL;
91 DROP FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT );
93 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
98 IF NEW.deleted THEN -- If this bib is deleted
100 PERFORM * FROM config.internal_flag WHERE
101 name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled;
103 tmp_bool := FOUND; -- Just in case this is changed by some other statement
105 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint, TRUE, tmp_bool );
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;
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
120 IF TG_OP = 'UPDATE' THEN -- re-ingest?
121 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
123 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
128 -- Record authority linking
129 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
131 PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
134 -- Flatten and insert the mfr data
135 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
137 PERFORM metabib.reingest_metabib_full_rec(NEW.id);
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;
142 PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted);
146 -- Gather and insert the field entry data
147 PERFORM metabib.reingest_metabib_field_entries(NEW.id);
150 IF TG_OP = 'INSERT' THEN
151 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
153 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
156 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
158 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
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;
166 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
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;
171 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
177 $func$ LANGUAGE PLPGSQL;
179 CREATE OR REPLACE FUNCTION unapi.mmr (
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
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;
199 xml_buf TEXT; -- growing XML document
200 tmp_xml TEXT; -- single-use XML string
201 xml_frag TEXT; -- single-use XML fragment
206 subxml XML; -- subordinate records elements
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?
216 '//*[starts-with(@tag, "6") or @tag="020" or @tag="022" or @tag="024"]';
218 IF org = '-' OR org IS NULL THEN
219 SELECT shortname INTO org FROM evergreen.org_top();
222 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
228 SELECT INTO mmrec * FROM metabib.metarecord WHERE id = obj_id;
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);
242 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
244 IF layout.name IS NULL THEN
248 SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
250 SELECT INTO leadrec * FROM biblio.record_entry WHERE id = mmrec.master_record;
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);
259 xml_buf = leadrec.marc;
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);
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
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
282 parts := parts || xpath(sub_xpath, subrec.marc::XML)::TEXT[];
285 SELECT ARRAY_TO_STRING( ARRAY_AGG( DISTINCT p ), '' )::XML INTO subxml FROM UNNEST(parts) p;
287 -- append data from the subordinate records to the
288 -- main record document before applying the XSLT
290 IF subxml IS NOT NULL THEN
291 xml_buf := REGEXP_REPLACE(xml_buf,
292 '</record>(.*?)$', subxml || '</record>' || E'\\1');
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');
303 xml_buf := oils_xslt_process(xml_buf, xfrm.xslt)::XML;
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');
311 IF axml IS NOT NULL THEN
312 xml_buf := REGEXP_REPLACE(xml_buf,
313 '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
316 IF hxml IS NOT NULL THEN
317 xml_buf := REGEXP_REPLACE(xml_buf,
318 '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
321 IF ('mmr.unapi' = ANY (includes)) THEN
322 output := REGEXP_REPLACE(
324 '</' || top_el || '>(.*?)',
328 'http://www.w3.org/1999/xhtml' AS xmlns,
330 'tag:open-ils.org:U2@mmr/' || obj_id || '/' || org AS title
332 )::TEXT || '</' || top_el || E'>\\1'
338 -- remove ignorable whitesace
339 output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
342 $F$ LANGUAGE PLPGSQL STABLE;