我试图建立一个搜索查询中的给定表中的字段项的函数。
对于像查询
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
有没有办法使用一个变量来引用一个外生查询一个命名绑定的方法吗?
所以,这个问题的答案似乎是不存在的外生支持这样做的一种方式。 @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
的缺点。
关键是要获取的命名绑定绑定的位置。命名绑定存储在%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了解更多信息