我有一个实体 Node
,它引用自身以创建树状结构。
这是迁移:
create table(:nodes) do
add :name, :string, null: false, size: 64
add :parent_id, references(:nodes, on_delete: :nothing)
end
这里是模式定义:
schema "nodes" do
field :name, :string
belongs_to :parent, Node
has_many :children, Node, foreign_key: :parent_id
end
我正在尝试使用这种方法加载整棵树:
root_nodes = Repo.all(
from n in Node,
where: is_nil(n.parent_id) # Root nodes don't have a parent
)
nodes = Enum.map(root_nodes, fn(n) ->
Ecto.build_assoc(n, :children, load_children(n.id))
end)
地点:
defp load_children(parent_id) do
nodes = Repo.all(
from n in Node,
where: n.parent_id == ^parent_id
)
if nodes != [] do
# If children aren't empty, apply recursively
nodes = Enum.map(nodes, fn(n) ->
Ecto.build_assoc(n, :children, load_children(n.id))
end)
end
nodes
end
但我得到:
** (FunctionClauseError) no function clause matching in Ecto.drop_meta/1
一般来说,我想我很难理解应该如何使用 Ecto ORM。大多数教程仅展示了如何获取孤立行或使用一级预加载的示例。我应该如何加载树状结构?感谢您的帮助。
最佳答案
Ecto 不是常规的 ORM,因为大多数 ORM 都试图完全抽象数据库,而 ecto 往往接近底层数据库语义。
这意味着真正的问题不是“如何使用 Ecto 加载树状结构”。但是“我如何使用 SQL 加载树状结构”(假设这就是您使用的)。
毫不奇怪,答案是它非常复杂 - 要么需要对每个嵌套级别进行一次查询(效率极低),recursive queries或更改表示(存储完整路径而不仅仅是 parent_id,或使用 Nested Set model )。
如果您保留 parent_id
-only 方法,最简单的方法可能是预先加载所有内容,并在加载到正确位置后适本地拼接(优点是您只执行一个查询)。
虽然这不能直接回答问题(因为没有好的答案),但我希望这能给您一些想法,在哪里寻找解决方案以及如何改变方法以使其更容易。
https://stackoverflow.com/questions/42718549/