lib/ex_teal/global_search.ex

defmodule ExTeal.GlobalSearch do
  @moduledoc """
  Functionality for searching across a collection of resources using a single search term.
  """

  defstruct conn: nil, resources: [], results: []

  @type t :: %__MODULE__{}

  import Ecto.Query, only: [from: 2]

  alias ExTeal.GlobalSearch
  alias ExTeal.Resource.{Index, Records, Serializer}
  alias Plug.Conn

  @spec new(Conn.t(), [module]) :: GlobalSearch.t()
  def new(conn, resources) do
    %__MODULE__{conn: conn, resources: resources}
  end

  def run(%GlobalSearch{conn: %Conn{params: params} = conn, resources: resources} = gs) do
    results =
      Enum.map(resources, fn resource ->
        result =
          conn
          |> resource.handle_index(params)
          |> Records.preload(resource)
          |> Index.search(params, resource)
          |> prepare_for_search(resource)
          |> resource.repo().all()

        {resource, result}
      end)

    Map.put(gs, :results, results)
  end

  def run(search), do: search

  def render(%GlobalSearch{results: results, conn: conn}) do
    results =
      results
      |> Enum.map(fn {resource, result} ->
        Enum.map(result, fn r ->
          r
          |> Serializer.schema_summary(resource)
          |> Map.merge(%{
            resource_name: resource.uri(),
            resource_title: resource.title()
          })
        end)
      end)
      |> Enum.concat()

    {:ok, body} = Jason.encode(%{results: results})
    Serializer.as_json(conn, body, 200)
  end

  defp prepare_for_search(query, _resource) do
    from(q in query, limit: 5)
  end
end