diff --git a/lib/music_library/artists.ex b/lib/music_library/artists.ex index 94174aa8..e8db07ef 100644 --- a/lib/music_library/artists.ex +++ b/lib/music_library/artists.ex @@ -75,6 +75,47 @@ defmodule MusicLibrary.Artists do |> Enum.into(%{}) end + @spec search_by_name(String.t(), non_neg_integer()) :: [map()] + def search_by_name(query, limit) do + case String.trim(query) do + "" -> + [] + + trimmed_query -> + normalized_query = String.downcase(trimmed_query) + + from(ar in ArtistRecord, + join: ai in ArtistInfo, + on: ar.musicbrainz_id == ai.id, + where: + fragment("lower(unaccent(artist ->> '$.name')) LIKE ?", ^"%#{normalized_query}%"), + group_by: ar.musicbrainz_id, + select: %{artist: ar.artist, image_data_hash: ai.image_data_hash}, + limit: ^limit, + order_by: fragment("artist ->> '$.name'") + ) + |> Repo.all() + end + end + + @spec search_by_name_count(String.t()) :: non_neg_integer() + def search_by_name_count(query) do + case String.trim(query) do + "" -> + 0 + + trimmed_query -> + normalized_query = String.downcase(trimmed_query) + + from(ar in ArtistRecord, + where: fragment("lower(artist ->> '$.name') LIKE ?", ^"%#{normalized_query}%"), + select: ar.musicbrainz_id, + distinct: true + ) + |> Repo.aggregate(:count, :musicbrainz_id) + end + end + @spec exists?(String.t()) :: boolean() def exists?(artist_id) do q = diff --git a/lib/music_library/search.ex b/lib/music_library/search.ex index f153ab1e..596e9142 100644 --- a/lib/music_library/search.ex +++ b/lib/music_library/search.ex @@ -8,11 +8,7 @@ defmodule MusicLibrary.Search do - Artists """ - import Ecto.Query, warn: false - - alias MusicLibrary.Artists.ArtistInfo - alias MusicLibrary.{Collection, RecordSets, Repo, Wishlist} - alias MusicLibrary.Records.ArtistRecord + alias MusicLibrary.{Artists, Collection, RecordSets, Wishlist} @pagination Application.compile_env!(:music_library, :pagination) @@ -62,26 +58,7 @@ defmodule MusicLibrary.Search do """ @spec search_artists(String.t(), non_neg_integer()) :: [map()] def search_artists(query, limit \\ @pagination[:search_preview_limit]) do - case String.trim(query) do - "" -> - [] - - trimmed_query -> - normalized_query = String.downcase(trimmed_query) - - q = - from ar in ArtistRecord, - join: ai in ArtistInfo, - on: ar.musicbrainz_id == ai.id, - where: - fragment("lower(unaccent(artist ->> '$.name')) LIKE ?", ^"%#{normalized_query}%"), - group_by: ar.musicbrainz_id, - select: %{artist: ar.artist, image_data_hash: ai.image_data_hash}, - limit: ^limit, - order_by: fragment("artist ->> '$.name'") - - Repo.all(q) - end + Artists.search_by_name(query, limit) end @doc """ @@ -115,25 +92,6 @@ defmodule MusicLibrary.Search do """ @spec search_artists_count(String.t()) :: non_neg_integer() def search_artists_count(query) do - case String.trim(query) do - "" -> - 0 - - trimmed_query -> - normalized_query = String.downcase(trimmed_query) - - # Use a subquery to count distinct musicbrainz_ids - subquery = - from ar in ArtistRecord, - where: fragment("lower(artist ->> '$.name') LIKE ?", ^"%#{normalized_query}%"), - select: ar.musicbrainz_id, - distinct: true - - q = - from s in subquery(subquery), - select: count() - - Repo.one(q) || 0 - end + Artists.search_by_name_count(query) end end diff --git a/test/music_library/artists_test.exs b/test/music_library/artists_test.exs index 1cc034a3..a7e73f02 100644 --- a/test/music_library/artists_test.exs +++ b/test/music_library/artists_test.exs @@ -31,6 +31,57 @@ defmodule MusicLibrary.ArtistsTest do end end + describe "search_by_name/2" do + test "returns artists matching the query" do + record = record_with_artist("Marillion") + [artist] = record.artists + artist_info(artist.musicbrainz_id) + + results = Artists.search_by_name("Marillion", 10) + + assert length(results) == 1 + assert hd(results).artist.name == "Marillion" + end + + test "returns empty list for empty query" do + assert Artists.search_by_name("", 10) == [] + assert Artists.search_by_name(" ", 10) == [] + end + + test "respects limit" do + for name <- ["Marillion", "Steven Wilson"] do + record = record_with_artist(name) + [artist] = record.artists + artist_info(artist.musicbrainz_id) + end + + results = Artists.search_by_name("i", 1) + + assert length(results) == 1 + end + end + + describe "search_by_name_count/1" do + test "returns count of matching artists" do + record = record_with_artist("Marillion") + _other = record_with_artist("Marillion") + + [artist] = record.artists + artist_info(artist.musicbrainz_id) + + assert Artists.search_by_name_count("Marillion") == 1 + end + + test "returns 0 for empty query" do + assert Artists.search_by_name_count("") == 0 + assert Artists.search_by_name_count(" ") == 0 + end + + test "returns 0 for no matches" do + assert Artists.search_by_name_count("zzz_nonexistent_zzz") == 0 + end + end + describe "fetch_artist_info/1" do test "it stores musicbrainz and discogs data" do steven_wilson_musicbrainz_id = "3a51b862-0144-40f6-aa17-6aaeefea29d9"