使用变量引用Ecto查询中的命名绑定

时间:2019-01-31 21:36:26

标签: elixir ecto

我正在尝试构建一个在查询中给定表的字段中搜索词条的函数。

对于类似

的查询
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}元组,得到:{^ table,t}       扩展宏:Ecto.Query.or_where / 3


有没有一种方法可以使用变量来引用Ecto查询中的命名绑定?

2 个答案:

答案 0 :(得分:2)

因此,此问题的答案似乎是Ecto没有支持的方法来执行此操作。 @maartenvanvliet解决方案效果很好,但缺点是依赖于内部实现。

我对这个问题的解决方案是使用here描述的search_field语法,使函数...始终在最后一个联接表中进行搜索:

# 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 :(得分:1)

诀窍是检索命名绑定在绑定中的位置。命名的绑定存储在 from tkinter import * from tkinter import messagebox import time #pop up def spam(): global spamreturn spamreturn = False def closed(): if messagebox.askokcancel("Quit", "Do you really wish to quit?"): mibox.destroy() return True mibox = Tk() topframe = Frame(mibox) miLabel = Label(mibox, text="Call 1-800-273-8255") mibutton = Button(topframe, text="Your Computer has been infected") mibutton2 = Button(topframe, text="Please call 1-800-273-8255 for Assistance") miLabel.pack() mibutton.pack() mibutton2.pack() topframe.pack() mibox.geometry("300x100+500+250") mibox.protocol("WM_DELETE_WINDOW", closed) mibox.mainloop() if closed(): spamreturn = True spam() if spamreturn == True: print("worked") time.sleep(3) 字段中。

%Ecto.Query{aliases: aliases}

我们首先在query.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

它应该产生类似

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来指代命名结合的位置是一个内部实现的,不能记录。因此,可能会有所变化。有关更多信息,请参见https://github.com/elixir-ecto/ecto/issues/2832