使用变量引用命名的外生查询绑定

问题描述 投票:3回答:2

我试图建立一个搜索查询中的给定表中的字段项的函数。

对于像查询

initial_query = 
  Answer
  |> join(:left, [a], q in assoc(a, :question), as: :question)
  |> join(:left, [a, q], s in assoc(a, :survey), as: :survey)

我希望能够通过:question:survey引用的表中进行搜索。

现在,此代码的工作:

initial_query
|> or_where(
  [question: t], #:question hard coded
  fragment(
    "CAST(? AS varchar) ILIKE ?",
    field(t, ^field),
    ^"%#{search_term}%"
  )
)

不过,我想有一个函数,它的命名为参数绑定,但我不能找到一个方法来做到这一点。

我尝试:

defp search_field(initial_query, table, field, search_term) do
  initial_query
  |> or_where(
    [{table, t}],
    fragment(
      "CAST(? AS varchar) ILIKE ?",
      field(t, ^field),
      ^"%#{search_term}%"
    )
  )
end

提供错误

**在查询(Ecto.Query.CompileError)未结合的可变t。如果你正试图插值的值,使用^ VAR扩大宏:Ecto.Query.or_where / 3

当这样调用:

search_field(initial_query, :question, :text, search_text)

defp search_field(initial_query, table, field, search_term) do
  initial_query
  |> or_where(
    [{^table, t}],
    fragment(
      "CAST(? AS varchar) ILIKE ?",
      field(t, ^field),
      ^"%#{search_term}%"
    )
  )
end

**(Ecto.Query.CompileError)绑定列表应该只包含变量或{as, var}元组,得到:{^表,T}扩展宏:Ecto.Query.or_where / 3


有没有办法使用一个变量来引用一个外生查询一个命名绑定的方法吗?

elixir ecto
2个回答
2
投票

所以,这个问题的答案似乎是不存在的外生支持这样做的一种方式。 @maartenvanvliet解决方案很好地工作,以依靠内部实施的不足。

我对这个问题的解决办法是有功能search_field总是在最后加入表中搜索,使用...语法描述here

# Searches for the `search_term` in the `field` in the last joined table in `initial_query`.
defp search_field(initial_query, field, search_term) do
  initial_query
  |> or_where(
    [..., t],
    fragment(
      "CAST(? AS varchar) ILIKE ?",
      field(t, ^field),
      ^"%#{search_term}%"
    )
  )
end

所以这个功能将使用这样的:

Answer
|> join(:left, [a], q in assoc(a, :question), as: :question)
|> search_field(:text, search_text)
|> join(:left, [a, q], s in assoc(a, :survey), as: :survey)
|> search_field(:title, search_text)

其中,在我看来,仍然很好地读取与要求,我们能够改变initial_query的缺点。


1
投票

关键是要获取的命名绑定绑定的位置。命名绑定存储在%Ecto.Query{aliases: aliases}领域。

def named_binding_position(query, binding) do
  Map.get(query.aliases, binding)
end

def search_field(query, table, field, search_term) do
  position = named_binding_position(query, table)
  query
  |> or_where(
    [{t, position}],
    fragment(
      "CAST(? AS varchar) ILIKE ?",
      field(t, ^field),
      ^"%#{search_term}%"
    )
  )
end

我们首先寻找的命名在query.aliases结合位置。然后使用这个位置来构建查询。

现在,当我们调用

Answer
|> join(:left, [a], q in assoc(a, :question), as: :question)
|> join(:left, [a, q], s in assoc(a, :survey), as: :survey)
|> search_field(:question, :text, "bogus")

它应该产生类似

#Ecto.Query<from a in Answer,
left_join: q in assoc(a, :question), as: :question,
or_where: fragment("CAST(? AS varchar) ILIKE ?", q.text, ^"%bogus%")>

值得注意的是,在%Query.aliases的{t, position}元组来指代命名结合的位置,内部实现,而不是记录。因此,可能会有所变动。见https://github.com/elixir-ecto/ecto/issues/2832了解更多信息

© www.soinside.com 2019 - 2024. All rights reserved.