From ad29b8f96d808c9f41171b51aa9edf94873d06a3 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sun, 25 May 2025 09:26:01 +0100 Subject: [PATCH] Normalized search index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that one can search for 'bjorn' and find 'Bjørn' --- lib/music_library/records.ex | 6 + ...5081349_create_unaccented_search_index.exs | 275 ++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 priv/repo/migrations/20250525081349_create_unaccented_search_index.exs diff --git a/lib/music_library/records.ex b/lib/music_library/records.ex index 31250051..e22e3496 100644 --- a/lib/music_library/records.ex +++ b/lib/music_library/records.ex @@ -76,10 +76,16 @@ defmodule MusicLibrary.Records do {:artist, artist}, search -> search |> where(fragment("records_search_index match 'artists : ?*'", literal(^artist))) + |> or_where( + fragment("records_search_index match 'normalized_artists : ?*'", literal(^artist)) + ) {:album, album}, search -> search |> where(fragment("records_search_index match 'title : ?*'", literal(^album))) + |> or_where( + fragment("records_search_index match 'normalized_title : ?*'", literal(^album)) + ) {:genre, genre}, search -> search diff --git a/priv/repo/migrations/20250525081349_create_unaccented_search_index.exs b/priv/repo/migrations/20250525081349_create_unaccented_search_index.exs new file mode 100644 index 00000000..d29fdd01 --- /dev/null +++ b/priv/repo/migrations/20250525081349_create_unaccented_search_index.exs @@ -0,0 +1,275 @@ +defmodule MusicLibrary.Repo.Migrations.CreateUnaccentedSearchIndex do + use Ecto.Migration + + def up do + execute "DROP TRIGGER IF EXISTS records_after_update" + execute "DROP TRIGGER IF EXISTS records_after_insert" + execute "DROP TABLE records_search_index" + + flush() + + execute """ + CREATE VIRTUAL TABLE records_search_index USING fts5( + id UNINDEXED, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids UNINDEXED, + included_release_group_ids UNINDEXED, + cover_hash UNINDEXED, + purchased_at UNINDEXED, + release_date + ); + """ + + flush() + + execute """ + CREATE TRIGGER records_after_insert + AFTER INSERT ON records + BEGIN + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records + WHERE NEW.id = records.id; + END; + """ + + execute """ + CREATE TRIGGER records_after_update + AFTER UPDATE ON records + BEGIN + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records + WHERE NEW.id = records.id; + END; + """ + + flush() + + execute """ + INSERT INTO records_search_index( + id, + type, + format, + title, + normalized_title, + artists, + normalized_artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + unaccent(title), + artists, + unaccent(artists), + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records; + """ + end + + def down do + execute "DROP TRIGGER IF EXISTS records_after_update" + execute "DROP TRIGGER IF EXISTS records_after_insert" + execute "DROP TABLE records_search_index" + + flush() + + execute """ + CREATE VIRTUAL TABLE records_search_index USING fts5( + id UNINDEXED, + type, + format, + title, + artists, + genres, + musicbrainz_id, + release_ids UNINDEXED, + included_release_group_ids UNINDEXED, + cover_hash UNINDEXED, + purchased_at UNINDEXED, + release_date + ); + """ + + flush() + + execute """ + CREATE TRIGGER records_after_insert + AFTER INSERT ON records + BEGIN + INSERT INTO records_search_index( + id, + type, + format, + title, + artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records + WHERE NEW.id = records.id; + END; + """ + + execute """ + CREATE TRIGGER records_after_update + AFTER UPDATE ON records + BEGIN + INSERT INTO records_search_index( + id, + type, + format, + title, + artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records + WHERE NEW.id = records.id; + END; + """ + + flush() + + execute """ + INSERT INTO records_search_index( + id, + type, + format, + title, + artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + ) SELECT + id, + type, + format, + title, + artists, + genres, + musicbrainz_id, + release_ids, + included_release_group_ids, + cover_hash, + purchased_at, + release_date + FROM records; + """ + end +end