我正在尝试使用左连接构建一个Ecto查询,并在连接上添加可选的额外条件.我将尝试用典型的帖子和评论示例来描述它.
发表has_many评论
评论belongs_to Post.
假设Comment有两个布尔字段,已批准并具有特色.
我想获得所有帖子,无论他们是否有评论,因此左连接.
我希望预加载注释,但最好是一个SQL查询.
我想选择过滤已批准和有特色的评论.
我正在尝试写一个这样的函数,如果批准或特色不是零,它们将被包含在连接中,如果它们是零,它们将被忽略.我没有找到比这样的更好的方法:
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参数的列表.由于要求确定,我尝试这样做的尝试失败了.
我不能把这些条件放在哪里因为如果我做了t.approved == true之类的地方我只得到帖子批准的评论.
最佳答案 我认为答案是使用
dynamic功能.
这有效. (省略了我之前的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参数.关键字列表(我假设暗示==)和更具表现力的格式.动态似乎不适用于关键字列表.它试图将p.id扩展为p.id().
我无法获得@ mudasobwa基于宏的解决方案.我不是一个宏观专家,但我没有看到nil匹配如何在运行时工作.
关于宏解决方案还有一件事.出于某种原因,它也不适用于关键字列表.我希望像这样的裸骨宏可以工作:
defmacrop do_join do
quote do
[post_id: p.id]
end
end
但事实并非如此.它试图将p.id扩展为p.id()