所以现在我使用的是:
dynamic(
[u],
ilike(
u.name, ^"%#{String.replace(term, "%", "\\%")}%"
)
)
这个术语是一个简单的字符串,比如"charlie"
。我该如何将其与一系列术语一起使用,比如:["charlie", "dennis", "frank"]
--没有片段,这是可能的吗?
发布于 2019-11-16 07:21:07
嗯,通过一些元编程,这在某种程度上是可能的。幸运的是,ecto允许在查询中使用自己的宏,所以我们所需要的就是构建自己的宏,将几个ILIKE
与or
粘合在一起。
这里有几个陷阱。首先,我们应该让编译器忘记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"))>
发布于 2019-11-18 10:40:48
您可以使用来自标准Ecto的or_where
:
def multi_ilike(queryable, words) do
for term <- words, reduce: queryable do
acc ->
str = "%#{String.replace(term, "%", "\\%")}%"
cond = dynamic([u], ilike(u.name, ^str))
or_where(acc, ^cond)
end
end
或者如果你不喜欢for
def multi_ilike(queryable, words) do
words
|> Enum.map(&"%#{String.replace(&1, "%", "\\%")}%")
|> Enum.map(&dynamic([u], ilike(u.name, ^&1))
|> Enum.reduce(queryable, &or_where(&2, ^&1))
end
https://stackoverflow.com/questions/58887180
复制相似问题