lib/couchx/query_handler.ex

defmodule Couchx.QueryHandler do
  @empty_response  [
    %{"rows" => []},
    %{"docs" => []},
    %{"bookmark" => "nil", "docs" => []}
  ]

  def query_results([], _, _), do: {0, []}
  def query_results({:error, reason}, _, _), do: raise Couchx.DbError, message: "#{reason}"

  def query_results({:ok, response}, _, _) when response in @empty_response do
    {0, []}
  end

  def query_results({:ok, response}, fields, metadata) do
    query_results(response, fields, metadata)
  end

  def query_results(%{"rows" => rows}, fields, metadata) do
    Enum.map(rows, &query_results(&1, fields, metadata))
    |> execute_response
  end

  def query_results(%{"docs" => docs}, fields, metadata) do
    Enum.map(docs, &process_docs(&1, fields, metadata))
    |> execute_response
  end

  def query_results(%{"doc" => doc}, fields, metadata) do
    process_docs(doc, fields, metadata)
  end

  def query_results(doc, fields, metadata) do
    process_docs(doc, fields, metadata)
    |> execute_response
  end

  defp execute_response([]), do: {0, []}

  defp execute_response([item | _] = values) when is_list(item) do
    {length(values), values}
  end

  defp execute_response(value), do: {1, [value]}

  defp process_docs(rows, fields, meta) when is_list(rows) do
    Enum.map(rows, &process_docs(&1, fields, meta))
  end

  defp process_docs(doc, fields, nil) do
    fields
    |> Enum.reduce([], fn({key, value}, acc)->
         confirmed_value = if doc[key], do: value, else: nil
         acc ++ confirmed_value
       end)
  end

  defp process_docs(doc, fields, meta) do
    template = doc_template(meta)
    doc = Map.take(doc, fields)

    template
    |> Map.merge(doc)
    |> Map.values
  end

  defp doc_template(fields) do
    fields
    |> Enum.reduce(%{}, fn({key, type}, acc)->
         Map.put(acc, "#{key}", default_value(type))
       end)
  end

  defp default_value(:string), do: ""
  defp default_value(:integer), do: 0
  defp default_value(:boolean), do: false
  defp default_value({:array, _}), do: []
  defp default_value(:map), do: %{}
  defp default_value({:map, _}), do: %{}
  defp default_value(:binary_id), do: ""
end