Current section
Files
Jump to
Current section
Files
lib/result.ex
defmodule Monet.Result do
@moduledoc """
Represents the result from a query to MonetDB.
For a select `columns` are the column names and `rows` is a list of lists.
These can be accessed directly. However, the module also implements Enumerable
and Jason.Encode. By default, Enumerationa and Jason.Encode will expose the list
of lists as-is. However, the Result can be configured to return a list of maps
(optionally with atom keys). See `Monet.as_map/1` and `Monet.as_map/2` for
more information.
`last_id` is non-nil in the case of an insert to a table with an auto incremental
column (e.g. serial) and nil in all other cases.
`row_count` represents either the number of affected rows (for an update or
delete) or the number of `rows` (for a select).
Responses from the MonetDB server generally include some meta data, such as
timing information. This data isn't useful to this driver, but it's exposed in
in the `meta` field, in case it's useful to the caller. This data is unparsed;
it's binary field.
"""
alias __MODULE__
defstruct [
:mode,
:meta,
:rows,
:last_id,
:columns,
:row_count,
]
@doc """
Creates a new Result from a select or other queries that return data
"""
def new(header, columns, rows, count) do
%Result{meta: header, columns: columns, rows: rows, row_count: count}
end
@doc """
Creates a new Result with only a meta field (the type of result you'd get
from a create table, for example)
"""
def meta(meta), do: upsert(meta, 0, nil)
@doc """
Creates a new Result with a count and last_id, used by update/delete/insert
"""
def upsert(meta, count, last_id) do
%Result{meta: meta, columns: [], rows: [], row_count: count, last_id: last_id}
end
@doc """
Switches the the mode of the result to enumerate or jason encode maps. See
`Monet.as_map/1` and `Monet.as_map/2` for more information.
"""
def as_map(result, opts) do
case Keyword.get(opts, :columns) == :atoms do
false -> %Result{result | mode: :map}
true ->
%Result{result |
mode: :map,
columns: Enum.map(result.columns, &String.to_atom/1)
}
end
end
end
defimpl Enumerable, for: Monet.Result do
alias Monet.Result
def slice(result) do
{:ok, result.row_count, &Enum.slice(result.rows, &1, &2)}
end
def count(result), do: {:ok, result.row_count}
def member?(_result, _value), do: {:error, __MODULE__}
def reduce(_result, {:halt, acc}, _fun), do: {:halted, acc}
def reduce(result, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(result, &1, fun)}
def reduce(%{rows: []}, {:cont, acc}, _fun), do: {:done, acc}
def reduce(result, {:cont, acc}, f) do
[row | rows] = result.rows
map = create_row(result.mode, result.columns, row)
reduce(%Result{result | rows: rows}, f.(map, acc), f)
end
@doc false
# exposed so that Jason.Encoder can use it
def create_row(:map, columns, row), do: columns |> Enum.zip(row) |> Map.new()
def create_row(_, _columns, row), do: row
end
defimpl Jason.Encoder, for: Monet.Result do
alias Jason.Encoder
alias Enumerable.Monet.Result
def encode(%{row_count: 0}, _opts), do: "[]"
def encode(result, opts) do
mode = result.mode
columns = result.columns
[row | rows] = result.rows
first = Encoder.encode(Result.create_row(mode, columns, row), opts)
remainder = Enum.reduce(rows, [], fn (row, acc) ->
[acc, ',' , Encoder.encode(Result.create_row(mode, columns, row), opts)]
end)
['[', first, remainder, ']']
end
end