json_api_query_builder v1.0.2 JsonApiQueryBuilder behaviour View Source
Behaviour and mixin for building an Ecto query from a JSON-API request.
Example
defmodule Article do
use Ecto.Schema
schema "articles" do
field :body, :string
field :description, :string
field :slug, :string
field :tag_list, {:array, :string}
field :title, :string
belongs_to :author, User, foreign_key: :user_id
has_many :comments, Comment
timestamps()
end
defmodule Query do
use JsonApiQueryBuilder,
schema: Article,
type: "article",
relationships: ["author", "comments"]
@impl JsonApiQueryBuilder
def filter(query, "tag", value), do: from(a in query, where: ^value in a.tag_list)
def filter(query, "comments", params) do
comment_query = from(Comment, select: [:article_id], distinct: true) |> Comment.Query.filter(params)
from a in query, join: c in ^subquery(comment_query), on: a.id == c.article_id
end
def filter(query, "author", params) do
user_query = from(User, select: [:id]) |> User.Query.filter(params)
from a in query, join: u in ^subquery(user_query), on: a.user_id == u.id
end
@impl JsonApiQueryBuilder
def include(query, "comments", comment_params) do
from query, preload: [comments: ^Comment.Query.build(comment_params)]
end
def include(query, "author", author_params) do
from query, select_merge: [:author_id], preload: [author: ^User.Query.build(author_params)]
end
end
end
Link to this section Summary
Callbacks
Builds an Ecto.Queryable.t
from parsed JSON-API request parameters
Optional callback responsible for mapping a JSON-API field string to an Ecto schema field
Applies sparse fieldset selection from a parsed JSON-API request to an Ecto.Queryable.t
Applies filter conditions from a parsed JSON-API request to an Ecto.Queryable.t
Callback responsible for adding a filter criteria to a query
Applies related resource inclusion from a parsed JSON-API request to an Ecto.Queryable.t
as preloads
Callback responsible for adding an included resource via preload
Applies sorting from a parsed JSON-API request to an Ecto.Queryable.t
Link to this section Types
A JSON-API request after parsing by Plug into a string keyed map.
May contain "filter"
, "sort"
, "fields"
, "include"
, "page"
keys.
Link to this section Callbacks
Builds an Ecto.Queryable.t
from parsed JSON-API request parameters.
An overridable default implementation is generated by the mixin.
Example:
User.Query.build(%{
"filter" => %{
"articles.tag" => "animals",
"comments" => %{
"body" => "Boo"
}
},
"include" => "articles.comments",
"fields" => %{"user" => "id,bio"}
})
#Ecto.Query<
from u in Blog.User,
join: a in ^#Ecto.Query<
from a in subquery(
from a in Blog.Article,
where: ^"animals" in a.tag_list,
distinct: true,
select: [:user_id]
)
>,
on: u.id == a.user_id,
select: [:id, :bio, :id],
preload: [
articles: #Ecto.Query<
from a in Blog.Article,
select: [:id, :body, :description, :slug, :tag_list, :title, :user_id, :inserted_at, :updated_at],
preload: [
comments: #Ecto.Query<
from c in Blog.Comment,
select: [:id, :body, :user_id, :article_id, :inserted_at, :updated_at]
>
]
>
]
>
Optional callback responsible for mapping a JSON-API field string to an Ecto schema field.
An overridable default implementation using String.to_existing_atom/1
is generated by the mixin.
Example
@impl JsonApiQueryBuilder
def field("username"), do: :name
def field("price"), do: :unit_price
def field(other), do: String.to_existing_atom(other)
fields(query :: Ecto.Queryable.t(), request()) :: Ecto.Queryable.t()
Applies sparse fieldset selection from a parsed JSON-API request to an Ecto.Queryable.t
An overridable default implementation is generated by the mixin.
By default all fields are selected unless specified in the "fields"
key of the request.
filter(query :: Ecto.Queryable.t(), request()) :: Ecto.Queryable.t()
Applies filter conditions from a parsed JSON-API request to an Ecto.Queryable.t
An overridable default implementation is generated by the mixin.
filter(query :: Ecto.Queryable.t(), field :: String.t(), value :: any()) :: Ecto.Queryable.t()
Callback responsible for adding a filter criteria to a query.
Attribute filters will generally add a where:
condition to the query.
Relationship filters will generally add a join:
based on a subquery.
When applying a filter to a has-many relationship, take care to select:
the foreign key with distinct: true
to avoid duplicated results.
For filtering a belongs-to relationships, selecting the primary key is all that is needed.
Example
@impl JsonApiQueryBuilder
def filter(query, "tag", value), do: from(article in query, where: ^value in article.tag_list)
def filter(query, "comments", params) do
comment_query = from(Comment, select: [:article_id], distinct: true) |> Comment.Query.filter(params)
from article in query, join: comment in ^subquery(comment_query), on: article.id == comment.article_id
end
def filter(query, "author", params) do
user_query = from(User, select: [:id]) |> User.Query.filter(params)
from article in query, join: user in ^subquery(user_query), on: article.user_id == user.id
end
include(query :: Ecto.Queryable.t(), request()) :: Ecto.Queryable.t()
Applies related resource inclusion from a parsed JSON-API request to an Ecto.Queryable.t
as preloads.
An overridable default implementation is generated by the mixin.
include(query :: Ecto.Queryable.t(), relationship :: String.t(), related_request :: request()) :: Ecto.Queryable.t()
Callback responsible for adding an included resource via preload
.
Any required foreign keys should be added to the query using select_merge:
as required by the preload.
Example
@impl JsonApiQueryBuilder
def include(query, "comments", comment_params) do
from query, preload: [comments: ^Comment.Query.build(comment_params)]
end
def include(query, "author", author_params) do
from query, select_merge: [:user_id], preload: [author: ^User.Query.build(author_params)]
end
sort(query :: Ecto.Queryable.t(), request()) :: Ecto.Queryable.t()
Applies sorting from a parsed JSON-API request to an Ecto.Queryable.t
An overridable default implementation is generated by the mixin.