[Proposal] preload function which handles {:ok, ...} or {:error, ...} tuple

21 views
Skip to first unread message

Tiago Barroso

unread,
Jan 20, 2024, 9:08:21 AMJan 20
to elixir-ecto
I think Ecto should export something like the following function preload_in_result function. The goal is to be able to easily preload results from the Repo.insert/1, Repo.update/1, etc inside pipelines.

@doc """
  Preload the associations in a database result.

  It accepts the following types of result:

    - `{:ok, struct}` where `struct` is an ecto schema
    - `{:error, changeset}` where `changeset` is a changeset

  This function is optimized to process the results of operations such as
  `c:Repo.insert/1` and `c:Repo.update/1`.
  """
  def preload_in_result({:ok, struct}, repo, preloads) do
    {:ok, repo.preload(struct, preloads)}
  end

  def preload_in_result({:error, changeset}, repo, preloads) do
    {:error, preload_in_changeset(changeset, repo, preloads)}
  end

  @doc """
  Preload the associations in a changeset
  """
  def preload_in_changeset(changeset, repo, preloads) do
    preload_in_changeset_helper(changeset, repo, preloads)
  end

  defp preload_in_changeset_helper(%Ecto.Changeset{} = changeset, repo, preloads) do
    # Preloading the data is very easy, because the data
    # is usually a struct already
    new_data = repo.preload(changeset.data, preloads)
    # Handling changes is harder.
    # We'll have to create a fake changeset.
    schema = changeset.data.__meta__.schema
    changes = changeset.changes
    # Create a "fake" struct so that we can preload the assocs.
    changes_struct = struct(schema, changes)
    changes_preloaded = repo.preload(changes_struct, preloads)
    preloaded_assocs = Map.take(changes_preloaded, preloads)
    merged_changes = Map.merge(changes, preloaded_assocs)
    # Update the data and the changesets
    %{changeset | data: new_data, changes: merged_changes}
  end

  defp preload_in_changeset_helper(list_of_changesets, repo, preloads)
        when is_list(list_of_changesets) do
    Enum.map(list_of_changesets, fn changeset ->
      preload_in_changeset_helper(changeset, repo, preloads)
    end)
  end
as
Reply all
Reply to author
Forward
0 new messages