Current section
Files
Jump to
Current section
Files
lib/selecto/schema/join.ex
defmodule Selecto.Schema.Join do
# selecto meta join can edit, add, alter this join!
@doc """
# Join Types ideas
Done
- dimension - this type of join the local table has an ID that points to a table with an ID and name and that name could easily just be a member of this table. So we will make special filters and columns.
Planned
- self - Joining into self we only want to grab columns that are asked for
- through - this type of join has interesting tables on both sides, but probably nothing interesting in itself. Let's SKIP adding columns from this table unless they are requested in the columns map
- parameterized - these are joins that can be repeated with different rows on the far side, like a flag or tag table
...
- one_to_one - Like a lookup but there are more interesting cols on the far side, we will treat normally. Also, default
- one_to_many - Will treat like a one to one
- belongs_to - Will treat like one-to-one
"""
defp normalize_joins(source, domain, joins, dep) do
# IO.inspect(joins, label: "Normalize")
Enum.reduce(joins, [], fn
{id, %{non_assoc: true} = config}, acc ->
acc = acc ++ [Selecto.Schema.Join.configure(id, config, dep, source)]
case Map.get(config, :joins) do
nil -> acc
_ -> acc ++ normalize_joins(config.source, domain, config.joins, id)
end
{id, config}, acc ->
### Todo allow this to be non-configured assoc
association = source.__schema__(:association, id)
acc = acc ++ [Selecto.Schema.Join.configure(id, association, config, dep, source)]
case Map.get(config, :joins) do
nil -> acc
_ -> acc ++ normalize_joins(association.queryable, domain, config.joins, id)
end
end)
end
# we consume the join tree (atom/list) to a flat map of joins then into a map
def recurse_joins(source, domain) do
normalize_joins(source, domain, domain.joins, :selecto_root)
|> List.flatten()
|> Enum.reduce(%{}, fn j, acc -> Map.put(acc, j.id, j) end)
end
#### Non-assoc joins
def configure(id, config, dep, from_source) do
end
## TODO this does not work yet!
def configure(id, %{through: through} = association, config, dep, from_source) do
### we are going to expand the through but only add the
##??????
end
### Custom TODO
# def configure(id, %{type: :custom} = config, dep) do
# ### this join does not have an association
# end
### Dimension table join
def configure(id, %{queryable: queryable} = association, %{type: :dimension} = config, dep, from_source) do
#dimension table, has one 'name-ish' value to display, and then the Local reference would provide ID filtering.
# So create a field for group-by that displays NAME and filters by ID
name = Map.get(config, :name, id)
from_field = case dep do
:selecto_root -> "#{association.owner_key}"
_ -> "#{dep}[#{association.owner_key}]"
end
config = Map.put(config, :custom_columns, Map.get(config, :custom_columns, %{}) |> Map.put(
"#{id}", %{ ## we will use the nane of the join's association!
name: name,
### concat_ws?
select: "#{association.field}[#{config.dimension_value}]",
### we will always get a tuple of select + group_by_filter_select here
group_by_format: fn {a, _id}, _def -> a end,
group_by_filter: from_field,
group_by_filter_select: ["#{association.field}[#{config.dimension_value}]", from_field ]
}
)
)
%{
dep: dep,
config: config,
from_source: from_source,
owner_key: association.owner_key,
my_key: association.related_key,
source: association.queryable.__schema__(:source),
id: id,
name: name,
## probably don't need 'where'
requires_join: dep,
filters: Map.get(config, :filters, %{}),
fields:
Selecto.Schema.Column.configure_columns(
association.field,
[config.dimension_value],
association.queryable,
config
)
} |> parameterize()
end
### Regular
def configure(id, association, config, dep, from_source) do
std_config(id, association, config, dep, from_source)
end
defp min_config(id, %{queryable: queryable} = association, config, dep, from_source) do
%{
dep: dep,
config: config,
from_source: from_source,
owner_key: association.owner_key,
my_key: association.related_key,
source: association.queryable.__schema__(:source),
id: id,
name: Map.get(config, :name, id),
## probably don't need 'where'
requires_join: dep,
filters: make_filters(config),
} |> parameterize()
end
defp std_config(id, %{queryable: queryable} = association, config, dep, from_source) do
%{
dep: dep,
config: config,
from_source: from_source,
owner_key: association.owner_key,
my_key: association.related_key,
source: association.queryable.__schema__(:source),
id: id,
name: Map.get(config, :name, id),
## probably don't need 'where'
requires_join: dep,
filters: make_filters(config),
fields:
Selecto.Schema.Column.configure_columns(
association.field,
association.queryable.__schema__(:fields) --
association.queryable.__schema__(:redact_fields),
association.queryable,
config
)
} |> parameterize()
end
defp parameterize(join) do
join
end
defp make_filters(config) do
Map.get(config, :filters, %{})
end
end