Skip to content

Instantly share code, notes, and snippets.

@narrowtux
Last active February 16, 2023 08:39
Show Gist options
  • Save narrowtux/025da9ccea503ea7412664cc8a5a4dbd to your computer and use it in GitHub Desktop.
Save narrowtux/025da9ccea503ea7412664cc8a5a4dbd to your computer and use it in GitHub Desktop.
Recursive models with ecto
defmodule Model do
schema "models" do
field :foo, :string
has_many :children, Model, foreign_key: :parent_id
belongs_to :parent, Model, foreign_key: :parent_id
end
@doc """
Recursively loads parents into the given struct until it hits nil
"""
def load_parents(parent) do
load_parents(parent, 10)
end
def load_parents(_, limit) when limit < 0, do: raise "Recursion limit reached"
def load_parents(%Model{parent: nil} = parent, _), do: parent
def load_parents(%Model{parent: %Ecto.Association.NotLoaded{}} = parent, limit) do
parent = parent |> Repo.preload(:parent)
Map.update!(parent, :parent, &Model.load_parents(&1, limit - 1))
end
def load_parents(nil, _), do: nil
@doc """
Recursively loads children into the given struct until it hits []
"""
def load_children(model), do: load_children(model, 10)
def load_children(_, limit) when limit < 0, do: raise "Recursion limit reached"
def load_children(%Model{children: %Ecto.Association.NotLoaded{}} = model, limit) do
model = model |> Repo.preload(:children) # maybe include a custom query here to preserve some order
Map.update!(model, :children, fn(list) ->
Enum.map(list, &Model.load_children(&1, limit - 1))
end)
end
end
defmodule ModelController do
def show(id) do
model = Repo.get(Model, id)
|> Model.load_parents
|> Model.load_children
# do something
end
end
@hieu292
Copy link

hieu292 commented Jul 11, 2018

If I have an adjacency list, How to save it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment