我有以下架构:
defmodule MyApp.Folder do
use Enterprise.Web, :model
schema "folders" do
has_many(:contracts, MyApp.Contract)
has_many(:child_folders, MyApp.Folder, foreign_key: :parent_id)
end
end
如您所见,每个文件夹可以递归地包含多个子文件夹,每个子文件夹都有自己的子文件夹,依此类推。在我的文件夹控制器中,我要计算每个文件夹中包含的合同总数及其子文件夹中的所有合同,等等。
说,我有一个名为root
的文件夹。如果我想计算文件夹顶层的合同数量,我可以简单地调用length(root.contracts)
。但是,我仍然没有考虑root
的子文件夹和每个子文件夹中的合同数量,以及每个子文件夹是否归入子孙文件夹及其合同的树中。
答案 0 :(得分:0)
对于诸如此类的问题,您将需要递归公用表表达式,Ecto本身还不支持这些表达式(请参阅:https://github.com/elixir-ecto/ecto/pull/2757)。我们必须使用片段(https://hexdocs.pm/ecto/Ecto.Query.html#join/5-joining-with-fragments)
您没有提到正在使用的数据库的版本,但是最新版本的Postgres / Mysql / MariaDb都支持CTE。我还假定Ecto 3。
Contract
|> join(:inner, [c], f in fragment("(
WITH RECURSIVE RecursiveFolders AS (
SELECT
F.id,
F.name,
F.parent_id as parent_id
FROM
Folders F
WHERE
F.id = ?
UNION
SELECT
F.id,
F.name,
F.parent_id
FROM
Folders F
JOIN RecursiveFolders C ON C.id = F.parent_id
)
SELECT
*
FROM
RecursiveFolders
)", root_id), on: c.folder_id == f.id)
|> select([c], count(c.id))
为说明起见,递归CTE返回带有root_id
及其子元素的所有行,我们在这些行上加入契约(我假设folder_id
模式的Contract
列)。最后,我们使Ecto返回所有返回行的计数。