diff --git a/lib/ayesql.ex b/lib/ayesql.ex index 2f76415..0b48b2b 100644 --- a/lib/ayesql.ex +++ b/lib/ayesql.ex @@ -131,6 +131,38 @@ defmodule AyeSQL do ] } ``` + AyeSQL also allows you to choose the type of returned data structures. + Instead of the default map you can also pass an `into` option to your query + possible values are: + - an empty map: `Map.new()` or `%{}` + - an empty list: Keyword.new()` or `[]` + - a struct + - `:raw` - will return unmodified Postgrex result + + ```elixir + iex> Queries.get_avg_clicks(params, into: []) + {:ok, + [ + [day: ..., count: ...], + [day: ..., count: ...], + [day: ..., count: ...], + ... + ] + } + ``` + + ```elixir + iex> defmodule AvgClicks do defstruct [:day, :count] end + iex> Queries.get_avg_clicks(params, into: AvgClicks) + {:ok, + [ + %AvgClicks{day: ..., count: ...}, + %AvgClicks{day: ..., count: ...}, + %AvgClicks{day: ..., count: ...}, + ... + ] + } + ``` """ alias AyeSQL.Compiler alias AyeSQL.Query diff --git a/lib/ayesql/runner.ex b/lib/ayesql/runner.ex index d9889fb..a14561e 100644 --- a/lib/ayesql/runner.ex +++ b/lib/ayesql/runner.ex @@ -21,22 +21,33 @@ defmodule AyeSQL.Runner do # Handles the result. @doc false - @spec handle_result(map()) :: [map() | struct()] - @spec handle_result(map(), keyword()) :: [map() | struct()] + @spec handle_result(map()) :: [map() | struct() | keyword()] + @spec handle_result(map(), keyword()) :: [map() | struct() | keyword()] def handle_result(result, options \\ []) + def handle_result(data, options) when is_list(options) do + handle_result(data, Map.new(options)) + end + + def handle_result(raw_data, %{into: :raw}) do + raw_data + end + def handle_result(%{columns: nil}, _options) do [] end def handle_result(%{columns: columns, rows: rows}, options) do - struct = options[:into] - columns = Enum.map(columns, &String.to_atom/1) + atom_columns = Stream.map(columns, &String.to_atom/1) rows - |> Stream.map(&Stream.zip(columns, &1)) + |> Stream.map(&Stream.zip(atom_columns, &1)) |> Enum.map(fn row -> - if struct, do: struct(struct, row), else: Map.new(row) + case options[:into] do + struct when is_struct(struct) -> struct(struct, row) + [] -> Enum.into(row, []) + _ -> Enum.into(row, %{}) + end end) end end diff --git a/test/ayesql/runner_test.exs b/test/ayesql/runner_test.exs index 810686a..3fca0b5 100644 --- a/test/ayesql/runner_test.exs +++ b/test/ayesql/runner_test.exs @@ -3,6 +3,10 @@ defmodule AyeSQL.RunnerTest do alias AyeSQL.Runner + defmodule User do + defstruct [:username, :email] + end + describe "handle_result/1" do test "when columns are nil, returns empty list" do data = %{columns: nil} @@ -10,7 +14,7 @@ defmodule AyeSQL.RunnerTest do assert [] = Runner.handle_result(data) end - test "when rows are not empty, returns a list of rows with columns" do + test "when rows are not empty, returns a list of map rows by default" do data = %{ columns: ["username", "email"], rows: [ @@ -24,7 +28,53 @@ defmodule AyeSQL.RunnerTest do %{username: "alice", email: "alice@example.com"} ] - assert ^expected = Runner.handle_result(data) + assert ^expected = Runner.handle_result(data, into: %{}) + end + + test "when rows are not empty, returns a list of keyword rows" do + data = %{ + columns: ["username", "email"], + rows: [ + ["bob", "bob@example.com"], + ["alice", "alice@example.com"] + ] + } + + expected = [ + [username: "bob", email: "bob@example.com"], + [username: "alice", email: "alice@example.com"] + ] + + assert expected == Runner.handle_result(data, into: []) + end + + test "when rows are not empty, returns a list of structs rows" do + data = %{ + columns: ["username", "email"], + rows: [ + ["bob", "bob@example.com"], + ["alice", "alice@example.com"] + ] + } + + expected = [ + %User{username: "bob", email: "bob@example.com"}, + %User{username: "alice", email: "alice@example.com"} + ] + + assert expected == Runner.handle_result(data, into: %User{}) + end + + test "when rows are not empty, returns a raw result with columns and rows" do + data = %{ + columns: ["username", "email"], + rows: [ + ["bob", "bob@example.com"], + ["alice", "alice@example.com"] + ] + } + + assert data == Runner.handle_result(data, into: :raw) end end end