Current section
Files
Jump to
Current section
Files
lib/query_builder/schema.ex
defmodule EctoShorts.QueryBuilder.Schema do
@moduledoc """
This module contains query building parts for schemas themselves,
when passed a query it can pull the schema from it and attempt
to filter on any natural field
"""
require Logger
require Ecto.Query
alias EctoShorts.QueryBuilder
alias EctoShorts.QueryBuilder.Schema.ComparisonFilter
@behaviour QueryBuilder
@impl QueryBuilder
def create_schema_filter({filter_field, val}, query) do
create_schema_filter(
{filter_field, val},
QueryBuilder.query_schema(query),
query
)
end
def create_schema_filter({filter_field, val}, schema, query) do
cond do
filter_field in schema.__schema__(:query_fields) ->
create_schema_query_field_filter(query, schema, filter_field, val)
filter_field in schema.__schema__(:associations) ->
relational_schema = get_associated_schema_from_field(schema, filter_field)
create_schema_assocation_filter(query, filter_field, val, schema, relational_schema)
true ->
Logger.debug("[EctoShorts] #{Atom.to_string(filter_field)} is neither a field nor has a valid association for #{schema.__schema__(:source)} where filter")
query
end
end
defp get_associated_schema_from_field(schema, field_key) do
association = schema.__schema__(:association, field_key)
case association do
%Ecto.Association.HasThrough{
through: [field1, field2]
} ->
schema
|> get_associated_schema_from_field(field1)
|> get_associated_schema_from_field(field2)
%{related: related} ->
related
_ ->
raise ArgumentError, message: "#{Atom.to_string(field_key)} does not have an associated schema for #{schema.__schema__(:source)}"
end
end
defp create_schema_query_field_filter(query, schema, filter_field, val) do
case schema.__schema__(:type, filter_field) do
{:array, _} ->
ComparisonFilter.build_array(query, schema.__schema__(:field_source, filter_field), val)
_ ->
ComparisonFilter.build(query, schema.__schema__(:field_source, filter_field), val)
end
end
defp create_schema_assocation_filter(_query, filter_field, _val, schema, nil) do
raise ArgumentError, message: "#{Atom.to_string(filter_field)} does not have an associated schema for #{schema.__schema__(:source)}"
end
defp create_schema_assocation_filter(query, filter_field, val, _schema, relational_schema) do
binding_alias = :"ecto_shorts_#{filter_field}"
query
|> Ecto.Query.with_named_binding(binding_alias, fn query, binding_alias ->
Ecto.Query.join(
query,
:inner,
[scm],
assoc in assoc(scm, ^filter_field), as: ^binding_alias)
end)
|> ComparisonFilter.build_relational(binding_alias, val, relational_schema)
end
end