How to autoload a relationship?

The setup: lib/app/shop/resources/product.ex
defmodule App.Shop.Product do
[...]
relationships do
belongs_to :category, App.Shop.Category do
attribute_writable? true
end
end
[...]
end
defmodule App.Shop.Product do
[...]
relationships do
belongs_to :category, App.Shop.Category do
attribute_writable? true
end
end
[...]
end
I want to query for the product "Banana":
iex(12)> fruits = App.Shop.Category.create!(%{name: "Fruits"})
#App.Shop.Category<
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
name: "Fruits",
...
>
iex(13)> banana = App.Shop.Product.create!(%{name: "Banana", price: 0.1, category_id: fruits.id})
#App.Shop.Product<
promotion: #Ash.NotLoaded<:relationship>,
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "d655fa01-ece5-4ad9-b927-764a6a48b165",
name: "Banana",
price: Decimal.new("0.1"),
category_id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
...
>
iex(14)> App.Shop.Product.by_name("Banana")
{:ok,
#App.Shop.Product<
promotion: #Ash.NotLoaded<:relationship>,
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "d655fa01-ece5-4ad9-b927-764a6a48b165",
name: "Banana",
price: Decimal.new("0.1"),
category_id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
...
>}
iex(15)> App.Shop.load(banana, :category)
{:ok,
#App.Shop.Product<
promotion: #Ash.NotLoaded<:relationship>,
category: #App.Shop.Category<
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
name: "Fruits",
...
>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "d655fa01-ece5-4ad9-b927-764a6a48b165",
name: "Banana",
price: Decimal.new("0.1"),
category_id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
...
>}
iex(12)> fruits = App.Shop.Category.create!(%{name: "Fruits"})
#App.Shop.Category<
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
name: "Fruits",
...
>
iex(13)> banana = App.Shop.Product.create!(%{name: "Banana", price: 0.1, category_id: fruits.id})
#App.Shop.Product<
promotion: #Ash.NotLoaded<:relationship>,
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "d655fa01-ece5-4ad9-b927-764a6a48b165",
name: "Banana",
price: Decimal.new("0.1"),
category_id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
...
>
iex(14)> App.Shop.Product.by_name("Banana")
{:ok,
#App.Shop.Product<
promotion: #Ash.NotLoaded<:relationship>,
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "d655fa01-ece5-4ad9-b927-764a6a48b165",
name: "Banana",
price: Decimal.new("0.1"),
category_id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
...
>}
iex(15)> App.Shop.load(banana, :category)
{:ok,
#App.Shop.Product<
promotion: #Ash.NotLoaded<:relationship>,
category: #App.Shop.Category<
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
name: "Fruits",
...
>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "d655fa01-ece5-4ad9-b927-764a6a48b165",
name: "Banana",
price: Decimal.new("0.1"),
category_id: "3a3ed806-6df4-4602-aff6-4eed50e814b2",
...
>}
Question: How can I autoload the category when I query for a product so that I don't have to do two SQL requests? App.Shop.Product.by_name("Banana") should get me the product and the category.
3 Replies
barnabasj
barnabasj2y ago
You can create a read function with a preparation that always adds the load
actions do
read :read do
prepare build(load: [:category])
end
end
actions do
read :read do
prepare build(load: [:category])
end
end
Or you can do it when you call the interface:
App.Shop.Product.by_name("Banana", load: [:category])
App.Shop.Product.by_name("Banana", load: [:category])
ZachDaniel
ZachDaniel2y ago
You can’t do it with only one sql query at the moment though At some point we will add that optimization to Ash to use joins for single cardinality relationships, but it won’t be something you can turn on or off.
kernel
kernel2y ago
or you can do it for all reads by using a preparations block in your resource
preparations do
prepare build(load: [:category])
end
preparations do
prepare build(load: [:category])
end
many ways to skin cats in Ash 🙂

Did you find this page helpful?