Hey @xgeek116 , quick note that you don’t need to fetch_query_params(conn).params in your controller action. Your query params will be in the params argument that you are ignoring in your function signature.
def index(conn, params) do
books = BooksHandler.list_books(params)
render(conn, "index.json", books: books)
end
In your list_books/1 function you can convert a map to a keyword list and pass it directly into the where query option:
def list_books(filters) when is_list(filters) do
Repo.all(from Book, where: ^filters)
end
def list_books(%{} = filters) do
keyword_filters =
Enum.map(filters, fn
{k, v} when is_binary(k) -> {String.to_existing_atom(k), v}
pair -> pair
end)
list_books(keyword_filters)
end
You need to parse the query params into a date struct to compare them and when you put the date in the query params, you need to do that in a way that is going to play nicely with query params and parsing. There is the Date.to/from_iso8601 functions available which might suit.
Enum.map(params, fn
{k, v} when is_binary(k) -> {String.to_existing_atom(k), v}
end)
Will create a keyword list with all query params and values and then the ecto query will use as it is, so if I pass in the query params, the param “created_before” for example with other params I will have something like :
But in the schema I don’t have “created_before” attribute so it will raise an error. I need a way to separte the date query params (“created_before” and “created_after”) from the other params and how to inject them in my Ecto query (add other where statements dynamically ?)
** (Protocol.UndefinedError) protocol Enumerable not implemented for #Ecto.Query<from p0 in Book, where: p0.X == ^"Y" and
(p0.Z == ^"W" and
)> of type Ecto.Query (a struct)
I have only updated your return :
def filter_and(params) do
where = Enum.reduce(
params,
true,
fn {key, value}, acc ->
[key, comparator] = case String.split(key, ":") do
[k, comp] -> [k, String.to_atom(comp)]
[k] -> [k, :eq]
end
atom_key = String.to_existing_atom(key)
case comparator do
:gt -> dynamic([q], field(q, ^atom_key) > ^value and ^acc)
:lt -> dynamic([q], field(q, ^atom_key) < ^value and ^acc)
:gte -> dynamic([q], field(q, ^atom_key) >= ^value and ^acc)
:lte -> dynamic([q], field(q, ^atom_key) <= ^value and ^acc)
:in -> dynamic([q], field(q, ^atom_key) in ^String.split(value, ",") and ^acc)
:ne -> dynamic([q], field(q, ^atom_key) != ^value and ^acc)
_ -> dynamic([q], field(q, ^atom_key) == ^value and ^acc)
end
end
)
where
end
And I call it :
def fetch_books(filters) do
try do
Book
|> where(^filters)
rescue
_ -> raise NotFound, message: "Books not found"
end
end