From c26de2d0a8ac6f57a3929878027230f6515fcae0 Mon Sep 17 00:00:00 2001 From: Mike Rylander Date: Thu, 1 Dec 2022 17:23:04 -0500 Subject: [PATCH] LP#1998363: Reduce churn caused by bib updates This commit causes reingest of the core search data to check for prexisting values that match exactly after pre-storage normalizers are run. When such rows exist, it is reused and rewritten so that changes to post-storage normalizer configuration will have the intended effects. This will have downstream benefits for the symspell subsystem that supports the Did You Mean functionality, allowing it to avoid reprocessing rows where the data to be processed has not changed in a way material to that logic. Signed-off-by: Mike Rylander --- Open-ILS/src/sql/Pg/030.schema.metabib.sql | 81 +++-- ...unction.metabib_field_entry_churn_reduction.sql | 349 +++++++++++++++++++++ 2 files changed, 397 insertions(+), 33 deletions(-) create mode 100644 Open-ILS/src/sql/Pg/upgrade/XXXX.function.metabib_field_entry_churn_reduction.sql diff --git a/Open-ILS/src/sql/Pg/030.schema.metabib.sql b/Open-ILS/src/sql/Pg/030.schema.metabib.sql index 40cd60ecaa..293324c653 100644 --- a/Open-ILS/src/sql/Pg/030.schema.metabib.sql +++ b/Open-ILS/src/sql/Pg/030.schema.metabib.sql @@ -1047,7 +1047,6 @@ CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( only_fields INT[] DEFAULT '{}'::INT[] ) RETURNS VOID AS $func$ DECLARE - fclass RECORD; ind_data metabib.field_entry_template%ROWTYPE; mbe_row metabib.browse_entry%ROWTYPE; mbe_id BIGINT; @@ -1057,6 +1056,12 @@ DECLARE b_skip_search BOOL; value_prepped TEXT; field_list INT[] := only_fields; + extant_mtfe BIGINT[]; + extant_mafe BIGINT[]; + extant_msfe BIGINT[]; + extant_msefe BIGINT[]; + extant_mife BIGINT[]; + extant_mkfe BIGINT[]; field_types TEXT[] := '{}'::TEXT[]; BEGIN @@ -1064,6 +1069,13 @@ BEGIN SELECT ARRAY_AGG(id) INTO field_list FROM config.metabib_field; END IF; + SELECT ARRAY_AGG(id) INTO extant_mtfe FROM metabib.title_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_mafe FROM metabib.author_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_msfe FROM metabib.subject_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_msefe FROM metabib.series_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_mife FROM metabib.identifier_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_mkfe FROM metabib.keyword_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + 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; 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; 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; @@ -1076,12 +1088,6 @@ BEGIN PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled; IF NOT FOUND THEN - IF NOT b_skip_search THEN - FOR fclass IN SELECT * FROM config.metabib_class LOOP - -- RAISE NOTICE 'Emptying out %', fclass.name; - EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id; - END LOOP; - END IF; IF NOT b_skip_facet THEN DELETE FROM metabib.facet_entry WHERE source = bib_id; END IF; @@ -1095,7 +1101,7 @@ BEGIN FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id, ' ', field_types, field_list ) LOOP - -- don't store what has been normalized away + -- don't store what has been normalized away CONTINUE WHEN ind_data.value IS NULL; IF ind_data.field < 0 THEN @@ -1148,25 +1154,50 @@ BEGIN END IF; IF ind_data.search_field AND NOT b_skip_search THEN + -- This function processes stored-value (negative) normalizers so we can properly check for duplicates + value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field); + -- Avoid inserting duplicate rows EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class || '_field_entry WHERE field = $1 AND source = $2 AND value = $3' - INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value; + INTO mbe_id USING ind_data.field, ind_data.source, value_prepped; -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id; - IF mbe_id IS NULL THEN + + IF mbe_id IS NULL THEN -- No existing field entry for the to-be-stored value EXECUTE $$ INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value) VALUES ($$ || quote_literal(ind_data.field) || $$, $$ || quote_literal(ind_data.source) || $$, $$ || - quote_literal(ind_data.value) || + quote_literal(value_prepped) || $$);$$; + ELSE -- update the row so that the positive normalizers can run + + -- reusing this row, consider it new instead of extant for the deletes below + extant_mtfe := ARRAY_REMOVE(extant_mtfe,mbe_id); + extant_mafe := ARRAY_REMOVE(extant_mafe,mbe_id); + extant_msfe := ARRAY_REMOVE(extant_msfe,mbe_id); + extant_msefe := ARRAY_REMOVE(extant_msefe,mbe_id); + extant_mife := ARRAY_REMOVE(extant_mife,mbe_id); + extant_mkfe := ARRAY_REMOVE(extant_mkfe,mbe_id); + + EXECUTE 'UPDATE metabib.' || ind_data.field_class || + '_field_entry SET value = $1 WHERE id = $2' + USING value_prepped, mbe_id; END IF; END IF; END LOOP; IF NOT b_skip_search THEN + -- Flush old, un-updated entries + DELETE FROM metabib.title_field_entry WHERE id = ANY (extant_mtfe); + DELETE FROM metabib.author_field_entry WHERE id = ANY (extant_mafe); + DELETE FROM metabib.subject_field_entry WHERE id = ANY (extant_msfe); + DELETE FROM metabib.series_field_entry WHERE id = ANY (extant_msefe); + DELETE FROM metabib.identifier_field_entry WHERE id = ANY (extant_mife); + DELETE FROM metabib.keyword_field_entry WHERE id = ANY (extant_mkfe); + PERFORM metabib.update_combined_index_vectors(bib_id); PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_symspell_reification' AND enabled; IF NOT FOUND THEN @@ -1894,6 +1925,7 @@ $func$ LANGUAGE PLPGSQL; CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$ DECLARE tmp_bool BOOL; + fclass RECORD; BEGIN IF NEW.deleted THEN -- If this bib is deleted @@ -1910,6 +1942,10 @@ BEGIN -- with the #deleted modifier, so one should turn on the named -- internal flag for that functionality. DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id; + + FOR fclass IN SELECT * FROM config.metabib_class LOOP + EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || NEW.id; + END LOOP; END IF; DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible @@ -2136,28 +2172,7 @@ BEGIN NEW.index_vector = ''::tsvector; IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN - FOR normalizer IN - SELECT n.func AS func, - n.param_count AS param_count, - m.params AS params - FROM config.index_normalizer n - JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id) - WHERE field = NEW.field AND m.pos < 0 - ORDER BY m.pos LOOP - EXECUTE 'SELECT ' || normalizer.func || '(' || - quote_literal( value ) || - CASE - WHEN normalizer.param_count > 0 - THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'') - ELSE '' - END || - ')' INTO value; - - END LOOP; - - NEW.value = value; - - FOR normalizer IN + FOR normalizer IN -- only process post-stored-value normalizers SELECT n.func AS func, n.param_count AS param_count, m.params AS params diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.function.metabib_field_entry_churn_reduction.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.function.metabib_field_entry_churn_reduction.sql new file mode 100644 index 0000000000..f783f64609 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.function.metabib_field_entry_churn_reduction.sql @@ -0,0 +1,349 @@ +BEGIN; + +SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version); + +CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$ +DECLARE + tmp_bool BOOL; + fclass RECORD; +BEGIN + + IF NEW.deleted THEN -- If this bib is deleted + + PERFORM * FROM config.internal_flag WHERE + name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled; + + tmp_bool := FOUND; -- Just in case this is changed by some other statement + + PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint, TRUE, tmp_bool ); + + IF NOT tmp_bool THEN + -- One needs to keep these around to support searches + -- with the #deleted modifier, so one should turn on the named + -- internal flag for that functionality. + DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id; + + FOR fclass IN SELECT * FROM config.metabib_class LOOP + EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || NEW.id; + END LOOP; + END IF; + + DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible + DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items + DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs + RETURN NEW; -- and we're done + END IF; + + IF TG_OP = 'UPDATE' AND OLD.deleted IS FALSE THEN -- re-ingest? + PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled; + + IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change + RETURN NEW; + END IF; + END IF; + + -- Record authority linking + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled; + IF NOT FOUND THEN + PERFORM biblio.map_authority_linking( NEW.id, NEW.marc ); + END IF; + + -- Flatten and insert the mfr data + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled; + IF NOT FOUND THEN + PERFORM metabib.reingest_metabib_full_rec(NEW.id); + + -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled; + IF NOT FOUND THEN + PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted); + END IF; + END IF; + + -- Gather and insert the field entry data + PERFORM metabib.reingest_metabib_field_entries(NEW.id); + + -- Located URI magic + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled; + IF NOT FOUND THEN PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor ); END IF; + + -- (re)map metarecord-bib linking + IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag + PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled; + IF NOT FOUND THEN + PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint ); + END IF; + ELSE -- we're doing an update, and we're not deleted, remap + PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled; + IF NOT FOUND THEN + PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint ); + END IF; + END IF; + + RETURN NEW; +END; +$func$ LANGUAGE PLPGSQL; + +CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( + bib_id BIGINT, + skip_facet BOOL DEFAULT FALSE, + skip_display BOOL DEFAULT FALSE, + skip_browse BOOL DEFAULT FALSE, + skip_search BOOL DEFAULT FALSE, + only_fields INT[] DEFAULT '{}'::INT[] +) RETURNS VOID AS $func$ +DECLARE + ind_data metabib.field_entry_template%ROWTYPE; + mbe_row metabib.browse_entry%ROWTYPE; + mbe_id BIGINT; + b_skip_facet BOOL; + b_skip_display BOOL; + b_skip_browse BOOL; + b_skip_search BOOL; + value_prepped TEXT; + field_list INT[] := only_fields; + extant_mtfe BIGINT[]; + extant_mafe BIGINT[]; + extant_msfe BIGINT[]; + extant_msefe BIGINT[]; + extant_mife BIGINT[]; + extant_mkfe BIGINT[]; + field_types TEXT[] := '{}'::TEXT[]; +BEGIN + + IF field_list = '{}'::INT[] THEN + SELECT ARRAY_AGG(id) INTO field_list FROM config.metabib_field; + END IF; + + SELECT ARRAY_AGG(id) INTO extant_mtfe FROM metabib.title_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_mafe FROM metabib.author_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_msfe FROM metabib.subject_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_msefe FROM metabib.series_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_mife FROM metabib.identifier_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + SELECT ARRAY_AGG(id) INTO extant_mkfe FROM metabib.keyword_field_entry WHERE source = bib_id AND field = ANY ( field_list ); + + 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; + 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; + 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; + 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; + + IF NOT b_skip_facet THEN field_types := field_types || '{facet}'; END IF; + IF NOT b_skip_display THEN field_types := field_types || '{display}'; END IF; + IF NOT b_skip_browse THEN field_types := field_types || '{browse}'; END IF; + IF NOT b_skip_search THEN field_types := field_types || '{search}'; END IF; + + PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled; + IF NOT FOUND THEN + IF NOT b_skip_facet THEN + DELETE FROM metabib.facet_entry WHERE source = bib_id; + END IF; + IF NOT b_skip_display THEN + DELETE FROM metabib.display_entry WHERE source = bib_id; + END IF; + IF NOT b_skip_browse THEN + DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id; + END IF; + END IF; + + FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id, ' ', field_types, field_list ) LOOP + + -- don't store what has been normalized away + CONTINUE WHEN ind_data.value IS NULL; + + IF ind_data.field < 0 THEN + ind_data.field = -1 * ind_data.field; + END IF; + + IF ind_data.facet_field AND NOT b_skip_facet THEN + INSERT INTO metabib.facet_entry (field, source, value) + VALUES (ind_data.field, ind_data.source, ind_data.value); + END IF; + + IF ind_data.display_field AND NOT b_skip_display THEN + INSERT INTO metabib.display_entry (field, source, value) + VALUES (ind_data.field, ind_data.source, ind_data.value); + END IF; + + + IF ind_data.browse_field AND NOT b_skip_browse THEN + -- A caveat about this SELECT: this should take care of replacing + -- old mbe rows when data changes, but not if normalization (by + -- which I mean specifically the output of + -- evergreen.oils_tsearch2()) changes. It may or may not be + -- expensive to add a comparison of index_vector to index_vector + -- to the WHERE clause below. + + CONTINUE WHEN ind_data.sort_value IS NULL; + + value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field); + IF ind_data.browse_nocase THEN + SELECT INTO mbe_row * FROM metabib.browse_entry + WHERE evergreen.lowercase(value) = evergreen.lowercase(value_prepped) AND sort_value = ind_data.sort_value + ORDER BY sort_value, value LIMIT 1; -- gotta pick something, I guess + ELSE + SELECT INTO mbe_row * FROM metabib.browse_entry + WHERE value = value_prepped AND sort_value = ind_data.sort_value; + END IF; + + IF FOUND THEN + mbe_id := mbe_row.id; + ELSE + INSERT INTO metabib.browse_entry + ( value, sort_value ) VALUES + ( value_prepped, ind_data.sort_value ); + + mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS); + END IF; + + INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority) + VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority); + END IF; + + IF ind_data.search_field AND NOT b_skip_search THEN + -- This function processes stored-value (negative) normalizers so we can properly check for duplicates + value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field); + + -- Avoid inserting duplicate rows + EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class || + '_field_entry WHERE field = $1 AND source = $2 AND value = $3' + INTO mbe_id USING ind_data.field, ind_data.source, value_prepped; + -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id; + + IF mbe_id IS NULL THEN -- No existing field entry for the to-be-stored value + EXECUTE $$ + INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value) + VALUES ($$ || + quote_literal(ind_data.field) || $$, $$ || + quote_literal(ind_data.source) || $$, $$ || + quote_literal(value_prepped) || + $$);$$; + ELSE -- update the row so that the positive normalizers can run + + -- reusing this row, consider it new instead of extant for the deletes below + extant_mtfe := ARRAY_REMOVE(extant_mtfe,mbe_id); + extant_mafe := ARRAY_REMOVE(extant_mafe,mbe_id); + extant_msfe := ARRAY_REMOVE(extant_msfe,mbe_id); + extant_msefe := ARRAY_REMOVE(extant_msefe,mbe_id); + extant_mife := ARRAY_REMOVE(extant_mife,mbe_id); + extant_mkfe := ARRAY_REMOVE(extant_mkfe,mbe_id); + + EXECUTE 'UPDATE metabib.' || ind_data.field_class || + '_field_entry SET value = $1 WHERE id = $2' + USING value_prepped, mbe_id; + END IF; + END IF; + + END LOOP; + + IF NOT b_skip_search THEN + -- Flush old, un-updated entries + DELETE FROM metabib.title_field_entry WHERE id = ANY (extant_mtfe); + DELETE FROM metabib.author_field_entry WHERE id = ANY (extant_mafe); + DELETE FROM metabib.subject_field_entry WHERE id = ANY (extant_msfe); + DELETE FROM metabib.series_field_entry WHERE id = ANY (extant_msefe); + DELETE FROM metabib.identifier_field_entry WHERE id = ANY (extant_mife); + DELETE FROM metabib.keyword_field_entry WHERE id = ANY (extant_mkfe); + + PERFORM metabib.update_combined_index_vectors(bib_id); + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_symspell_reification' AND enabled; + IF NOT FOUND THEN + PERFORM search.symspell_dictionary_reify(); + END IF; + END IF; + + RETURN; +END; +$func$ LANGUAGE PLPGSQL; + +CREATE OR REPLACE FUNCTION public.oils_tsearch2 () RETURNS TRIGGER AS $$ +DECLARE + normalizer RECORD; + value TEXT := ''; + temp_vector TEXT := ''; + ts_rec RECORD; + cur_weight "char"; +BEGIN + + value := NEW.value; + NEW.index_vector = ''::tsvector; + + IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN + FOR normalizer IN -- only process post-stored-value normalizers + SELECT n.func AS func, + n.param_count AS param_count, + m.params AS params + FROM config.index_normalizer n + JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id) + WHERE field = NEW.field AND m.pos >= 0 + ORDER BY m.pos LOOP + EXECUTE 'SELECT ' || normalizer.func || '(' || + quote_literal( value ) || + CASE + WHEN normalizer.param_count > 0 + THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'') + ELSE '' + END || + ')' INTO value; + + END LOOP; + END IF; + + IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN + + value := ARRAY_TO_STRING( + evergreen.regexp_split_to_array(value, E'\\W+'), ' ' + ); + value := public.search_normalize(value); + NEW.index_vector = to_tsvector(TG_ARGV[0]::regconfig, value); + + ELSIF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN + FOR ts_rec IN + + SELECT DISTINCT m.ts_config, m.index_weight + FROM config.metabib_class_ts_map m + LEFT JOIN metabib.record_attr_vector_list r ON (r.source = NEW.source) + LEFT JOIN config.coded_value_map ccvm ON ( + ccvm.ctype IN ('item_lang', 'language') AND + ccvm.code = m.index_lang AND + r.vlist @> intset(ccvm.id) + ) + WHERE m.field_class = TG_ARGV[0] + AND m.active + AND (m.always OR NOT EXISTS (SELECT 1 FROM config.metabib_field_ts_map WHERE metabib_field = NEW.field)) + AND (m.index_lang IS NULL OR ccvm.id IS NOT NULL) + UNION + SELECT DISTINCT m.ts_config, m.index_weight + FROM config.metabib_field_ts_map m + LEFT JOIN metabib.record_attr_vector_list r ON (r.source = NEW.source) + LEFT JOIN config.coded_value_map ccvm ON ( + ccvm.ctype IN ('item_lang', 'language') AND + ccvm.code = m.index_lang AND + r.vlist @> intset(ccvm.id) + ) + WHERE m.metabib_field = NEW.field + AND m.active + AND (m.index_lang IS NULL OR ccvm.id IS NOT NULL) + ORDER BY index_weight ASC + + LOOP + + IF cur_weight IS NOT NULL AND cur_weight != ts_rec.index_weight THEN + NEW.index_vector = NEW.index_vector || setweight(temp_vector::tsvector,cur_weight); + temp_vector = ''; + END IF; + + cur_weight = ts_rec.index_weight; + SELECT INTO temp_vector temp_vector || ' ' || to_tsvector(ts_rec.ts_config::regconfig, value)::TEXT; + + END LOOP; + NEW.index_vector = NEW.index_vector || setweight(temp_vector::tsvector,cur_weight); + ELSE + NEW.index_vector = to_tsvector(TG_ARGV[0]::regconfig, value); + END IF; + + RETURN NEW; +END; +$$ LANGUAGE PLPGSQL; + +COMMIT; + -- 2.11.0