首页 > 解决方案 > Ecto ILIKE 用于多个关键字?

问题描述

所以现在我正在使用这个:

dynamic(
  [u],
  ilike(
    u.name, ^"%#{String.replace(term, "%", "\\%")}%"
  ) 
)

该术语是一个简单的字符串,例如"charlie". 我将如何将其与术语列表一起使用,例如:["charlie", "dennis", "frank"]- 没有片段甚至可能吗?

标签: elixirecto

解决方案


嗯,这在某种程度上可以通过一些元编程来实现。幸运的是,允许在查询中使用您自己的宏,所以我们只需要构建自己的宏,将几个ILIKEs 与ors 粘合起来。

这里有几个陷阱。首先,我们应该让编译器忘记Kernel.or/2,否则,它将尝试在适当的位置进行逻辑析取。然后,我们应该显式地导入Ecto.Query.API.or/2. 最后,我们应该递归地构建生成的 AST 以传递给查询。

加起来。

defmodule MultiOr do
  import Ecto.Query
  import Kernel, except: [or: 2]
  import Ecto.Query.API, only: [or: 2]

  # terminating call, we are done
  defp or_ilike(var, [term1, term2] = terms) do
    quote do
      ilike(unquote(var), unquote(term1)) or ilike(unquote(var), unquote(term2))
    end
  end

  # recursive call
  defp or_ilike(var, [term | terms]) do
    quote do
      ilike(unquote(var), unquote(term)) or unquote(or_ilike(var, terms))
    end
  end

  # the macro wrapper to inject AST
  defmacrop multi_ilike(var, terms) do
    Macro.expand(or_ilike(var, terms), __CALLER__)
  end

  # test
  def test do
    Ecto.Query.where(User, [u],
      multi_ilike(u.name, ["charlie", "dennis", "frank"]))
  end
end

请注意,这or_ilike/2函数,返回 AST。它们不能是宏(这可能会简化一切),因为不能递归调用宏(应该在第一次调用之前预先定义。)

让我们来看看。

MultiOr.test
#⇒ #Ecto.Query<from u0 in User,
#   where: ilike(u0.name, "charlie") or (ilike(u0.name, "dennis") or ilike(u0.name, "frank"))>

推荐阅读