我正在尝试使用左联接建立Ecto查询,并在联接上带有可选的附加条件。我将尝试通过典型的帖子和评论示例对其进行描述。
发表has_many留言评论属于留言。
假设“评论”具有两个布尔字段,即“已批准”和“精选”。
我想获取所有帖子,无论它们是否有评论,因此请保留联接。我要预加载注释,但最好是一个SQL查询。我想有选择地过滤已批准和精选的评论。
我正在尝试编写类似这样的函数,如果批准或推荐的功能不为nil,它们将被包含在联接中;如果为nil,它们将被忽略。我没有找到比这样更好的方法了:
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
可以,但是必须有更好的方法。如果我有第三个参数,那就太疯狂了。我正在寻找一种动态构建该列表的on参数的方法join()。由于需要固定,我的尝试失败了。
我不能将这些条件放在a中,where因为如果我做类似的事情,where t.approved == true我只会收到批准的帖子。
我认为答案是使用动态函数。
这有效。(忽略我之前的 some_var 条件)。
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 匹配如何在运行时工作。
关于宏观解决方案的另一件事。由于某种原因,它也不适用于关键字列表。我希望像这样的简单宏能够工作:
defmacrop do_join do
  quote do
    [post_id: p.id]
  end
end
但事实并非如此。它尝试将 p.id 扩展为 p.id()
| 归档时间: | 
 | 
| 查看次数: | 1090 次 | 
| 最近记录: |