使用动态运算符创建 Ecto 查询
Posted
技术标签:
【中文标题】使用动态运算符创建 Ecto 查询【英文标题】:Create Ecto query with dynamic operators 【发布时间】:2020-01-09 13:28:38 【问题描述】:我知道如何在 Ecto 查询中使用动态字段和值,例如:
field = :age
value = 20
Ecto.Query.where(App.User, [x], field(x, ^field) < ^value)
但是,可以动态定义运算符(在本例中为<
)吗?以及如何?
我已经用fragment
和插值字符串试过了:
operator = ">"
Ecto.Query.where(App.User, [x], fragment("? #operator ?", field(x, ^field), ^value))
但它引发了异常:Ecto.Query.CompileError) to prevent SQL injection attacks, fragment(...) does not allow strings...
【问题讨论】:
【参考方案1】:你可以,通过一点元编程。 ecto 支持自定义宏。
defmacrop custom_where(t, f, v, sign) do
sign, [context: Elixir, import: Kernel],
[
:field, [], [t, :^, [], [f]],
:^, [], [v]
]
end
并像使用它
field = :age
value = 20
Ecto.Query.where(User, [x],
custom_where(x, field, value, :<))
#⇒ #Ecto.Query<from u0 in User, where: u0.age < ^20>
这里的诀窍是我们欺骗ecto 就地注入 AST,因此它不执行检查。
【讨论】:
从变量传递符号时不起作用:Ecto.Query.where(User, [x], custom_where(x, field, value, sign)
。它显示此错误:(Ecto.Query.CompileError) sign(field(x, ^field), ^value) is not a valid query expression
任何建议?
这显然行不通,因为传递给 macros 的是 AST。它应该以不同的方式处理,可能是显式引用宏的结果并取消引用在引用块中传递的变量。以上是关于使用动态运算符创建 Ecto 查询的主要内容,如果未能解决你的问题,请参考以下文章
Linq 动态查询问题 - 运算符“或”与操作数类型“布尔”和“字符串”不兼容