使用动态数量的 where 过滤器组合 Ecto [v1] 查询
Composing an Ecto [v1] query with a dynamic amount of where filters
这是一个理论问题,但假设数据结构不能改变,并且这绝对是我们想要进行查询的方式。这个问题更多的是为了更好地理解 where
过滤器将如何动态组合,而不是真正想要从这样的查询中获取结果。
假设一个数据库有一辆汽车 table,其中每辆汽车都有一个 manufacturer_id
列,其中包含一个 ID,例如 "BD324" 或 "GM512",其中 "BD" & "GM" 被视为前缀。
我们需要对汽车 table 进行查找,以便当汽车的 manufacturer_id
前缀与给定的一组前缀匹配时,汽车得到 returned。所以,给定一个前缀列表:
prefixes = ["BD", "GM", "EX", "RD", "DE"]
..我们想要 return 所有具有 manufacturer_id
且以 任何 开头的汽车。即 (LIKE x OR LIKE y OR LIKE z).
以下 Elixir/Ecto 代码将搜索一个前缀:
search_prefix = Enum.at(prefixes, 0) <> "%"
from c in Car, where: like(c.manufacturer_id, ^search_prefix)
我们如何根据 prefixes
列表构建 where
子句?
Ecto 似乎缺少一种简单的方法来将查询的动态部分与 OR
连接起来,但我可能错过了一些东西。如有不妥请指正
但是,您可以使用等效的 ANY
查询,我声称它更易于阅读且更易于构建:
SELECT * from cars
WHERE manufacturer_id LIKE ANY(ARRAY['BD%', 'GM%', 'EX%', 'RD%', 'DE%']);
要使用 Ecto 创建这种查询,您可以使用 fragment
:
prefixes_like = prefixes |> Enum.map(&"#{&1}%")
from c in Car,
where: fragment("? LIKE ANY(?)", c.manufacturer_id, ^prefixes_like),
select: c
您可以将组合查询视为数据转换:
prefixes -> query with multiple where conditions
为此,您需要将一种数据结构简化为另一种数据结构,而 Ecto 2 提供了满足您需要的 or_where
。
下面是一个例子:
def filter_by_prefixes(query, prefixes) do
Enum.reduce prefixes, query, fn prefix, query ->
filter_by_prefix(query, prefix)
end
end
def filter_by_prefix(query, prefix) do
or_where(query, [c], like(c.manufacturer_id, ^"#{prefix}%"))
end
# Then build the query
Car |> filter_by_prefixes(prefixes)
这是一个理论问题,但假设数据结构不能改变,并且这绝对是我们想要进行查询的方式。这个问题更多的是为了更好地理解 where
过滤器将如何动态组合,而不是真正想要从这样的查询中获取结果。
假设一个数据库有一辆汽车 table,其中每辆汽车都有一个 manufacturer_id
列,其中包含一个 ID,例如 "BD324" 或 "GM512",其中 "BD" & "GM" 被视为前缀。
我们需要对汽车 table 进行查找,以便当汽车的 manufacturer_id
前缀与给定的一组前缀匹配时,汽车得到 returned。所以,给定一个前缀列表:
prefixes = ["BD", "GM", "EX", "RD", "DE"]
..我们想要 return 所有具有 manufacturer_id
且以 任何 开头的汽车。即 (LIKE x OR LIKE y OR LIKE z).
以下 Elixir/Ecto 代码将搜索一个前缀:
search_prefix = Enum.at(prefixes, 0) <> "%"
from c in Car, where: like(c.manufacturer_id, ^search_prefix)
我们如何根据 prefixes
列表构建 where
子句?
Ecto 似乎缺少一种简单的方法来将查询的动态部分与 OR
连接起来,但我可能错过了一些东西。如有不妥请指正
但是,您可以使用等效的 ANY
查询,我声称它更易于阅读且更易于构建:
SELECT * from cars
WHERE manufacturer_id LIKE ANY(ARRAY['BD%', 'GM%', 'EX%', 'RD%', 'DE%']);
要使用 Ecto 创建这种查询,您可以使用 fragment
:
prefixes_like = prefixes |> Enum.map(&"#{&1}%")
from c in Car,
where: fragment("? LIKE ANY(?)", c.manufacturer_id, ^prefixes_like),
select: c
您可以将组合查询视为数据转换:
prefixes -> query with multiple where conditions
为此,您需要将一种数据结构简化为另一种数据结构,而 Ecto 2 提供了满足您需要的 or_where
。
下面是一个例子:
def filter_by_prefixes(query, prefixes) do
Enum.reduce prefixes, query, fn prefix, query ->
filter_by_prefix(query, prefix)
end
end
def filter_by_prefix(query, prefix) do
or_where(query, [c], like(c.manufacturer_id, ^"#{prefix}%"))
end
# Then build the query
Car |> filter_by_prefixes(prefixes)