Map a Postgrex Row to a Struct
Here's how to map a Postgrex wow to a struct.
Postgrex Rows
Postgrex is a postgres driver for Elixir. It's great for running raw sql against your Postgres database. Your selection/projection gets returned as rows/columns. The columns are returned as a list of strings. The rows are returned as a list of a list of various value types, depending on the database column type. What we want is to convert this data into a list of structs.
Our Struct
Let's say we have a struct for a book:
defmodule Book do
defstruct title: "", author: "", publish_date: ~D[0000-01-01]
Map the Result
Let's say that our query is selecting these same values, and we need to map the result. We'll use Postgrex.query!, but note that you can access the same functionality in Ecto via Ecto.Adapters.SQL.query!
Postgrex.query!(
MyProject.Repo,
"select title, author, publish_date from books",
[]
)
|> case do
%Postgrex.Result{columns: columns, rows: rows} ->
column_atoms = Enum.map(columns, &String.to_atom/1)
books =
rows
|> Enum.map(fn row ->
map_row(column_atoms, row)
end)
{:ok, books}
_ ->
{:error, "Failed to query books"}
end
# ...
defp map_row(column_atoms, row) do
book_keywords = Enum.zip(column_atoms, row)
book = struct!(MyProject.Book, book_keywords)
book
end
In the end, we have a list of Book structs. How is it done?
- First, we destructure the
Postgrex.Resultto access the columns and rows. - Next, we convert the columns (a list of strings) to a list of atoms.
- We loop through each row, mapping each
- For each row, we create a keyword list. For instance,
book_keywords = [{title: "War and Peace"}, {author: "Leo Tolstoy"}, {publish_date: ~D[1867-07-04]}]. - We pass this keyword list and the struct definition to the
struct!function, which is a built-in global.
And there you have it. 73 steps later, a list of structs.