Skip to content

feat: custom filters on context level#63

Open
subaru9 wants to merge 1 commit intoMikaAK:mainfrom
subaru9:feat/query-builder-in-context
Open

feat: custom filters on context level#63
subaru9 wants to merge 1 commit intoMikaAK:mainfrom
subaru9:feat/query-builder-in-context

Conversation

@subaru9
Copy link

@subaru9 subaru9 commented Oct 4, 2024

Overview

Introduced ability to convert params into custom queries and apply them to accumulator query using:

  • query builder functions at context level
  • custom query functions from schema

Example Usage

The following example illustrates how to define custom filters in a context that tracks balances in different currencies:

  1. Migration:
defmodule PaySys.Repo.Migrations.CreateMoneyWithCurrencyAndBalances do
  use Ecto.Migration

  def up do
    execute("""
    CREATE TYPE public.money_with_currency AS (
      currency_code varchar,
      amount numeric
    );
    """)

    create table(:balances) do
      add :balance, :money_with_currency
      add :user_id, references(:users, on_delete: :nothing)

      timestamps(type: :utc_datetime_usec)
    end

    create index(:balances, [:user_id])
  end

  def down do
    drop table(:balances)
    execute("DROP TYPE public.money_with_currency;")
  end
end
  1. Context with query builder implementation.
defmodule PaySys.Funds do
  
  alias EctoShorts.Actions.QueryBuilder
  alias PaySys.Funds.Balance

  @behaviour QueryBuilder
  
  @impl QueryBuilder
  def filters, do: [:currency_code, :amount_gte]
  
  @impl QueryBuilder
  def build_query(Balance, %{currency_code: val}, query), 
    do: Balance.by_currency_code(query, val)

  @impl QueryBuilder
  def build_query(Balance, %{amount_gte: val}, query), 
    do: Balance.by_amount_gte(query, val)
end
  1. Schema with custom query functions:
defmodule PaySys.Funds.Balance do
  use Ecto.Schema

  import Ecto.Query, only: [where: 3, fragment: 1]

  def by_currency_code(query \\ __MODULE__) do
    where(query, [b], fragment("(?).currency_code = ?", b.balance, ^to_string(val)))
  end

  def by_amount_gte(query \\ __MODULE__) do
    where(query, [b], fragment("(?).amount >= ?", b.balance, ^val))
  end
end
  1. Context for Querying:
defmodule PaySys.Funds do

  alias EctoShorts.Actions
  alias PaySys.Funds
  alias PaySys.Funds.Balance

  @doc """
  ## Examples

      iex> Funds.list_balances(%{currency_code: "USD", amount_gte: 100})
      [%Balance{balance: %Money{currency: :USD, amount: 200}}]

      iex> Funds.list_balances(%{currency_code: "EUR", amount_gte: 50})
      [%Balance{balance: %Money{currency: :EUR, amount: 150}}]

      iex> Funds.list_balances(%{currency_code: "USD", amount_gte: 1000})
      []
  """
  @spec list_balances(Actions.filter_params()) :: [Balance.t()] | []
  def list_balances(params \\ %{}) do
    Actions.all(Balance, params)
  end
end

@subaru9
Copy link
Author

subaru9 commented Oct 4, 2024

@MikaAK, @cylkdev pls review. PR based on our conversation

@subaru9 subaru9 force-pushed the feat/query-builder-in-context branch from b4012d1 to ce836ac Compare October 21, 2024 10:43
Introduced ability to convert params into custom queries and apply them
to accumulator query using:
  * query builder functions at context level
  * custom query functions from schema
@subaru9 subaru9 force-pushed the feat/query-builder-in-context branch from ce836ac to 452ed30 Compare October 21, 2024 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments