How can I move data in a json field?

I need to do a migration where the data from some columns needs to be migrated into a json column containing an array of maps.

As an explicit example, let’s say that there is a column named title and a column named items

schema "foos" do 
    field(:title, :string, default: "") 
    field(:items, {:array, :map}, default: [])

I need to create a migration where at the end, each item in items has a field called title with the value that is currently in the column title.

What I ended up doing is:

  def put_data({title, items}) do
    Enum.map(items, fn item ->
      item
      |> Map.put("title", title)
  end

  def change do
    data =
      from(f in "foos",
        select:
          {f.id, {f.title, f.items}}
      )
      |> Repo.all()
      |> Enum.map(fn {id, data} -> {id, put_data(data)} end)

    Enum.each(data, fn {id, mailing_drops} ->
      query =
        from(m in "mailing_jobs",
          where: m.id == ^id,
          update: [set: [mailing_drops: ^mailing_drops]]
        )

      Repo.update_all(query, [])
    end)
  end

… but I would love to see a better option. My code feels wrong somehow.

1 Like