901a6b1c93
credo 1.7.12 => 1.7.13 usage_rules 0.1.24 => 0.1.25
123 lines
3.1 KiB
Elixir
123 lines
3.1 KiB
Elixir
defmodule MusicLibrary.Search do
|
|
@moduledoc """
|
|
Universal search functionality across Records and Artists.
|
|
|
|
This module provides unified search across:
|
|
- Records in Collection (purchased records)
|
|
- Records in Wishlist (unpurchased records)
|
|
- Artists
|
|
"""
|
|
|
|
import Ecto.Query, warn: false
|
|
|
|
alias MusicLibrary.Artists.ArtistInfo
|
|
alias MusicLibrary.{Collection, Repo, Wishlist}
|
|
alias MusicLibrary.Records.ArtistRecord
|
|
|
|
@doc """
|
|
Performs a universal search across all entity types.
|
|
|
|
Returns a map with grouped results:
|
|
- :collection - Records in the collection
|
|
- :wishlist - Records in the wishlist
|
|
- :artists - Artists
|
|
|
|
Options:
|
|
- :limit - Limit per category (default: 5)
|
|
"""
|
|
def universal_search(query, opts \\ []) do
|
|
limit = Keyword.get(opts, :limit, 5)
|
|
|
|
%{
|
|
collection: search_collection(query, limit),
|
|
wishlist: search_wishlist(query, limit),
|
|
artists: search_artists(query, limit)
|
|
}
|
|
end
|
|
|
|
@doc """
|
|
Searches records in the collection (purchased records).
|
|
"""
|
|
def search_collection(query, limit \\ 5) do
|
|
Collection.search_records(query, limit: limit)
|
|
end
|
|
|
|
@doc """
|
|
Searches records in the wishlist (unpurchased records).
|
|
"""
|
|
def search_wishlist(query, limit \\ 5) do
|
|
Wishlist.search_records(query, limit: limit)
|
|
end
|
|
|
|
@doc """
|
|
Searches artists by name.
|
|
|
|
Searches across artist names in the artist_records table,
|
|
returning distinct artists that match the query.
|
|
"""
|
|
def search_artists(query, limit \\ 5) 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
|
|
end
|
|
|
|
@doc """
|
|
Gets the count of search results for each category.
|
|
|
|
Returns a map with counts for each category:
|
|
- :collection_count
|
|
- :wishlist_count
|
|
- :artists_count
|
|
"""
|
|
def search_counts(query) do
|
|
%{
|
|
collection_count: Collection.search_records_count(query),
|
|
wishlist_count: Wishlist.search_records_count(query),
|
|
artists_count: search_artists_count(query)
|
|
}
|
|
end
|
|
|
|
@doc """
|
|
Gets the count of artists matching the search query.
|
|
"""
|
|
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
|
|
end
|
|
end
|