在Ecto的“where”中调用我自己的谓词
问题描述:
我希望能够调用一个函数或我在Ecto的“where”中的谓词。我想这样的:在Ecto的“where”中调用我自己的谓词
items = where(Item, [x], Item.my_predicate?(x))
# items = where(Item, [x], Item.my_predicate?(x) == true)
它抛出一个异常:
`Item.my_predicate?(x)` is not a valid query expression
如何解决呢?
答
但是,当然可以使用功能but macros。
例子:
defmodule Foo do
defmacro concat_ws(joiner, columns) do
params_list = "?" |> List.duplicate(Enum.count(columns)) |> Enum.join(",")
quote do
fragment(unquote("concat_ws(?," <> params_list <>")", joiner, unquote_splicing(columns))
end
end
end
items = where(Item, [x], Foo.concat_ws(" ", [x.a, x.b]) == "a b")
例无fragment
:
defmodule Foo do
defmacro eql(a, b) do
quote do: not (is_nil(a) or is_nil(b)) and a == b
end
end
但还是宏观的结果必须是有效的查询AST,所以没有什么花哨可以在那里着陆。
答
我不认为这是可能的。
正如一条路可走,你可以创建一个实现函数来处理其上的记录或作为查询的一部分单独的策略模块:
defmodule CheapItemPolicy do
alias Ecto.Query
import Query, only: [where: 3]
def is_cheap?(%Item{price: price}) do
price in 100..200
end
def is_cheap?(%Query{} = query) do
query
|> where([q], q.price >= 100)
|> where([q], q.price <= 200)
end
end
显然,这只是一条路可走。你可以把所有这些函数放到已经存在的模块中,或者把它们分解成不同的模块或者用相同的逻辑创建一个数据库函数,或者处理应用程序级别的检查或者其他任何事情
答
Lukasz的答案是你所需要的。 Ecto查询是在编译时建立的,所以如果你想使用某些谓词的话,你需要宏。
但我想建议你也可以撰写你的查询。这应该给你的想法:
def my_predicate(query, value) do
from r in query, where: r.some_column == ^value
end
而且使用它像:由位
query = from r in MySchema, where: r.another_column == ^another_value
query |> my_predicate(some_value)
基本上,构建查询一下,使用组合的功能。
PS。我总是使用Ecto查询的“查询”表单。您还可以使用“管道”形式并通过管道查询功能编写查询。
这是不可能的。当你有这样的查询时,它需要翻译成你的数据库查询语言。如果您开始在其中编写任意代码,ecto将无法进行该翻译。 –
如果您向我们提供您想要运行的谓词和您使用的数据库的示例,那么向您提供更多帮助会更容易。 – Hauleth