Packages

Ecdo is a dynamic interface for ecto aims to simplify building dynamic query API based on ecto models.

Current section

Files

Jump to
ecdo lib ecdo.ex
Raw

lib/ecdo.ex

defmodule Ecdo do
@moduledoc """
Provides the Query function.
Queries are used to retrieve and manipulate data in a repository (see
Ecto.Repo). Although this module provides a complete API, supporting
expressions like `where`, `select` and so forth.
"""
@derive [Access]
defstruct [sources: %{}, modules: %{}, count: 0, param: nil, query: %Ecto.Query{}]
@doc """
Ecdo is dynamic query interface to ecto. It implements `Ecto.Queryable` protocol, and can be
mixed and used with other ecto queries.
Let's see an example:
defmodule Weather do
use Ecto.Model
# weather is the DB table
schema "weather" do
field :city, :string
field :temp_lo, :integer
field :temp_hi, :integer
field :prcp, :float, default: 0.0
end
end
## Query
Ecdo simplify you to write dinamic query engines and send
them to the repository, which translates them to the underlying database.
Let's see an example:
import Ecdo, only: [query: 2]
query {"w", Weather}, %{where: "w.prcp > 0"}
# Returns %Weather{} structs matching the query
Repo.all(query)
For easy of use, keywords can be as binaries or as atoms. select can be defined as
list (`["w.id", "w.name"]`) or as string (`"w.id, w.name"`). All existing functionality
is build to be easy used from CLI, as all of arguments can be unparsed strings. Ecdo take
care of it.
The supported keywords are:
* `:distinct`
* `:where`
* `:order_by`
* `:offset`
* `:limit`
* `:join`
* `:select`
* `:preload`
Let's see more examples:
query {"w", Weather}, %{where: "w.prcp > 0", distinct: true}
query {"w", Weather}, %{"where" => "w.prcp > 0", "order_by" => "w.id"}
query {"w", Weather}, %{where: "w.prcp > 0", limit: 10, offset: 10}
query {"w", Weather}, %{where: "w.prcp > 0", select: "w.prpc"}
query {"w", Weather}, %{where: "w.prcp > 0", preload: ["city"]}
query {"w", Weather}, %{where: "w.prcp > 0 and city.id < 10", join: "city"}
query {"w", Weather}, %{where: "w.prcp > 0 and city.id < 10", join: "city, foobar"}
"""
def query(source, query) do
query = Map.keys(query) |> Enum.reduce(%{}, &check_string(&1, query, &2))
%Ecdo{} |> Ecdo.Builder.From.apply(source)
|> Ecdo.Builder.Join.apply(query)
|> Ecdo.Builder.Where.apply(query)
|> Ecdo.Builder.Select.apply(query)
|> Ecdo.Builder.OrderBy.apply(query)
|> Ecdo.Builder.QueryExpr.apply(query)
|> Ecdo.Builder.Load.apply(query)
end
@keys [:where, :select, :select_as, :count, :avg, :sum, :min, :max, :limit, :offset, :distinct,
:order_by, :load, :preload, :left_join, :right_join, :full_join, :join]
for key <- @keys do
defp check_string(unquote(key), %{unquote(key) => value}, acc),
do: Map.put(acc, unquote(key), value)
defp check_string(unquote(to_string(key)), %{unquote(to_string(key)) => value}, acc),
do: Map.put(acc, unquote(key), value)
end
defp check_string(_key, _, acc), do: acc
end
defimpl Ecto.Queryable, for: Ecdo do
def to_query(ecdo), do: ecdo.query
end