Record simple authority headings for search and sort, and respect Non-filing Indicato...
authorMike Rylander <mrylander@gmail.com>
Wed, 21 Sep 2011 20:38:08 +0000 (16:38 -0400)
committerMike Rylander <mrylander@gmail.com>
Fri, 23 Sep 2011 19:59:12 +0000 (15:59 -0400)
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Open-ILS/src/sql/Pg/011.schema.authority.sql
Open-ILS/src/sql/Pg/999.functions.global.sql

index 329f356..8dafc25 100644 (file)
@@ -33,6 +33,7 @@ CREATE TABLE authority.control_set_authority_field (
     main_entry  INT     REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     control_set INT     NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     tag         CHAR(3) NOT NULL,
+    nfi         CHAR(1),          -- non-filing indicator
     sf_list     TEXT    NOT NULL,
     name        TEXT    NOT NULL, -- i18n
     description TEXT              -- i18n
@@ -149,27 +150,58 @@ CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaur
 DECLARE
     acsaf           authority.control_set_authority_field%ROWTYPE;
     tag_used        TEXT;
+    nfi_used        TEXT;
     sf              TEXT;
     thes_code       TEXT;
     cset            INT;
     heading_text    TEXT;
     tmp_text        TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
 BEGIN
-    thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
-    IF thes_code IS NULL THEN
-        thes_code := '|';
+    SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    IF cset IS NULL THEN
+        SELECT  control_set INTO cset
+          FROM  authority.control_set_authority_field
+          WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
+          LIMIT 1;
     END IF;
 
-    SELECT control_set INTO cset FROM authority.thesaurus WHERE code = thes_code;
-    IF NOT FOUND THEN
-        cset = 1;
+    IF thes_code = 'z' THEN
+        thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
     END IF;
 
     heading_text := '';
     FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
         tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+        first_sf := TRUE;
         FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
             tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
+
+            IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
+
+                tmp_text := SUBSTRING(
+                    tmp_text FROM
+                    COALESCE(
+                        NULLIF(
+                            REGEXP_REPLACE(
+                                oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
+                                $$\D+$$,
+                                '',
+                                'g'
+                            ),
+                            ''
+                        )::INT,
+                        0
+                    ) + 1
+                );
+
+            END IF;
+
+            first_sf := FALSE;
+
             IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
                 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
             END IF;
@@ -177,15 +209,11 @@ BEGIN
         EXIT WHEN heading_text <> '';
     END LOOP;
 
-    IF thes_code = 'z' THEN
-        thes_code := oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml);
-    END IF;
-
     IF heading_text <> '' THEN
         IF no_thesaurus IS TRUE THEN
             heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
         ELSE
-            heading_text := tag_used || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
+            heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
         END IF;
     ELSE
         heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
@@ -195,6 +223,88 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL IMMUTABLE;
 
+CREATE TABLE authority.simple_heading (
+    id              BIGSERIAL   PRIMARY KEY,
+    record          BIGINT      NOT NULL REFERENCES authority.record_entry (id),
+    atag            INT         NOT NULL REFERENCES authority.control_set_authority_field (id),
+    value           TEXT        NOT NULL,
+    index_vector    tsvector    NOT NULL
+);
+CREATE TRIGGER authority_simple_heading_fti_trigger
+    BEFORE UPDATE OR INSERT ON authority.simple_heading
+    FOR EACH ROW EXECUTE PROCEDURE tsearch2(index_vector, value);
+
+CREATE INDEX authority_simple_heading_index_vector_idx ON authority.full_rec USING GIST (index_vector);
+CREATE INDEX authority_simple_heading_value_idx ON authority.simple_heading (value);
+
+CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
+DECLARE
+    res             authority.simple_heading%ROWTYPE;
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    nfi_used        TEXT;
+    sf              TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    tmp_text        TEXT;
+    tmp_xml         TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
+BEGIN
+
+    res.record := auth_id;
+
+    SELECT  control_set INTO cset
+      FROM  authority.control_set_authority_field
+      WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]) )
+      LIMIT 1;
+
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
+
+        res.atag := acsaf.id;
+        tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+
+        FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)) LOOP
+            heading_text := '';
+
+            FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
+                heading_text := heading_text || COALESCE( ' ' || oils_xpath_string('//*[@code="'||sf||'"]',tmp_xml::TEXT), '');
+            END LOOP;
+            
+            IF nfi_used IS NOT NULL THEN
+
+                heading_text := SUBSTRING(
+                    heading_text FROM
+                    COALESCE(
+                        NULLIF(
+                            REGEXP_REPLACE(
+                                oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
+                                $$\D+$$,
+                                '',
+                                'g'
+                            ),
+                            ''
+                        )::INT,
+                        0
+                    ) + 1
+                );
+
+            END IF;
+
+            IF heading_text IS NOT NULL AND heading_text <> '' THEN
+                res.value := public.naco_normalize( BTRIM(heading_text) );
+                RETURN NEXT res;
+            END IF;
+
+        END LOOP;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
     SELECT authority.normalize_heading($1, TRUE);
 $func$ LANGUAGE SQL IMMUTABLE;
@@ -210,6 +320,7 @@ index to defend against duplicated authority records from the same
 thesaurus.
 $$;
 
+
 -- Adding indexes using oils_xpath_string() for the main entry tags described in
 -- authority.control_set_authority_field would speed this up, if we ever want to use it, though
 -- the existing index on authority.normalize_heading() helps already with a record in hand
index 25dfb6a..3fedcf8 100644 (file)
@@ -1421,6 +1421,7 @@ BEGIN
     IF NEW.deleted IS TRUE THEN -- If this authority is deleted
         DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
         DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
+        DELETE FROM authority.simple_heading WHERE record = NEW.id;
           -- Should remove matching $0 from controlled fields at the same time?
         RETURN NEW; -- and we're done
     END IF;
@@ -1431,10 +1432,16 @@ BEGIN
         IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
             RETURN NEW;
         END IF;
+
         -- Propagate these updates to any linked bib records
         PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
+
+        DELETE FROM authority.simple_heading WHERE record = NEW.id;
     END IF;
 
+    INSERT INTO authority.simple_heading (record,atag,value)
+        SELECT record, atag, value FROM authority.simple_heading_set(NEW.marc);
+
     -- Flatten and insert the afr data
     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
     IF NOT FOUND THEN