def render(assigns) do ~H""" <div> <form phx-submit="search" phx-change="search"> <input type="text" name="query" value=@search_term placeholder="Search posts..." class="w-full p-2 border rounded" /> <button type="submit">Search</button> </form>
def handle_event("search", %"query" => query, socket) do results = Blog.search_posts(query)
from q in queryable, where: fragment( """ to_tsvector('english', ?) @@ to_tsquery('english', ?) """, fragment("concat_ws(' ', ?)", ^fields), ^search_term ) end end # lib/my_app/ecto/full_text_search.ex defmodule MyApp.Ecto.FullTextSearch do @moduledoc """ Ecto plugin for full-text search functionality """ defmacro using (opts) do quote bind_quoted: [opts: opts] do @search_language opts[:language] || "english" @search_fields opts[:fields] || []
def index(conn, %"q" => query) do results = Blog.search_posts(query, category: conn.params["category"])
base_query |> apply_filters(params) |> apply_full_text_search(params) |> rank_by_relevance(params[:search_term]) end
render(conn, "index.html", query: query, results: results, total_count: length(results) ) end
:ok end
Using PostgreSQL's Full-Text Search # lib/my_app/repo.ex defmodule MyApp.Repo do use Ecto.Repo, otp_app: :my_app Custom full-text search function def full_text_search(queryable, search_term, fields) do search_term = search_term |> String.trim() |> String.replace(~r/\s+/, " & ") # Convert spaces to & for AND operator