2
0
mirror of https://github.com/moebooru/moebooru synced 2025-08-22 01:47:48 +00:00
moebooru/db/migrate/20100906054326_add_fts_to_history.rb
2018-03-10 18:15:56 +09:00

96 lines
3.3 KiB
Ruby

class AddFtsToHistory < ActiveRecord::Migration[5.1]
def self.up
execute "SET statement_timeout = 0"
execute "SET search_path = public"
execute "ALTER TABLE history_changes ADD COLUMN value_index tsvector"
# Is there seriously no string join function in Postgres, even though it has three
# different functions for split?
execute """
CREATE OR REPLACE FUNCTION join_string(words varchar[], delimitor varchar) RETURNS varchar AS $$
DECLARE
result varchar := '';
first boolean := true;
BEGIN
FOR i IN coalesce(array_lower(words, 1), 0) .. coalesce(array_upper(words, 1), 0) LOOP
IF NOT first THEN
result := result || delimitor;
ELSE
first := false;
END IF;
result := result || words[i];
END LOOP;
RETURN result;
END
$$ LANGUAGE plpgsql;
"""
execute """
CREATE OR REPLACE FUNCTION get_new_tags(old_array varchar[], new_array varchar[]) RETURNS varchar[] AS $$
DECLARE
changed_tags varchar[];
BEGIN
FOR i IN array_lower(new_array, 1) .. array_upper(new_array, 1) LOOP
IF NOT new_array[i] = ANY (old_array) THEN
changed_tags := array_append(changed_tags, new_array[i]);
END IF;
END LOOP;
RETURN changed_tags;
END
$$ LANGUAGE plpgsql;
"""
# For most value fields, just index it directly. For cached_tags, index the changes compared
# to the previous value.
execute """
CREATE OR REPLACE FUNCTION history_changes_index_trigger() RETURNS trigger AS $$
DECLARE
old_tags varchar;
old_tags_array varchar[];
new_tags_array varchar[];
changed_tags_array varchar[];
indexed_value varchar;
BEGIN
IF (new.table_name, new.field) IN (('posts', 'cached_tags')) THEN
old_tags := prev.value FROM history_changes prev WHERE (prev.id = new.previous_id) LIMIT 1;
old_tags_array := regexp_split_to_array(COALESCE(old_tags, ''), ' ');
new_tags_array := regexp_split_to_array(COALESCE(new.value, ''), ' ');
changed_tags_array := get_new_tags(old_tags_array, new_tags_array);
changed_tags_array := array_cat(changed_tags_array, get_new_tags(new_tags_array, old_tags_array));
indexed_value := join_string(changed_tags_array, ' ');
ELSEIF (new.table_name, new.field) IN (('pools', 'name')) THEN
indexed_value := translate(new.value, '_', ' ');
ELSEIF (new.table_name, new.field) IN (('posts', 'cached_tags'), ('posts', 'source'), ('pools', 'description')) THEN
indexed_value := new.value;
ELSE
RETURN new;
END IF;
new.value_index := to_tsvector('public.danbooru', indexed_value);
RETURN new;
END
$$ LANGUAGE plpgsql;
"""
execute """
CREATE TRIGGER trg_history_changes_value_index_update BEFORE INSERT OR UPDATE ON history_changes
FOR EACH ROW EXECUTE PROCEDURE history_changes_index_trigger();
"""
# Trigger a value_index update for all rows.
execute "UPDATE history_changes SET value = value"
# Create the index after updating.
execute "CREATE INDEX index_history_changes_on_value_index ON history_changes USING gin(value_index)"
end
def self.down
execute "ALTER TABLE history_changes DROP COLUMN value_index"
end
end