2015-09-27 115 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之前寫的,但現在肯定是這樣。 –