You can perform bulk insertions for records that contain embedded schemas by doing the following:
alias MyApp.MyEmbed
alias MyApp.MyMainSchema
# multiple_attrs is a list of map attrs that we want to insert
def bulk_create(multiple_attrs) do
multiple_attrs = Enum.map(multiple_attrs, fn attrs ->
attrs
# put timestamps, since they are not auto-generated for us
|> Map.put(:inserted_at, NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second))
|> Map.put(:updated_at, NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second))
# This will pipe the attributes into the anonymous function
# we will do embed-specific operations here
|> (fn attrs ->
# This creates a struct of MyEmbed on the key :my_embed
%{attrs | my_embed: struct(MyEmbed, attrs.my_embed)}
# this puts the binary_id on the MyEmbed struct that we created.
# you will need to ensure that the MyEmbed module implements the Access behaviour
|> put_in([:my_embed, :id], Ecto.UUID.generate())
end).()
end)
{num, _} = MyApp.Repo.insert_all(MyMainSchema, multiple_attrs)
{:ok, num}
end
Step by step explanation:
- We define this function
bulk_create
to wrap theRepo.insert_all/2
function. - We alias
MyEmbed
andMyMainSchema
for convenience purposes. - We need to map over the received attributes. In this case, we are receiving a list of maps, hence we need to perform some tasks (like auto-generating timestamps and ids that insert_all does not handle out of the box).
- First, we place timestamps. Ecto only accepts timestamps without milliseconds, hence we need to truncate the generated timestamp to only having seconds. Do this for
:inserted_at
and:updated at
. - Second, we need to convert the attributes for the embed into a
MyEmbed
struct. - Third, we need to generate a UUID for the created struct.
- First, we place timestamps. Ecto only accepts timestamps without milliseconds, hence we need to truncate the generated timestamp to only having seconds. Do this for
Thereafter, we are then ready to pass it all to MyApp.Repo.insert_all/1
.
Hope this helps!