2019-01-29 04:00:01 +01:00
|
|
|
"""empty message
|
|
|
|
|
|
|
|
Revision ID: 7ff57806ffd5
|
|
|
|
Revises: 2f3c3597c78d
|
|
|
|
Create Date: 2019-01-29 02:57:50.279918
|
|
|
|
|
|
|
|
"""
|
|
|
|
from alembic import op
|
2023-05-13 18:53:21 +02:00
|
|
|
from sqlalchemy import text
|
2019-01-29 04:00:01 +01:00
|
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
|
|
revision = '7ff57806ffd5'
|
|
|
|
down_revision = '2f3c3597c78d'
|
|
|
|
branch_labels = None
|
|
|
|
depends_on = None
|
|
|
|
|
|
|
|
|
|
|
|
def upgrade():
|
|
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
2023-05-13 18:53:21 +02:00
|
|
|
op.execute(text("""
|
2019-01-29 04:00:01 +01:00
|
|
|
DROP TYPE IF EXISTS tsq_state CASCADE;
|
|
|
|
|
|
|
|
CREATE TYPE tsq_state AS (
|
|
|
|
search_query text,
|
|
|
|
parentheses_stack int,
|
|
|
|
skip_for int,
|
|
|
|
current_token text,
|
|
|
|
current_index int,
|
|
|
|
current_char text,
|
|
|
|
previous_char text,
|
|
|
|
tokens text[]
|
|
|
|
);
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION tsq_append_current_token(state tsq_state)
|
|
|
|
RETURNS tsq_state AS $$
|
|
|
|
BEGIN
|
|
|
|
IF state.current_token != '' THEN
|
|
|
|
state.tokens := array_append(state.tokens, state.current_token);
|
|
|
|
state.current_token := '';
|
|
|
|
END IF;
|
|
|
|
RETURN state;
|
|
|
|
END;
|
|
|
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION tsq_tokenize_character(state tsq_state)
|
|
|
|
RETURNS tsq_state AS $$
|
|
|
|
BEGIN
|
|
|
|
IF state.current_char = '(' THEN
|
|
|
|
state.tokens := array_append(state.tokens, '(');
|
|
|
|
state.parentheses_stack := state.parentheses_stack + 1;
|
|
|
|
state := tsq_append_current_token(state);
|
|
|
|
ELSIF state.current_char = ')' THEN
|
|
|
|
IF (state.parentheses_stack > 0 AND state.current_token != '') THEN
|
|
|
|
state := tsq_append_current_token(state);
|
|
|
|
state.tokens := array_append(state.tokens, ')');
|
|
|
|
state.parentheses_stack := state.parentheses_stack - 1;
|
|
|
|
END IF;
|
|
|
|
ELSIF state.current_char = '"' THEN
|
|
|
|
state.skip_for := position('"' IN substring(
|
|
|
|
state.search_query FROM state.current_index + 1
|
|
|
|
));
|
|
|
|
|
|
|
|
IF state.skip_for > 1 THEN
|
|
|
|
state.tokens = array_append(
|
|
|
|
state.tokens,
|
|
|
|
substring(
|
|
|
|
state.search_query
|
|
|
|
FROM state.current_index FOR state.skip_for + 1
|
|
|
|
)
|
|
|
|
);
|
|
|
|
ELSIF state.skip_for = 0 THEN
|
|
|
|
state.current_token := state.current_token || state.current_char;
|
|
|
|
END IF;
|
|
|
|
ELSIF (
|
|
|
|
state.current_char = '-' AND
|
|
|
|
(state.current_index = 1 OR state.previous_char = ' ')
|
|
|
|
) THEN
|
|
|
|
state.tokens := array_append(state.tokens, '-');
|
|
|
|
ELSIF state.current_char = ' ' THEN
|
|
|
|
state := tsq_append_current_token(state);
|
|
|
|
IF substring(
|
|
|
|
state.search_query FROM state.current_index FOR 4
|
|
|
|
) = ' or ' THEN
|
|
|
|
state.skip_for := 2;
|
|
|
|
|
|
|
|
-- remove duplicate OR tokens
|
|
|
|
IF state.tokens[array_length(state.tokens, 1)] != ' | ' THEN
|
|
|
|
state.tokens := array_append(state.tokens, ' | ');
|
|
|
|
END IF;
|
|
|
|
END IF;
|
|
|
|
ELSE
|
|
|
|
state.current_token = state.current_token || state.current_char;
|
|
|
|
END IF;
|
|
|
|
RETURN state;
|
|
|
|
END;
|
|
|
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION tsq_tokenize(search_query text) RETURNS text[] AS $$
|
|
|
|
DECLARE
|
|
|
|
state tsq_state;
|
|
|
|
BEGIN
|
|
|
|
SELECT
|
|
|
|
search_query::text AS search_query,
|
|
|
|
0::int AS parentheses_stack,
|
|
|
|
0 AS skip_for,
|
|
|
|
''::text AS current_token,
|
|
|
|
0 AS current_index,
|
|
|
|
''::text AS current_char,
|
|
|
|
''::text AS previous_char,
|
|
|
|
'{}'::text[] AS tokens
|
|
|
|
INTO state;
|
|
|
|
|
|
|
|
state.search_query := lower(trim(
|
|
|
|
regexp_replace(search_query, '""+', '""', 'g')
|
|
|
|
));
|
|
|
|
|
|
|
|
FOR state.current_index IN (
|
|
|
|
SELECT generate_series(1, length(state.search_query))
|
|
|
|
) LOOP
|
|
|
|
state.current_char := substring(
|
|
|
|
search_query FROM state.current_index FOR 1
|
|
|
|
);
|
|
|
|
|
|
|
|
IF state.skip_for > 0 THEN
|
|
|
|
state.skip_for := state.skip_for - 1;
|
|
|
|
CONTINUE;
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
state := tsq_tokenize_character(state);
|
|
|
|
state.previous_char := state.current_char;
|
|
|
|
END LOOP;
|
|
|
|
state := tsq_append_current_token(state);
|
|
|
|
|
|
|
|
state.tokens := array_nremove(state.tokens, '(', -state.parentheses_stack);
|
|
|
|
|
|
|
|
RETURN state.tokens;
|
|
|
|
END;
|
|
|
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
|
|
-- Processes an array of text search tokens and returns a tsquery
|
|
|
|
CREATE OR REPLACE FUNCTION tsq_process_tokens(config regconfig, tokens text[])
|
|
|
|
RETURNS tsquery AS $$
|
|
|
|
DECLARE
|
|
|
|
result_query text;
|
|
|
|
previous_value text;
|
|
|
|
value text;
|
|
|
|
BEGIN
|
|
|
|
result_query := '';
|
|
|
|
FOREACH value IN ARRAY tokens LOOP
|
|
|
|
IF value = '"' THEN
|
|
|
|
CONTINUE;
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
IF left(value, 1) = '"' AND right(value, 1) = '"' THEN
|
|
|
|
value := phraseto_tsquery(config, value);
|
|
|
|
ELSIF value NOT IN ('(', ' | ', ')', '-') THEN
|
|
|
|
value := quote_literal(value) || ':*';
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
IF previous_value = '-' THEN
|
|
|
|
IF value = '(' THEN
|
|
|
|
value := '!' || value;
|
|
|
|
ELSE
|
|
|
|
value := '!(' || value || ')';
|
|
|
|
END IF;
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
SELECT
|
|
|
|
CASE
|
|
|
|
WHEN result_query = '' THEN value
|
|
|
|
WHEN (
|
|
|
|
previous_value IN ('!(', '(', ' | ') OR
|
|
|
|
value IN (')', ' | ')
|
|
|
|
) THEN result_query || value
|
|
|
|
ELSE result_query || ' & ' || value
|
|
|
|
END
|
|
|
|
INTO result_query;
|
|
|
|
previous_value := value;
|
|
|
|
END LOOP;
|
|
|
|
|
|
|
|
RETURN to_tsquery(config, result_query);
|
|
|
|
END;
|
|
|
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION tsq_process_tokens(tokens text[])
|
|
|
|
RETURNS tsquery AS $$
|
|
|
|
SELECT tsq_process_tokens(get_current_ts_config(), tokens);
|
|
|
|
$$ LANGUAGE SQL IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION tsq_parse(config regconfig, search_query text)
|
|
|
|
RETURNS tsquery AS $$
|
|
|
|
SELECT tsq_process_tokens(config, tsq_tokenize(search_query));
|
|
|
|
$$ LANGUAGE SQL IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION tsq_parse(config text, search_query text)
|
|
|
|
RETURNS tsquery AS $$
|
|
|
|
SELECT tsq_parse(config::regconfig, search_query);
|
|
|
|
$$ LANGUAGE SQL IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION tsq_parse(search_query text) RETURNS tsquery AS $$
|
|
|
|
SELECT tsq_parse(get_current_ts_config(), search_query);
|
|
|
|
$$ LANGUAGE SQL IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
|
|
-- remove first N elements equal to the given value from the array (array
|
|
|
|
-- must be one-dimensional)
|
|
|
|
--
|
|
|
|
-- If negative value is given as the third argument the removal of elements
|
|
|
|
-- starts from the last array element.
|
|
|
|
CREATE OR REPLACE FUNCTION array_nremove(anyarray, anyelement, int)
|
|
|
|
RETURNS ANYARRAY AS $$
|
|
|
|
WITH replaced_positions AS (
|
|
|
|
SELECT UNNEST(
|
|
|
|
CASE
|
|
|
|
WHEN $2 IS NULL THEN
|
|
|
|
'{}'::int[]
|
|
|
|
WHEN $3 > 0 THEN
|
|
|
|
(array_positions($1, $2))[1:$3]
|
|
|
|
WHEN $3 < 0 THEN
|
|
|
|
(array_positions($1, $2))[
|
|
|
|
(cardinality(array_positions($1, $2)) + $3 + 1):
|
|
|
|
]
|
|
|
|
ELSE
|
|
|
|
'{}'::int[]
|
|
|
|
END
|
|
|
|
) AS position
|
|
|
|
)
|
|
|
|
SELECT COALESCE((
|
|
|
|
SELECT array_agg(value)
|
|
|
|
FROM unnest($1) WITH ORDINALITY AS t(value, index)
|
|
|
|
WHERE index NOT IN (SELECT position FROM replaced_positions)
|
|
|
|
), $1[1:0]);
|
|
|
|
$$ LANGUAGE SQL IMMUTABLE;
|
2023-05-13 18:53:21 +02:00
|
|
|
"""))
|
2019-01-29 04:00:01 +01:00
|
|
|
# ### end Alembic commands ###
|
|
|
|
|
|
|
|
|
|
|
|
def downgrade():
|
|
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
|
|
pass
|
|
|
|
# ### end Alembic commands ###
|