使用ecto预加载所有链接的记录

时间:2019-03-12 06:02:10

标签: sql postgresql elixir ecto preload

我有链表的结构

defmodule Data.Record do
  use Data.Web, :model

  alias Data.{Record, Repo}

  schema "records" do
    field(:date_start, :date)
    field(:date_end, :date)
    field(:change_reason, :string)
    field(:is_active, :boolean, default: true)
    field(:notes, :string)
    belongs_to(
      :changed_from,
      Data.Record,
      foreign_key: :changed_from_id
    )

    belongs_to(
      :changed_to,
      Data.Record,
      foreign_key: :changed_to_id
    )

    timestamps()
  end
end

但是问题是我们需要动态预加载所有嵌套记录。例如,列表可以将记录1更改为->记录2更改为->记录3更改为。但是ecto不会/无法动态加载预加载,例如record |> preload([{:changed_to, :changed_to}])

预加载所有链接的changed_to记录的最佳方法/解决方法是什么?

1 个答案:

答案 0 :(得分:1)

嗯,最(肮脏)的解决方法可能是这样的。它将preload的参数构建到一定深度:

def preload_args(relation, max_level \\ 50) do
  preload_args(relation, max_level - 1, relation)
end

defp preload_args(_relation, level, acc) when level <= 0, do: acc
defp preload_args(relation, level, acc) do
  preload_args(relation, level - 1, [{relation, acc}])
end

要使用它:

Repo.preload record, Record.preload_args(:changed_to)

这会将每个:changed_to关系预先加载到特定级别,或者直到不再加载为止。当然,这不是您真正想使用的解决方案,因为它会针对每个预加载执行一次查询,并且您不知道链将提前多长时间,可能会超过50个步骤。

(请不要为我的代码/建议而烦我,您也特别要求过解决方法。))

我认为Aetherus的this comment about a 'closure table'(向我指出this article)可能会为您提供更好的解决方案。这也加强了我的假设,即您不需要首先存储父ID和子ID,仅使用parent_id就足够了。这也将使插入新记录变得更加容易:您也不需要更新父记录。