Last active
October 22, 2022 13:40
-
-
Save greven/8acfac78dc381eb82d7c4bcba02efda9 to your computer and use it in GitHub Desktop.
Ecto Migration add Full Text Search
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
add_searchable_column("skills", "searchable", "english", [ | |
{:string, "name"}, | |
{:array, "alt_names"}, | |
{:array, "hidden_labels"} | |
]) | |
add_translated_searchable_column( | |
"skills", | |
"searchable_pt", | |
"portuguese", | |
"translations", | |
["$.**.name", "$.**.alt_names", "$.**.hidden_labels"] | |
) | |
create(index(:skills, [:name])) | |
create(index(:skills, [:hit_rank])) | |
# Indexes for SIMILARITY comparisons | |
create(index(:skills, ["name gin_trgm_ops"], using: "GIN", concurrently: true)) | |
# Indexes for SIMILARITY comparisons - Portuguese | |
create( | |
index(:skills, ["(translations -> 'pt' ->> 'name') gin_trgm_ops"], | |
using: "GIN", | |
concurrently: true | |
) | |
) | |
end | |
defp add_searchable_column(table, column_name, language, search_columns, create_indexes \\ true) do | |
execute(""" | |
CREATE OR REPLACE FUNCTION immutable_array_to_string(text[]) RETURNS text as $$ | |
SELECT array_to_string($1, ','); $$ LANGUAGE sql IMMUTABLE; | |
""") | |
expression = | |
search_columns | |
|> Enum.reduce([], fn | |
{:string, col}, acc -> | |
["coalesce(#{col}, '') || ' '" | acc] | |
{:array, col}, acc -> | |
["coalesce(immutable_array_to_string(#{col}), '')" | acc] | |
_, acc -> | |
acc | |
end) | |
|> Enum.reverse() | |
|> Enum.join(" || ") | |
execute( | |
""" | |
ALTER TABLE #{table} | |
ADD COLUMN #{column_name} tsvector | |
GENERATED ALWAYS AS (to_tsvector('#{language}', #{expression})) STORED | |
""", | |
"ALTER TABLE skills DROP COLUMN #{column_name}" | |
) | |
# Create Indexes | |
if create_indexes do | |
create( | |
index(table, ["(to_tsvector('#{language}', #{expression}))"], | |
name: "#{table}_search_vector_#{language}", | |
using: "GIN", | |
concurrently: true | |
) | |
) | |
end | |
end | |
defp add_translated_searchable_column( | |
table, | |
column_name, | |
language, | |
jsonb_column, | |
paths, | |
create_indexes \\ true | |
) do | |
json_paths = | |
paths | |
|> Enum.reduce([], fn path, acc -> | |
["jsonb_path_query_array(#{jsonb_column}, 'strict #{path}')" | acc] | |
end) | |
|> Enum.reverse() | |
|> Enum.join(" || ") | |
execute("CREATE TEXT SEARCH CONFIGURATION u_#{language} ( COPY = 'simple' )") | |
execute(""" | |
ALTER TEXT SEARCH CONFIGURATION u_#{language} | |
ALTER MAPPING FOR asciiword, asciihword, hword_asciipart, word, hword, hword_part | |
WITH unaccent, #{language}_stem; | |
""") | |
execute( | |
""" | |
ALTER TABLE #{table} | |
ADD COLUMN #{column_name} tsvector | |
GENERATED ALWAYS AS (jsonb_to_tsvector('u_#{language}', #{json_paths}, '["string"]')) STORED | |
""", | |
"ALTER TABLE skills DROP COLUMN #{column_name}" | |
) | |
# Create Indexes | |
if create_indexes do | |
create( | |
index(table, ["(to_tsvector('u_#{language}', #{json_paths}))"], | |
name: "#{table}_search_vector_#{language}", | |
using: "GIN", | |
concurrently: true | |
) | |
) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Querying example | |
defp or_where_fulltext_search(queryable, search_term) do | |
queryable | |
|> or_where( | |
[s], | |
fragment("websearch_to_tsquery('english', ?) @@ ?", ^search_term, s.searchable) | |
) | |
end | |
defp or_where_fulltext_translation_search(queryable, search_term, locale) do | |
language = language_name_by_code(locale) |> String.downcase() | |
search_column = "searchable_#{locale}" |> String.to_atom() | |
text_search_config = "u_#{language}" | |
queryable | |
|> or_where( | |
[s], | |
fragment( | |
"websearch_to_tsquery('?', unaccent(?)) @@ ?", | |
literal(^text_search_config), | |
^search_term, | |
field(s, ^search_column) | |
) | |
) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment