I'm currently working in a project using GraphQL with Absinthe. I have a problem with the query of multiple tables which has an arg (recieve from GraphQL variables) in the WHERE clause.
I want my SQL to be like this:
SELECT p.*
FROM posts p
JOIN categories c
ON p.category_id = c.id
WHERE c.name = <the-GraphQL-arg> AND p.deleted_at IS NULL AND p.is_published IS TRUE
And here is my Elixir codes: (the Post model) post.ex
schema "posts" do
field :body, :string
field :deleted_at, :date
field :description, :string
field :image_url, :string
field :is_published, :boolean, default: false
field :title, :string
belongs_to :category, MyPrj.Post.Category
timestamps()
end
(the Category model) category.ex
schema "categories" do
field :name, :string
field :image_url, :string
field :title, :string
has_many :posts, MyPrj.Post.Post
timestamps()
end
schema.ex
query do
@desc "Get a list of posts by category"
field :posts_by_category, list_of(:post) do
arg :order, type: :sort_order, default_value: :desc
arg :category_name, non_null(:string)
arg :limit, :integer
arg :offset, :integer
resolve &Resolvers.Posts.posts_by_category/3
end
end
resolvers/post.ex
def posts_by_category(_, args, _) do
{:ok, Post.list_posts_by_category!(args)}
end
(the Phoenix context) post.ex
def list_posts_by_category!(criteria) do
query = from p in Post,
join: c in Category,
on: p.category_id == c.id,
where: p.is_published == true and is_nil(p.deleted_at)
Enum.reduce(criteria, query, fn
{:limit, limit}, query ->
from q in query, limit: ^limit
{:offset, offset}, query ->
from q in query, offset: ^offset
{:order, order}, query ->
from q in query, order_by: [{^order, :inserted_at}, {^order, :id}]
{:category_name, category_name}, query ->
from q in query, where: q.name == ^category_name
end)
|> Repo.all()
end
My GraphQL query:
query postsByCategory {
postsByCategory(categoryName: "family", order: DESC) {
body
category {
id
title
}
id
title
}
}
But it returns an error like this:
[error] #PID<0.580.0> running MyPrjWeb.Endpoint (connection #PID<0.579.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /api
** (exit) an exception was raised:
** (Ecto.QueryError) lib/my_prj/post.ex:185: field `name` in `where` does not exist in schema MyPrj.Post.Post in query:
from p in MyPrj.Post.Post,
join: c in MyPrj.Post.Category,
on: p.category_id == c.id,
where: p.is_published == true and is_nil(c.deleted_at),
where: p.name == ^"family",
order_by: [desc: p.inserted_at, desc: p.id]
How can I get the field "name" (c.name) inside the Category into the query of the list_posts_by_category!(criteria) function?
Thank you very much!
Resolved it with these codes:
(the Phoenix context) post.ex
def list_posts_by_category!(criteria) do
query = from p in Post,
join: c in Category,
as: :category,
on: p.category_id == c.id,
where: p.is_published == true and is_nil(p.deleted_at)
Enum.reduce(criteria, query, fn
{:limit, limit}, query ->
from q in query, limit: ^limit
{:offset, offset}, query ->
from q in query, offset: ^offset
{:order, order}, query ->
from q in query, order_by: [{^order, :inserted_at}, {^order, :id}]
{:category_name, category_name}, query ->
from [q, category: c] in query, where: c.name == ^category_name
end)
|> Repo.all()
end