lib/query_builder/common.ex

defmodule EctoShorts.QueryBuilder.Common do
  @moduledoc """
  This module contains query building parts for common things such
  as preload, start/end date and others
  """

  import Logger, only: [debug: 1]
  import Ecto.Query, only: [
    offset: 2, preload: 2, where: 3, limit: 2,
    exclude: 2, from: 2, subquery: 1, order_by: 2
  ]

  alias EctoShorts.QueryBuilder

  @behaviour QueryBuilder

  @filters [
    :preload,
    :start_date,
    :end_date,
    :before,
    :after,
    :ids,
    :first,
    :last,
    :limit,
    :offset,
    :search,
    :order_by
  ]

  @spec filters :: list(atom)
  def filters, do: @filters

  @impl QueryBuilder
  def create_schema_filter({:preload, val}, query), do: preload(query, ^val)

  @impl QueryBuilder
  def create_schema_filter({:start_date, val}, query), do: where(query, [m], m.inserted_at >= ^(val))

  @impl QueryBuilder
  def create_schema_filter({:end_date, val}, query), do: where(query, [m], m.inserted_at <= ^val)

  @impl QueryBuilder
  def create_schema_filter({:before, id}, query), do: where(query, [m], m.id < ^id)

  @impl QueryBuilder
  def create_schema_filter({:after, id}, query), do: where(query, [m], m.id > ^id)

  @impl QueryBuilder
  def create_schema_filter({:ids, ids}, query), do: where(query, [m], m.id in ^ids)

  @impl QueryBuilder
  def create_schema_filter({:offset, val}, query), do: offset(query, ^val)

  @impl QueryBuilder
  def create_schema_filter({:limit, val}, query), do: limit(query, ^val)

  @impl QueryBuilder
  def create_schema_filter({:first, val}, query), do: limit(query, ^val)

  @impl QueryBuilder
  def create_schema_filter({:order_by, val}, query), do: order_by(query, ^val)

  @impl QueryBuilder
  def create_schema_filter({:last, val}, query) do
    query
      |> exclude(:order_by)
      |> from(order_by: [desc: :inserted_at], limit: ^val)
      |> subquery
      |> order_by(:id)
  end

  @impl QueryBuilder
  def create_schema_filter({:search, val}, query) do
    schema = QueryBuilder.query_schema(query)

    if function_exported?(schema, :by_search, 2) do
      schema.by_search(query, val)
    else
      debug "create_schema_filter: #{inspect schema} doesn't define &search_by/2 (query, params)"

      query
    end
  end
end