Объедините два экто-запроса в одной и той же схеме в один с подзапросом или соединением - PullRequest
0 голосов
/ 16 декабря 2018

У меня есть Post схема с виртуальным полем children, которую я сейчас заполняю, выполнив второй запрос после первого получения моего Post.

Начального запроса для получения Post:

post = Post |> Repo.get(id)

затем я запускаю второй запрос, чтобы получить его дочерние элементы и обновить виртуальное поле children:

children_query =
    from(p in Post,
      where: fragment("hierarchy <@ ?", ^post.hierarchy),
      order_by: p.inserted_at
    )

children = Repo.all(children_query)
post = Map.merge(post, %{children: children})

Поле иерархии сохраняется как Ltree в БД и имеет тип :string в схеме.

Можно ли как-нибудь объединить эти запросы в один?Я пытался работать с функцией Ecto subquery / 2 , но не смог ее обработать.

Я пытался это сделать, но не мог понять, как передать Post (р в этом случае) в дочерний подзапрос, не получая сообщение об ошибке the variable p is undefined в строке соединения.

  def children_query(post) do
    from p in Post,
    where: fragment("hierarchy <@ ?", ^post.hierarchy),
    order_by: v.inserted_at
  end

  def get_post(p_id) do
    from(p in Post, 
    where: p.id == ^p_id,
    join: c in subquery(children_query(p)),
    on: p.id == c.id, # not sure how it would join on
    select: %{p | children: c}
    )
  end

Я хочу выяснить это, потому что это становится очень неэффективным при отображении индексной страницы Postи необходимость запуска дополнительного дочернего запроса для каждого сообщения в списке.

1 Ответ

0 голосов
/ 17 декабря 2018

Полагаю, что подзапрос не может быть параметризован таким образом, потому что он должен быть выполнен до основного запроса.Можно было бы переместить оба условия в основной запрос.

children = 
  from(p in Post,
       join: s in subquery(from p in Post),
       where: p.id == ^p_id and fragment("? <@ ?", s.hierarchy, p.hierarchy),
       select: [s, p])

Вышеприведенный результат дает немного избыточный результат с Post, приклеенным к каждому из его дочерних элементов, но мне не удалось его улучшить.

Теперь вам нужно только разделить результат.

{post, children} =
  case Repo.all(children) do
    [[_, post] | _] = result -> {post, Enum.map(result, &hd/1)}
    [] -> {Repo.get(Post, p_id), []}
  end

Последний запрос необходим, поскольку при отсутствии дочерних элементов объединение возвращает пустой набор.

...