Archeometer.Query (Archeometer v0.5.0)
This module exposes a from/2
macro to creates queries for the Archeometer
database. The recommended way to use it is to import the whole module. Let's
see a simple example!
import Archeometer.Query
alias Archeometer.Schema.Module
# then you can use it to make queries!
Archeometer.Repo.all(
from m in Module,
select: m.name,
where: m.num_lines > 50
)
The first argument must be an expression in the form of m in Module
, where
m
will be the prefix used for all the fields in the query, and Module
is
a module implementing Archeometer.Schema
.
The available schemas are
Archeometer.Schema.Module
Archeometer.Schema.Function
Archeometer.Schema.Macro
Archeometer.Schema.XRef
Archeometer.Schema.App
Each schema specifies its fields and how it is related to the other schemas, like in an SQL Database!
query-keywords
Query keywords
The rest of the arguments specify how to construct the query. Each option is mapped to an SQL keyword. They are
:select
:where
:order_by
:group_by
:having
:limit
Each option accepts either an Archeometer.Query.Term
expression or an
Archeometer.Query.Term.Container
container (tuples, lists or maps).
For keyword lists and maps, the keys can be used as aliases in the rest of
the query.
In the case of the order_by
, they keys are used to determine the order.
import Archeometer.Query
alias Archeometer.Schema.Module
from m in Module,
select: [app: m.app.name, avg_cc: avg(m.cc)]
where: m.app == "archeometer"
order_by: [desc: sum(m.num_lines)],
group_by: m.app.name
having: avg_cc > 5,
limit: 10
fields-and-tables
Fields and tables
Only fields can be selected. Trying to select reference to other tables will return an error. For example
import Archeometer.Query
alias Archeometer.Schema.Module
# this will fail because `functions` is a table
from m in Module, select: m.functions
# this works because `name` is a field
from m in Module, select: m.functions.name
more-examples
More Examples
like
operator with named columnsiex> from f in Archeometer.Schema.Function, ...> select: [name: f.name, arity: f.num_args], ...> where: arity > 3, ...> where: like(f.module.name, "Kamaji.Web.%")
Boolean operators
iex> from m in Archeometer.Schema.Module, ...> where: like(m.name, "%") > 5 and sum(m.functions.cc) > 5 or not m.num_lines < 500, ...> select: [m.name, m.app.name]
Grouping with nested aggregation
iex> from m in Archeometer.Schema.Module, ...> group_by: m.id, ...> having: max_cc > 5, ...> select: [module: m.name, max_cc: max(m.functions.cc)] iex> from m in Archeometer.Schema.Module, ...> group_by: name, ...> select: [name: m.name, num_deps: count(m.out_refs.callee.name)] iex> from m in Archeometer.Schema.Module, ...> group_by: name, ...> select: [name: m.name, num_usages: count(m.in_refs.caller.name)]
not
andis_nil
iex> from f in Archeometer.Schema.Function, ...> where: not is_nil(f.coverage), ...> select: [f.module.name, f.name, f.num_args]
A more complex query
iex> from m in Archeometer.Schema.Module, ...> select: [name: m.name, acc_cc: sum(m.functions.cc)], ...> group_by: name, ...> where: m.num_lines > 100 and m.coverage < 0.9, ...> where: not exists(m.macros.name), ...> order_by: [desc: acc_cc], ...> limit: 10