Pass one from Tidewave

This commit is contained in:
Claudio Ortolina
2025-09-13 08:25:08 +03:00
parent 7eab76af28
commit 62578de950
+50 -7
View File
@@ -58,6 +58,39 @@ defmodule MusicLibrary.Records do
end end
end end
defp fts_escape(term) do
# The SearchParser may have already doubled single quotes
# For FTS5, we need to wrap special terms in double quotes instead
# First, undo the SearchParser's quote doubling
clean_term = String.replace(term, "''", "'")
# For FTS5, if the term contains special characters, we need to wrap it in double quotes
if String.contains?(clean_term, ["'", " ", "\"", "(", ")", "^", "-", ":"]) do
# Escape internal double quotes and wrap in double quotes
escaped = String.replace(clean_term, "\"", "\"\"")
"\"#{escaped}\"*"
else
"#{clean_term}*"
end
end
defp fts_query_escape(query) do
# For general queries, split terms and combine with AND
# First undo SearchParser's quote doubling
clean_query = String.replace(query, "''", "'")
clean_query
|> String.split(~r/\s+/, trim: true)
|> Enum.map_join(" AND ", fn term ->
if String.contains?(term, ["'", " ", "\"", "(", ")", "^", "-", ":"]) do
escaped = String.replace(term, "\"", "\"\"")
"\"#{escaped}\""
else
term
end
end)
end
defp build_search(initial_search, query, order \\ :alphabetical) do defp build_search(initial_search, query, order \\ :alphabetical) do
{:ok, parsed_query} = SearchParser.parse(query) {:ok, parsed_query} = SearchParser.parse(query)
@@ -84,26 +117,34 @@ defmodule MusicLibrary.Records do
Enum.reduce(parsed_query, search_with_order, fn Enum.reduce(parsed_query, search_with_order, fn
{:artist, artist}, search -> {:artist, artist}, search ->
escaped_artist = fts_escape(artist)
search search
|> where(fragment("records_search_index match 'artists : ?*'", identifier(^artist))) |> where(fragment("records_search_index MATCH 'artists : ' || ?", ^escaped_artist))
|> or_where( |> or_where(
fragment("records_search_index match 'normalized_artists : ?*'", identifier(^artist)) fragment("records_search_index MATCH 'normalized_artists : ' || ?", ^escaped_artist)
) )
{:album, album}, search -> {:album, album}, search ->
escaped_album = fts_escape(album)
search search
|> where(fragment("records_search_index match 'title : ?*'", identifier(^album))) |> where(fragment("records_search_index MATCH 'title : ' || ?", ^escaped_album))
|> or_where( |> or_where(
fragment("records_search_index match 'normalized_title : ?*'", identifier(^album)) fragment("records_search_index MATCH 'normalized_title : ' || ?", ^escaped_album)
) )
{:genre, genre}, search -> {:genre, genre}, search ->
escaped_genre = fts_escape(genre)
search search
|> where(fragment("records_search_index match 'genres : ?*'", identifier(^genre))) |> where(fragment("records_search_index MATCH 'genres : ' || ?", ^escaped_genre))
{:mbid, mbid}, search -> {:mbid, mbid}, search ->
escaped_mbid = fts_escape(mbid)
search search
|> where(fragment("records_search_index = '?*'", identifier(^mbid))) |> where(fragment("records_search_index MATCH ?", ^escaped_mbid))
{:format, format}, search -> {:format, format}, search ->
search |> where([r], r.format == ^format) search |> where([r], r.format == ^format)
@@ -115,8 +156,10 @@ defmodule MusicLibrary.Records do
search search
{:query, raw_query}, search -> {:query, raw_query}, search ->
escaped_query = fts_query_escape(raw_query)
search search
|> where(fragment("records_search_index = '?*'", identifier(^raw_query))) |> where(fragment("records_search_index MATCH ?", ^escaped_query))
end) end)
end end