Hi everyone,
I’m working with Ecto and have a schema that includes several levels of embeds_one
and embeds_many
. Here’s a simplified example to illustrate the structure:
defmodule MyApp.Foo do
use Ecto.Schema
import Ecto.Changeset
schema "foos" do
embeds_one :bar, Bar do
embeds_one :baz, Baz do
embeds_many :items, Item, on_replace: :delete do
field :index, :integer
field :value, :string
end
field :target_index, :integer
end
end
timestamps()
end
end
Now I need to update just one item inside the items
list, which is inside baz
, which is inside bar
.
The item I want to update is the one where index == target_index
, and I only want to change its value
. Here’s the verbose way I’m doing it:
foo
|> change()
|> put_embed(
:bar,
foo.bar
|> change()
|> put_embed(
:baz,
foo.bar.baz
|> change()
|> put_embed(
:items,
foo.bar.baz.items
|> Enum.map(fn
%{index: i} = item when i == foo.bar.baz.target_index ->
item |> change() |> put_change(:value, new_value)
item -> item
end)
)
)
)
|> Repo.update()
This works, but as you can see, it becomes deeply nested and hard to read/maintain, especially when dealing with many levels.
Is there a cleaner or more idiomatic way to update a deeply nested embedded field in Ecto without having to manually call change/2
and put_embed/3
at every level?