Uni Ecto Plugin |work| 💎

timestamps() end

defp apply_full_text_search(query, %search_term: term) when is_binary(term) and term != "" do from q in query, where: fragment( "to_tsvector('english', ?) @@ plainto_tsquery('english', ?)", fragment("coalesce(?, '') || ' ' || coalesce(?, '')", q.title, q.content), ^term ) end defp apply_full_text_search(query, _), do: query

test "search handles empty query" do results = Blog.search_posts("") assert results == [] end end uni ecto plugin

json(conn, %suggestions: suggestions) end end # lib/my_app_web/live/search_live.ex defmodule MyAppWeb.SearchLive do use MyAppWeb, :live_view alias MyApp.Blog

defp update_search_vector(changeset) do case changeset do %Ecto.Changesetvalid?: true -> Ecto.Changeset.put_change(changeset, :search_vector, fragment( "to_tsvector('english', coalesce(?, '') || ' ' || coalesce(?, ''))", changeset.data.title, changeset.data.content )) _ -> changeset end end end # priv/repo/migrations/20240101000000_add_full_text_search.exs defmodule MyApp.Repo.Migrations.AddFullTextSearch do use Ecto.Migration def change do # Add GIN index for full-text search execute(""" CREATE INDEX posts_search_idx ON posts USING GIN(to_tsvector('english', title || ' ' || content)) """) timestamps() end defp apply_full_text_search(query

defp order_by_relevance(query, nil), do: query defp order_by_relevance(query, term) do from q in query, order_by: [desc: fragment( "ts_rank(to_tsvector('english', ?), plainto_tsquery('english', ?))", fragment("coalesce(?, '') || ' ' || coalesce(?, '')", q.title, q.content), ^term )] end end # lib/my_app_web/controllers/search_controller.ex defmodule MyAppWeb.SearchController do use MyAppWeb, :controller alias MyApp.Blog

<%= if @searching do %> <div class="mt-4"> <h3>Found @results results</h3> <div class="space-y-2"> <%= for result <- @results do %> <div class="p-4 border rounded"> <h4 class="font-bold"><%= result.title %></h4> <p><%= result.content %></p> </div> <% end %> </div> </div> <% end %> </div> """ end end # config/config.exs config :my_app, :search, language: "english", min_word_length: 2, stop_words: ["the", "a", "an", "and", "or"], highlight: true, highlight_tag: "<mark>" 10. Testing the Search # test/my_app/search_test.exs defmodule MyApp.SearchTest do use MyApp.DataCase alias MyApp.Blog alias MyApp.Blog.Post where: fragment( "to_tsvector('english'

base_query |> apply_filters(params) |> apply_full_text_search(params) |> rank_by_relevance(params[:search_term]) end