在 Ecto 的 "where" 中调用我自己的谓词
Calling my own predicate in "where" in Ecto
我希望能够在 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
如何解决?
我觉得不可能。
作为一种方法,您可以创建一个单独的策略模块来实现在记录上或作为查询的一部分处理它的功能:
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
显然这只是一种方式。您可以将所有这些功能放入现有模块或将它们分解为单独的模块或创建具有相同逻辑的数据库功能或处理应用程序级别的检查或其他任何内容。
当然可以,但是你不能使用函数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,所以没有什么特别的东西可以落在那里。
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。我总是使用 "query" 形式进行 Ecto 查询。您还可以使用 "piped" 形式并通过将查询管道化到您的函数中来编写查询。
我希望能够在 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
如何解决?
我觉得不可能。
作为一种方法,您可以创建一个单独的策略模块来实现在记录上或作为查询的一部分处理它的功能:
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
显然这只是一种方式。您可以将所有这些功能放入现有模块或将它们分解为单独的模块或创建具有相同逻辑的数据库功能或处理应用程序级别的检查或其他任何内容。
当然可以,但是你不能使用函数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,所以没有什么特别的东西可以落在那里。
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。我总是使用 "query" 形式进行 Ecto 查询。您还可以使用 "piped" 形式并通过将查询管道化到您的函数中来编写查询。