微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 ecto 进行动态表连接?

如何解决如何使用 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 举报,一经查实,本站将立刻删除。