如何解决如何使用 ecto 进行动态表连接?
我正在尝试编写一个涉及动态表连接(照片到相册)的动态查询。我的第一次尝试仅适用于一对多(要放置的照片):
defmodule Test1 do
def filter_by_place_id(dynamic,id) do
dynamic([p],^dynamic and p.place_id == ^id)
end
end
dynamic =
true
|> Test1.filter_by_place_id(248)
但这不适用于多对多领域。我认为这需要表连接。所以我的下一次尝试:
defmodule Test2 do
def filter_by_place_id({query,dynamic},id) do
dynamic = dynamic([p],^dynamic and p.place_id == ^id)
{query,dynamic}
end
def filter_by_album_id({query,id) do
query = join(query,:inner,[p],album in assoc(p,:albums),as: :x)
dynamic = dynamic([{:x,x}],^dynamic and x.id == ^id)
{query,dynamic}
end
end
query = from(p in Photo)
{query,dynamic} =
{query,true}
|> Test2.filter_by_place_id(248)
|> Test2.filter_by_album_id(10)
|> Test2.filter_by_album_id(11)
但是这失败了,因为绑定 :x
是硬编码的,显然我不能重用它。但我需要一个绑定来确保 where 子句引用正确的连接。
但是如果我尝试使用 as: ^binding
而不是 as :x
,我会收到错误:
** (Ecto.Query.CompileError) `as` must be a compile time atom,got: `^binding`
(ecto 3.6.2) expanding macro: Ecto.Query.join/5
meow.exs:30: Test2.filter_by_album_id/2
所以我不知道从哪里开始。是否可以为连接动态分配绑定?
解决方法
绑定可以这样写:first,...,last
https://hexdocs.pm/ecto/Ecto.Query.html:
同样,如果您只对查询中的最后一个绑定(或最后一个绑定)感兴趣,您可以使用 ... 指定“之前的所有绑定”并匹配最后一个。
从posts_with_comments中的[p,c],选择:{p.title,c.body}
所以尝试重写filter_by_album_id
:
def filter_by_album_id({query,dynamic},id) do
query = join(query,:inner,[p],album in assoc(p,:albums))
dynamic = dynamic([p,x],^dynamic and x.id == ^id)
{query,dynamic}
end
,
另一个答案给了我一个想法,放弃动态,只使用多个 where 子句。我不确定这会得到支持。它看起来像多个 where 子句组合在一起。
def filter_by_value({query,field,id) do
query = join(query,^field.id))
|> where([p,x.id == ^id)
{query,dynamic}
end
不幸的是,这消除了您通过动态获得的灵活性,这是我非常喜欢的,但现在这是一个足够的解决方案。
,实际上,您可以动态绑定别名。
Ecto 查询是一个结构体,它有一个属性 aliases
来存储名称绑定映射。
例如您有一个查询:
from(p in Post,join: c in assoc(p,:comments),as: :comments)
然后别名将如下所示:
%Ecto.Query{
aliases: %{
comments: 1
}
}
comments
是别名,1
是位置。因此您可以手动更新别名映射。
query = from(p in Post,:comments))
column = :comments
aliases = query.aliases
if Map.has_key?(aliases,column) do
raise ArgumentError,"Do not allow 2 ref binding with the same name"
end
aliases =
case Enum.map(aliases,&elem(&1,1)) do
[] ->
%{column => 1}
positions ->
max = Enum.max(positions)
Map.put(aliases,column,max + 1)
end
# Update the alias map
query = struct(query,aliases: aliases)
但这只是一个 HACK,请注意这一点。
我在构建动态连接查询时发现了这个,你可以在这里找到源代码 https://github.com/bluzky/filtery/blob/6338f53c81608fd44f1eeac0a2ceb108043e56c9/lib/base.ex#L285
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。