lib/meilisearch/stats.ex

defmodule Meilisearch.Stats do
  @moduledoc """
  Get Stats about your Meilisearch indexes.
  [Key API](https://www.meilisearch.com/docs/reference/api/stats)
  """

  use TypedEctoSchema

  @primary_key false
  typed_embedded_schema do
    field(:databaseSize, :integer)
    field(:lastUpdate, :utc_datetime)
    field(:indexes, :map) :: %{String.t() => Meilisearch.Stats.Stat.t()}
  end

  def cast(data) when is_map(data) do
    %__MODULE__{}
    |> Ecto.Changeset.cast(data, [:databaseSize, :lastUpdate, :indexes])
    |> cast_indexes()
    |> Ecto.Changeset.apply_changes()
  end

  defp cast_indexes(changeset) do
    indexes =
      changeset
      |> Ecto.Changeset.get_change(:indexes, %{})
      |> Enum.map(fn {k, v} -> {k, Meilisearch.Stats.Stat.cast(v)} end)
      |> Enum.into(%{})

    Ecto.Changeset.put_change(changeset, :indexes, indexes)
  end

  @doc """
  Get stats about all indexes of your Meilisearch instance.
  [Meilisearch documentation](https://www.meilisearch.com/docs/reference/api/stats#get-stats-of-all-indexes)

  ## Examples

      iex> client = Meilisearch.Client.new(endpoint: "http://localhost:7700", key: "master_key_test")
      iex> Meilisearch.Stats.all(client)
      {:ok, %{
        databaseSize: 447819776,
        lastUpdate: ~U[2021-08-12 10:00:00],
        indexes: %{
          "movies" => %{
            numberOfDocuments: 19654,
            isIndexing: false,
            fieldDistribution: %{
              "poster" => 19654,
              "overview" => 19654,
              "title" => 19654,
              "id" => 19654,
              "release_date" => 19654
            }
          },
          "books" => %{
            numberOfDocuments: 5,
            isIndexing: false,
            fieldDistribution: %{
              "id" => 5,
              "title" => 5,
              "author" => 5,
              "price" => 5,
              "genres" => 5
            }
          }
        }
      }}

  """
  @spec all(Tesla.Client.t(), offset: integer(), limit: integer()) ::
          {:ok, __MODULE__.t()}
          | {:error, Meilisearch.Client.error()}
  def all(client, opts \\ []) do
    with {:ok, data} <-
           client
           |> Tesla.get("/stats", query: opts)
           |> Meilisearch.Client.handle_response() do
      {:ok, cast(data)}
    end
  end

  @doc """
  Get stats about a specific index of your Meilisearch instance.
  [Meilisearch documentation](https://www.meilisearch.com/docs/reference/api/stats#get-stats-of-an-index)

  ## Examples

      iex> client = Meilisearch.Client.new(endpoint: "http://localhost:7700", key: "master_key_test")
      iex> Meilisearch.Stats.get(client, "movies")
      {:ok, %{
        numberOfDocuments: 19654,
        isIndexing: false,
        fieldDistribution: &{
          "poster" => 19654,
          "release_date" => 19654,
          "title" => 19654,
          "id" => 19654,
          "overview" => 19654
        }
      }}

  """
  @spec get(Tesla.Client.t(), String.t()) ::
          {:ok, Meilisearch.Stats.Stat.t()} | {:error, Meilisearch.Client.error()}
  def get(client, index_uid) do
    with {:ok, data} <-
           client
           |> Tesla.get("/indexes/:index_uid/stats", opts: [path_params: [index_uid: index_uid]])
           |> Meilisearch.Client.handle_response() do
      {:ok, Meilisearch.Stats.Stat.cast(data)}
    end
  end

  defmodule Stat do
    @moduledoc """
    see [documentation](https://www.meilisearch.com/docs/reference/api/stats#stats-object)
    """

    use TypedEctoSchema

    @primary_key false
    typed_embedded_schema do
      field(:numberOfDocuments, :integer)
      field(:isIndexing, :boolean)
      field(:fieldDistribution, :map) :: %{String.t() => integer()}
    end

    def cast(data) when is_map(data) do
      %__MODULE__{}
      |> Ecto.Changeset.cast(data, [:numberOfDocuments, :isIndexing, :fieldDistribution])
      |> Ecto.Changeset.apply_changes()
    end
  end
end