# FAT
## Description
FAT provides methods for _dynamically_ building queries depending on the parameters it receive.
Currently it's supporting following **query functions**:
- where
- select
- joins
- order_by
- include
- group_by
## Installation
### For now, you can add directly via github
```elixir
{:fat_ecto, github: "tanweerdev/fat_ecto"}
```
## Config
```elixir
config :fat_ecto, :repo,
seed_base_path: "priv/seed/csvs/",
base_repo: ExApi.Repo,
query_repo: ExApi.Repo,
default_limit: 10,
max_limit: 100
```
If [available in Hex](https://hex.pm/docs/publish), the package can be installed by adding `fat_ecto` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:fat_ecto, "~> 0.1.0"}
]
end
```
## Use
Once installed you can _import_ **FatEcto.FatQuery** inside your module and use the `build method`. Which is the entry method for building every query. And also the main method for the **FatEcto.FatQuery**.
```elixir
build(schema_name, params)
```
#### Example
```eliixir
import FatEcto.FatQuery
query_opts = %{
"$select" => %{
"$fields" => ["name", "location", "rating"],
"fat_rooms" => ["beds", "capacity"]
},
"$order" => %{"id" => "$desc"},
"$where" => %{"rating" => 4},
"$group" => ["total_staff", "rating"],
"$include" => %{
"fat_doctors" => %{
"$include" => ["fat_patients"],
"$where" => %{"name" => "ham"},
"$order" => %{"id" => "$desc"},
"$join" => "$right"
}
},
"$right_join" => %{
"fat_rooms" => %{
"$on_field" => "id",
"$on_join_table_field" => "hospital_id",
"$select" => ["beds", "capacity", "level"],
"$where" => %{"incharge" => "John"},
"$order" => %{"level" => "$asc"}
}
}
}
iex> build(FatEcto.FatHospital, query_opts)
iex> #Ecto.Query<from f0 in FatEcto.FatHospital, right_join: f1 in "fat_rooms",
on: f0.id == f1.hospital_id, right_join: f2 in assoc(f0, :fat_doctors),
where: f0.rating == ^4 and ^true, where: f1.incharge == ^"John" and ^true,
group_by: [f0.total_staff], group_by: [f0.rating], order_by: [asc: f1.level],
order_by: [desc: f0.id],
select: merge(map(f0, [:name, :location, :rating, :id, {:fat_rooms, [:beds, :capacity]}]),
%{^:fat_rooms => map(f1, [:beds, :capacity, :level])}),
preload: [fat_doctors: #Ecto.Query<from f0 in FatEcto.FatDoctor,
left_join: f1 in assoc(f0, :fat_patients),
where: f0.name == ^"ham" and ^true, order_by: [desc: f0.id],
limit: ^10, offset: ^0, preload: [:fat_patients]>]>
```
##### Options:
These are the options supported
| Option | Description |
| ----------------------- | ------------------------------------------------------------------------ |
| $include | Include the assoication model `doctors` |
| $include: :fat_patients | Include the assoication `patients`. Which has association with `doctors` |
| $select | Select the fields from `hospital` and `rooms` |
| $where | Added the where attribute in the query |
| $group | Added the group_by attribute in the query as a list |
| $order | Sort the result based on the order attribute |
| $right_join | Specify the type of join |
| $on_field | Specify the field for join |
| $on_join_table_field | Specify the field for join in the joining table |
see docs for more details.
### Where
_Where_ include methods from [Ecto.Query.API](https://hexdocs.pm/ecto/Ecto.Query.API.html). These are the options _where_ supports.
| function | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------------ |
| like | matches the substring with the attribute in the database. `"$like"` . |
| notLike | return result where value in the substring doesn't match. `"$not_like"` . |
| ilike | matches the substring passed with the attribute in the database `"$ilike"` . |
| notILike | return result where value in the substring doesn't match.`"$not_ilike"`. |
| lessthan | fetch result where value is less than the numerical value `"$lt"` (also apply on non_numerical fields). |
| lessthanequal | fetch result where value is less than equal to the numerical value `"$lte"` (also apply on non_numerical fields. |
| greaterthan | fetch result where value is greater than the numerical value `"$gt`" (also apply on non_numerical fields.) |
| greaterthanequal | fetch result where value is greater than equal to the numerical value `"gte"` (also apply on non_numerical fields. |
| between | [] ,fetch the result wher value is between the provided attributes. `"$between"`. |
| notBetween | [] . fetch the result wher value is not between the provided list attributes. `"$not_between"`. |
| in | [] , fetch result where value is in the the provided list attributes. `"$in"` . |
| notIn | [] ,fetch result where value is not in the the provided list attributes. `"$not_in"` . |
| isnil | value is nil. `"nil"`. |
| not isnil | value is not nil. `"$not_null"` . |
#### Example:
```elixir
query_ opts = %{
"$where" => %{"designation" => %{"$ilike" => "%surge %"}}
}
iex> build(FatEcto.FatDoctor, query_opts)
iex> #Ecto.Query<from f in FatEcto.FatDoctor,
where: ilike(fragment("(?)::TEXT", f.designation), ^"%surge %") and ^true>
query_ opts = %{
"$where" => %{"rating" => %{"$lte" => "$total_staff"}}
}
iex> build(FatEcto.FatHospital, opts)
iex> #Ecto.Query<from f in FatEcto.FatHospital,
where: f.rating <= f.total_staff and ^true>
```
see docs for more info
### Select
_Select_ include fields from the models as well as from the associated models and also adds foreign key dynamically.
It also select fields from the model as a list.
#### Example:
```elixir
query_opts = %{
"$select" => ["name", "location", "rating"],
"$order" => %{"id" => "$desc"}
}
iex> build(FatEcto.FatHospital, query_opts)
iex> #Ecto.Query<from f in FatEcto.FatHospital, order_by: [desc: f.id],
select: map(f, [:name, :location, :rating])>
query_ opts = %{
"$select" => %{
"$fields" => ["name", "location", "rating"],
"fat_rooms" => ["beds", "capacity"]
},
"$where" => %{"id" => 2}
}
iex> build(FatEcto.FatHospital, query_opts)
iex> #Ecto.Query<from f in FatEcto.FatHospital, where: f.id == ^2 and ^true,
select: map(f, [:name, :location, :rating, :id, {:fat_rooms, [:beds, :capacity]}])>
```
see docs for more info
### Joins
_Joins_ with another table on the type of join you passed in the params. it also supports _where_ , _select_, _order_ . Supported Join types are:
- inner
- left
- right
- full
#### Example
```elixir
query_opts = %{
"$right_join" => %{
"fat_rooms" => %{
"$on_field" => "id",
"$on_join_table_field" => "hospital_id",
"$select" => ["beds", "capacity", "level"],
"$where" => %{"incharge" => "John"},
"$order" => %{"nurses" => "$asc"}
}
}
}
iex> build(FatEcto.FatHospital, opts)
iex> Ecto.Query<from f0 in FatEcto.FatHospital, right_join: f1 in "fat_rooms",
on: f0.id == f1.hospital_id, where: f1.incharge == ^"John" and ^true,
order_by: [asc: f1.nurses],
select: merge(f0, %{^:fat_rooms => map(f1, [:beds, :capacity, :level])})>
```
see docs for more info.
### Order_by
_order_by_ returns the query by sort the results as _asc_ or _desc_ order.
#### Example
```elixir
query_opts = %{
"$select" => %{
"$fields" => ["name", "location", "rating"],
"fat_rooms" => ["beds", "capacity"]
},
"$order" => %{"id" => "$desc"}
}
iex> build(FatEcto.FatHospital, opts)
iex> #Ecto.Query<from f in FatEcto.FatHospital, order_by: [desc: f.id],
select: map(f, [:name, :location, :rating, :id, {:fat_rooms, [:beds, :capacity]}])>
query_ opts = %{
"$select" => ["name", "location", "rating"],
"$order" => %{"id" => "$asc"}
}
iex> build(FatEcto.FatHospital, query_opts)
iex> #Ecto.Query<from f in FatEcto.FatHospital, order_by: [asc: f.id],
select: map(f, [:name, :location, :rating])>
```
see docs for more info.
### Include
_include_ has different sub methods. You can specify the _where_ , _order_by_ , _join_ in the include for the associated schema.
#### Example
```elixir
query_opts = %{
"$include" => %{
"fat_hospitals" => %{
"$join" => "$right",
"$order" => %{"id" => "$desc"},
"$where" => %{"name" => "Saint"}
}
},
"$where" => %{"name" => "John"}
}
iex> build(FatEcto.FatDoctor, opts)
iex> #Ecto.Query<from f0 in FatEcto.FatDoctor,
right_join: f1 in assoc(f0, :fat_hospitals),
where: f0.name == ^"John" and ^true,
preload: [fat_hospitals: #Ecto.Query<from f in FatEcto.FatHospital,
where: f.name == ^"Saint" and ^true, order_by: [desc: f.id], limit: ^10, offset: ^0>]>
```
see docs for more info.
### Group_By
_group_by_ Groups together rows from the schema that have the same values in the given fields. It also supports multiple _group_by_ fields in a list.
#### Example
```elixir
query_opts = %{
"$inner_join" => %{
"fat_rooms" => %{
"$on_field" => "id",
"$on_join_table_field" => "hospital_id",
"$select" => ["beds", "capacity", "level"],
"$where" => %{"incharge" => "John"}
}
},
"$where" => %{"rating" => 3},
"$group" => ["rating", "total_staff"]
}
iex> build(FatEcto.FatHospital, query_opts)
iex> #Ecto.Query<from f0 in FatEcto.FatHospital, join: f1 in "fat_rooms",
on: f0.id == f1.hospital_id, where: f0.rating == ^3 and ^true,
where: f1.incharge == ^"John" and ^true, group_by: [f0.rating],
group_by: [f0.total_staff],
select: merge(f0, %{^:fat_rooms => map(f1, [:beds, :capacity, :level])})>
query_opts = %{
"$select" => ["name", "location", "rating"],
"$order" => %{"id" => "$asc"},
"$group" => "rating"
}
iex> build(FatEcto.FatHospital, query_opts)
iex> #Ecto.Query<from f in FatEcto.FatHospital, group_by: [f.rating],
order_by: [asc: f.id], select: map(f, [:name, :location, :rating])>
```
see docs for more info.
### Paginator
_FAT_ allows to restrict the number of results you want to return from the repo. You can define _limit_ as limit and _offset_ as skip.
#### Example
```elixir
opts = %{
"$select" => ["name", "location", "rating"],
"$order" => %{"id" => "$asc"},
"$group" => "rating",
"$limit" => 20,
"$skip" => 0
}
```
If no limit is defined then FAT automatically uses `default_limit`. For this to work you have to define the default_limit in fat_ecto config.
see docs for more info.
## Documentation
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/fat_ecto](https://hexdocs.pm/fat_ecto).