首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用动态构建的条件连接Ecto

使用动态构建的条件连接Ecto
EN

Stack Overflow用户
提问于 2018-06-01 11:22:22
回答 2查看 2.3K关注 0票数 7

我正在尝试构建一个带有左连接的Ecto查询,该连接具有可选的附加条件。我将尝试用典型的帖子和评论示例来描述它。

发表has_many评论评论belongs_to帖子。

假设Comment有两个布尔型字段,approved和featured。

我想要获取所有的帖子,不管它们是否有评论,因此使用了左连接。我希望评论预先加载,但最好是一个SQL查询。我想有选择地过滤评论的批准和特色。

我正在试着写一个类似这样的函数,如果approved或Feature值不为nil,它们将被包含在连接中,如果它们为nil,它们将被忽略。我还没有找到比这更好的方法:

代码语言:javascript
复制
def posts_with_comments(approved, featured, some_var) do
  query = Post
  |> where([p], p.some_field == ^some_var

  cond do
    !is_nil(approved) and !is_nil(featured)
      -> join(query, :left, [p], c in Comment, [post_id: p.id, approved: ^approved, featured: ^featured])

    !is_nil(approved)
      -> join(query, :left, [p], c in Comment, [post_id: p.id, approved: ^approved])

    !is_nil(featured)
      -> join(query, :left, [p], c in Comment, [post_id: p.id, featured: ^featured])

    true -> join(query, :left, [p], c in Comment, [post_id: p.id])
  end

  |> preload([p, c], [comments: c])
  |> select([p], p)
  |> Repo.all

end

这是可行的,但肯定有更好的方法。如果我有第三个参数,它会变得疯狂。我正在寻找一种为join()on参数动态构建该列表的方法。我尝试这样做的尝试失败了,因为需要pin。

我不能把这些条件放在where中,因为如果我做了像where t.approved == true这样的事情,我只能得到帖子批准的评论。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-06-03 06:59:41

我认为答案是使用dynamic函数。

这是可行的。(省略了我之前遇到的some_var条件)。

代码语言:javascript
复制
def posts_with_comments(approved, featured) do
  query = Post
  join(query, :left, [p], c in Comment, ^do_join(approved, featured))
  |> preload([p, c], [comments: c])
  |> Repo.all
end

defp do_join(approved, featured) do
  dynamic = dynamic([p, c], c.post_id == p.id)

  dynamic =
  case approved do
    nil -> dynamic
    _ -> dynamic([p, c], ^dynamic and c.approved == ^approved)
  end

  case featured do
    nil -> dynamic
    _ -> dynamic([p, c], ^dynamic and c.featured == ^featured)
  end
end

这比我的第一次尝试要好得多,因为它是一个简单的连接,只是随着条件的增加而变得更长,而不是条件的爆炸性增长。

作为练习,我无法通过向它提供一个字段列表并使用诸如reduce之类的东西来使它变得更通用。我遇到的问题是让字段名(例如,c.approved)从变量中工作。

join似乎支持两种类型的on参数。关键字列表(我假设这意味着==)和更具表现力的格式。dynamic似乎不能处理关键字列表。它试图将p.id扩展为p.id()。

我不能让@mudasobwa的基于宏的解决方案工作。我还不是一个真正的宏观专家,但我不知道nil匹配在运行时是如何工作的。

关于宏观解决方案还有一件事。由于某些原因,它也不适用于关键字列表。我希望像这样的基本宏能正常工作:

代码语言:javascript
复制
defmacrop do_join do
  quote do
    [post_id: p.id]
  end
end

它试图将p.id扩展为p.id()

票数 2
EN

Stack Overflow用户

发布于 2018-06-01 11:43:41

我会在其中声明一个helper和模式匹配参数:

代码语言:javascript
复制
def posts_with_comments(approved, featured, some_var) do
  query = Post
          |> where([p], p.some_field == ^some_var)
          |> join(:left, [p], c in Comment, do_join(approved, featured))
          |> preload([p, c], [comments: c])
          |> select([p], p)
          |> Repo.all
end

defmacrop do_join(nil, nil) do
  quote do: [post_id: p.id]
end
defmacrop do_join(approved, nil) do
  quote bind_quoted: [approved: approved] do
    [post_id: p.id, approved: ^approved]
  end
end
defmacrop do_join(nil, featured) do
  quote bind_quoted: [featured: featured] do
    [post_id: p.id, featured: ^featured]
  end
end
defmacrop do_join(approved, featured) do
  quote bind_quoted: [approved: approved, featured: featured] do
    [post_id: p.id, approved: ^approved, featured: ^featured]
  end
end

defmacro是允许引脚操作符脱离上下文所必需的。

或者,Enum.reduce/3 it:

代码语言:javascript
复制
# kw is approved: approved, featured: featured
defmacrop do_join(kw) do
  initial = [{:post_id, {{:., [], [{:p, [], Elixir}, :id]}, [], []}}]
  Enum.reduce(kw, initial, fn
    {_, nil}, acc -> acc
    {k, _}, acc ->
      quoted = {k, {:^, [], [{k, [], Elixir}]}}
      [quoted | acc]
  end)
end
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50635661

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档