Things I Learned using Ecto.Schema in 2024 (Part 1)


The problem
I have two schemas, coffees
and orders
wherein I want to get the recent completed order from each coffees
First solution
defmodule Schemas.Coffee do
# ... more code
has_many :completed_orders, Schemas.Order
@spec coffee_query(coffee_id :: Ecto.UUID.t()) :: Ecto.Query.t()
def coffee_query(coffee_id) do
from c in __MODULE__,
where: c.id == coffee_id,
preload: [completed_orders: Orders.completed_order],
limit: 1
end
end
defmodule Schemas.Order do
# ... more code
@completed_status :completed
@spec completed_order_query() :: Ecto.Query.t()
def completed_order_query() do
from o in __MODULE__,
where: o.status in ^@completed_status,
order_by: [desc: o.updated_at]
end
end
defmodule Contexts.Coffees do
# more code
@spec get_coffee_with_recent_completed_order(coffee_id :: Ecto.UUID.t())
:: Order.t()
def get_coffee_with_recent_completed_order(coffee_id) do
coffee = coffee_id
|> Schemas.Coffee.coffee_query()
|> Repo.one
List.first(coffee.completed_orders)
end
end
Based from the setup, I can actually get the
completed_orders
of coffees but the only downside for me is that the preload will return alist
Since it will return a
list
, that will result into another process for me just to get the recent completed order.
Then, has_one/3
came into my life
defmodule Schemas.Coffee do
# ... more code
has_one :completed_order, Schemas.Order,
where: [status: :completed],
preload_order: [desc: :updated_at]
@spec coffee_query(coffee_id :: Ecto.UUID.t()) :: Ecto.Query.t()
def coffee_query(coffee_id) do
from c in __MODULE__,
where: c.id == ^coffee_id
preload: [:completed_order],
limit: 1
end
end
defmodule Contexts.Coffees do
# more code
@spec get_coffee_with_recent_completed_order(coffee_id :: Ecto.UUID.t())
:: Order.t()
def get_coffee_with_recent_completed_order(coffee_id) do
coffee_id
|> Schemas.Coffee.coffee_query()
|> Repo.one
|> Map.get(:completed_order, nil)
end
end
What's the magic behind this scheme?
How does has_one/3
works?
Indicates a one-to-one association with another schema
From the current situation, coffee belongs_to order
. As a result, coffee has_many orders
, but I only need one
result to return coming from the orders schema to identify if the coffee order is completed already.
Using `has_one/3` I was able to achieve what I want.
has_one/3
- indicates a one-to-one association with another schema.where
- indicates aFiltering associations
to get my specific items.preload_order
- Sets the defaultorder_by
of the association, and since I am usinghas_one/3
Ecto set a queryLIMIT
set toone
.
[see has_one/3 on docs](https://hexdocs.pm/ecto/Ecto.Schema.html#has_one/3)
Happy Coding
Subscribe to my newsletter
Read articles from Pau Riosa directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Pau Riosa
Pau Riosa
๐ฅ Youtuber ๐ป. Software Developer ๐ Blogger I am software developer who has passion for career development and software engineering. I love building stuff that can help people all around the world. Let's connect and share that idea!