Ecto - 递归计算嵌套关联
Ecto - counting nested associations recursively
我有以下架构:
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
的子文件夹和每个子文件夹中的合同数量,以及每个子文件夹是否下降到后代文件夹及其合同树中。
你需要递归通用 Table 表达式来解决这些问题,Ecto 本身还不支持这些(参见:https://github.com/elixir-ecto/ecto/pull/2757). We'll have to use fragments (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 return 包含 root_id
及其子行的所有行,我们在这些行上加入合同(我假设 folder_id
列 Contract
模式)。最后,我们确实使 Ecto return 成为所有 returned 行的计数。
我有以下架构:
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
的子文件夹和每个子文件夹中的合同数量,以及每个子文件夹是否下降到后代文件夹及其合同树中。
你需要递归通用 Table 表达式来解决这些问题,Ecto 本身还不支持这些(参见:https://github.com/elixir-ecto/ecto/pull/2757). We'll have to use fragments (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 return 包含 root_id
及其子行的所有行,我们在这些行上加入合同(我假设 folder_id
列 Contract
模式)。最后,我们确实使 Ecto return 成为所有 returned 行的计数。