Duplicate struct at the database level with all its associations using Ecto.Multi

Hi everyone,

I need to add functionality to my Phoenix App for my users to be able to completely duplicate a given struct and all its associations.

I am thinking of using Ecto.Multi so that I can ensure the duplication process completed successfully.

The problem at the moment is that my struct has deeply nested associations and I can’t seem to be able to figure out a way to duplicate my root struct and all the related associations with the new ID’s that are generated after via the Multi.

Let’s say I have a %City{} that has many %Neighborhood{} which have many %House{} which have many %GroupOfIndividuals{} which have many %Individual{}.

My goal is to essentially “clone” a %City{} and have the new city (the clone) have its one ID and obviously new ID’s all the way down to the individuals, if that makes sense.

I am not sure I see the difficulty? Use Repo.transaction and just start top to bottom, doing put_assoc of the lower-level objects pointing at the higher-level objects (after they are inserted).

Or you can just create all objects in memory as structs (not changesets) and do a Repo.insert on that.

If your associations make a tree, then this is relatively easy. If there are cycles in your associations, this is less easy. If it’s a nice tree then you can:

city = Repo.preload(city, [various: [associations: [go: :here]])

Then, recursively descend through the struct tree and strip out all of the id values, and parent foreign key column values:

dup_city = strip_ids(city)

Then you can just Repo.insert(dup_city) and Ecto will handle inserting the whole tree.

Thank you very much.

How should I best recursively descend through the struct?

Also, I am using Ecto.Multi and this is the first time I hear Repo.insert(dup_city) mentioned in that context.
Did you by chance mean Repo.transaction()?

Thank you, at the moment I was manually inputing the parent element id, have never used put_assoc but I will try it.