Ecto - подсчет вложенных ассоциаций рекурсивно - PullRequest
0 голосов
/ 10 января 2019

У меня есть следующая схема:

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 и количество контрактов в каждой дочерней папке, и если каждая дочерняя папка спускается в дерево дочерних папок и их контрактов.

1 Ответ

0 голосов
/ 11 января 2019

Вам понадобятся рекурсивные выражения общих таблиц для подобных проблем, 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 возвращать счетчик всех возвращенных строк.

...