2015-09-27 72 views
9

这是一个理论问题,但假定数据结构不能更改,而且这绝对是我们想要执行查询的方式。这个问题更多的是为了更好地理解where过滤器将如何动态组合,而不是实际上想从这样的查询中获得结果。假设一个Car表的数据库,其中每辆车都有一个manufacturer_id列,其中包含诸如“BD324”或“GM512”的ID,“BD”&“GM”被视为前缀。使用动态数量的过滤器编写Ecto查询

我们需要在汽车桌上进行查找,以便汽车在前缀匹配给定的前缀集时返回。因此,考虑前缀列表:

prefixes = ["BD", "GM", "EX", "RD", "DE"] 

..we'd想返回有manufacturer_id所列的任何开头的所有汽车。即(如同x LIKE y LIKE z)。

以下药剂/外生代码将寻找一个前缀:

search_prefix = Enum.at(prefixes, 0) <> "%" 
from c in Car, where: like(c.manufacturer_id, ^search_prefix) 

我们将如何着手建立基于prefixes列表中where条款?

回答

4

Ecto似乎缺少一个简单的方法来连接查询的动态部分与OR,但我可能错过了一些东西。如果我错了,请纠正我。

但是,你可以使用等效ANY查询,我要求是既更容易阅读和易于构建:

SELECT * from cars 
WHERE manufacturer_id LIKE ANY(ARRAY['BD%', 'GM%', 'EX%', 'RD%', 'DE%']); 

要创建这种与外生的查询,你可以使用一个fragment

prefixes_like = prefixes |> Enum.map(&"#{&1}%") 
from c in Car, 
    where: fragment("? LIKE ANY(?)", c.manufacturer_id, ^prefixes_like), 
    select: c 
+0

谢谢!向IRC发表José的讲话,这确实是一条路(至少现在)。如果你的要求是AND,而不是OR,那么你可以在acc中使用一个reduce:'Enum.reduce前缀,查询,fn前缀,acc - >,其中:like(q.manufacturer_id,^(前缀< >“%”))end' –

+1

or_where查询是在Ecto 2.1中添加的https://github.com/elixir-ecto/ecto/commit/640e8f5f7f97eab68e4eebcd517803f71d90ebc1#commitcomment-18945603 –

3

你可以认为构成查询作为数据转换:

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) 
+0

感谢您的更新!这是在Ecto 2之前写的,但现在肯定是这样。 –