diff --git a/lib/pinchflat/media/media_query.ex b/lib/pinchflat/media/media_query.ex index c3a1a65..2eeb5f9 100644 --- a/lib/pinchflat/media/media_query.ex +++ b/lib/pinchflat/media/media_query.ex @@ -125,6 +125,18 @@ defmodule Pinchflat.Media.MediaQuery do ) end + def matches_search_term(nil), do: dynamic([mi], true) + + def matches_search_term(term) do + escaped_term = clean_search_term(term) + + # Matching on `term` instead of `escaped_term` because the latter can mangle empty strings + case String.trim(term) do + "" -> dynamic([mi], true) + _ -> dynamic([mi], fragment("media_items_search_index MATCH ?", ^escaped_term)) + end + end + def require_assoc(query, identifier) do if has_named_binding?(query, identifier) do query @@ -133,6 +145,10 @@ defmodule Pinchflat.Media.MediaQuery do end end + defp do_require_assoc(query, :media_items_search_index) do + from(mi in query, join: s in assoc(mi, :media_items_search_index), as: :media_items_search_index) + end + defp do_require_assoc(query, :source) do from(mi in query, join: s in assoc(mi, :source), as: :source) end @@ -148,9 +164,11 @@ defmodule Pinchflat.Media.MediaQuery do def matching_search_term(query, nil), do: query def matching_search_term(query, term) do + escaped_term = clean_search_term(term) + from(mi in query, join: mi_search_index in assoc(mi, :media_items_search_index), - where: fragment("media_items_search_index MATCH ?", ^term), + where: fragment("media_items_search_index MATCH ?", ^escaped_term), select_merge: %{ matching_search_term: fragment(""" @@ -162,4 +180,26 @@ defmodule Pinchflat.Media.MediaQuery do order_by: [desc: fragment("rank")] ) end + + # SQLite's FTS5 is very picky about what it will accept as a search term. + # To that end, we need to clean up the search term before passing it to the + # MATCH clause. + # This method: + # - Trims leading and trailing whitespace + # - Collapses multiple spaces into a single space + # - Removes quote characters + # - Wraps any word in quotes (must happen after the double quote replacement) + # + # This allows for works with apostrophes and quotes to be searched for correctly + defp clean_search_term(nil), do: "" + defp clean_search_term(""), do: "" + + defp clean_search_term(term) do + term + |> String.trim() + |> String.replace(~r/\s+/, " ") + |> String.split(~r/\s+/) + |> Enum.map(fn str -> String.replace(str, ~s("), "") end) + |> Enum.map_join(" ", fn str -> ~s("#{str}") end) + end end diff --git a/lib/pinchflat_web/components/layouts/partials/header.html.heex b/lib/pinchflat_web/components/layouts/partials/header.html.heex index 1c03f08..ebbfd8d 100644 --- a/lib/pinchflat_web/components/layouts/partials/header.html.heex +++ b/lib/pinchflat_web/components/layouts/partials/header.html.heex @@ -22,7 +22,7 @@ type="text" name="q" value={@params["q"]} - placeholder="Type to search..." + placeholder="Search all media..." class="w-full bg-transparent pl-9 pr-4 border-0 focus:ring-0 focus:outline-none" /> diff --git a/lib/pinchflat_web/controllers/searches/search_html/show.html.heex b/lib/pinchflat_web/controllers/searches/search_html/show.html.heex index 22ea5dd..47a09fb 100644 --- a/lib/pinchflat_web/controllers/searches/search_html/show.html.heex +++ b/lib/pinchflat_web/controllers/searches/search_html/show.html.heex @@ -10,19 +10,13 @@ <%= if match?([_|_], @search_results) do %> <.table rows={@search_results} table_class="text-black dark:text-white"> <:col :let={result} label="Title"> - <%= result.title %> + <.subtle_link href={~p"/sources/#{result.source_id}/media/#{result.id}"}> + <%= StringUtils.truncate(result.title, 35) %> + <:col :let={result} label="Excerpt"> <.highlight_search_terms text={result.matching_search_term} /> - <:col :let={result} label="" class="flex place-content-evenly"> - <.link - href={~p"/sources/#{result.source_id}/media/#{result.id}"} - class="hover:text-secondary duration-200 ease-in-out mx-0.5" - > - <.icon name="hero-eye" /> - - <% else %>
No results found
diff --git a/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex b/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex index 48e766c..9912137 100644 --- a/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex +++ b/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex @@ -8,7 +8,7 @@ defmodule Pinchflat.Sources.MediaItemTableLive do @limit 10 - def render(%{records: []} = assigns) do + def render(%{total_record_count: 0} = assigns) do ~H"""