FatEcto.Query.Dynamics.Buildable behaviour (FatEcto v1.4.0)
View SourceBuilds queries after filtering fields based on user-provided filterable and overrideable fields.
This module provides functionality to filter Ecto queries using predefined filterable fields (handled by Builder)
and overrideable fields (handled by a fallback function).
Options
filterable: A keyword list of fields and their allowed operators. Supports nested structures for filtering on joined/associated tables. Example: [
]id: ["$EQUAL", "$NOT_EQUAL"], name: ["$ILIKE"], # Nested = join filter (association name must match `as:` binding) classifications: [ subject_id: ["$EQUAL"], grade_id: ["$EQUAL", "$IN"] ]overrideable: A list of fields that can be overridden. Example: ["name", "phone"]ignoreable: A map of fields and their ignoreable values. Example: [
]"name" => ["%%", "", [], nil], "phone" => ["%%", "", [], nil]default_dynamic: When set to:return_true, returnsdynamic([q], true)instead ofnilwhen no dynamics are built. Example:default_dynamic: :return_true
Join Filters (Nested Filterable)
When a filterable entry has a keyword list as its value (instead of a list of operator strings),
it's treated as a join filter. The key becomes the expected named binding (via as: option in join).
use FatEcto.Query.Dynamics.Buildable,
filterable: [
title: ["$ILIKE"], # Direct filter on main table
classifications: [ # Join filter on :classifications binding
subject_id: ["$EQUAL"],
grade_id: ["$EQUAL", "$IN"]
]
]The caller must provide the join with matching as: binding:
Video
|> join(:inner, [v], c in assoc(v, :classifications), as: :classifications)
|> where(^dynamics)Join filters support all standard operators: $EQUAL, $NOT_EQUAL, $IN, $NOT_IN,
$LIKE, $ILIKE, $GT, $GTE, $LT, $LTE, $NULL, $NOT_NULL.
Join filters also work seamlessly with $OR and $AND operators:
%{
"$OR" => [
%{"subject_id" => %{"$EQUAL" => "math-uuid"}},
%{"grade_id" => %{"$EQUAL" => "grade-10-uuid"}}
]
}Global Configuration
You can configure the default behavior for all Buildable modules in your config:
# config/config.exs
config :fat_ecto, :default_dynamic, :return_trueThis will make all Buildable modules return dynamic([q], true) when no filters are applied,
unless explicitly overridden at the module level with default_dynamic: nil.
Example Usage
defmodule FatEcto.HospitalDynamicsBuilder do
use FatEcto.Query.Dynamics.Buildable,
filterable: [
id: ["$EQUAL", "$NOT_EQUAL"]
],
overrideable: ["name", "phone"],
ignoreable: [
name: ["%%", "", [], nil],
phone: ["%%", "", [], nil]
]
import Ecto.Query
def override_buildable("name", "$ILIKE", value) do
dynamic([q], ilike(fragment("(?)::TEXT", q.name), ^value))
end
def override_buildable("phone", "$ILIKE", value) do
dynamic([q], ilike(fragment("(?)::TEXT", q.phone), ^value))
end
def override_buildable(_field, _operator, _value) do
nil
end
# Optional: Override after_buildable to perform custom processing on the final dynamics
def after_buildable(dynamics) do
IO.puts("Do something on final Dynamics")
dynamics
end
end
Summary
Callbacks
Callback for performing custom processing on the final dynamics.
Callback for handling custom filtering logic for overrideable fields.
Callbacks
@callback after_buildable(dynamics :: Ecto.Query.dynamic_expr() | nil) :: any()
Callback for performing custom processing on the final dynamics.
This function is called at the end of the build/2 function. The default behavior is to return the dynamics,
but it can be overridden by the using module.
@callback override_buildable( field :: String.t() | atom(), operator :: String.t(), value :: any() ) :: Ecto.Query.dynamic_expr() | nil
Callback for handling custom filtering logic for overrideable fields.
This function acts as a fallback for overrideable fields. The default behavior is to return nil, but it can be overridden by the using module.