使用子查询或联接将同一架构上的两个ecto查询合并为一个

时间:2018-12-16 11:28:25

标签: sql postgresql elixir ecto

我有一个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(在这种情况下为p)传递给子级子查询,而没有在连接行上得到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 :(得分:1)

我认为子查询无法以这种方式进行参数化,因为它要在主查询之前 执行。可以将两个条件都移到主查询中。

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

需要后一个查询,因为当没有子级时,联接将返回一个空集。