2013-01-03 31 views
4

首先,我使用Rails v3.2.9和Squeel 1.0.13,以下是我想要做的:使用Squeel時,您可以在有條件的地方添加子句嗎?

我想要使用三種標識信息中的任何一種來搜索客戶端 - 名稱,出生日期(dob)和社會保險號碼(罪惡)。結果集必須包含任何具有任何標識符的記錄 - 條件的OR。我之前在Squeel做過這件事,它看起來像這樣:

scope :by_any, ->(sin, name, dob){ where{(client.sin == "#{sin}") | (client.name =~ "%#{name}%") | (client.dob == "#{dob}")} } 

只要我提供所有的標識符,這工作正常。但是如果我只有一個名字呢?以上範圍的結果:

SELECT "clients".* FROM "clients" WHERE ((("clients"."sin" IS NULL OR "clients"."name" ILIKE '%John Doe%') OR "clients"."dob" IS NULL)) 

這包括組客戶端罪在那裏是空和客戶端組,其中DOB是與像「李四」的名稱所要求的一組客戶端沿空。

因此,請輸入我的嘗試,以有條件地向where塊添加子句。起初,我試圖檢查使用零的值?方法:

def self.by_any (sin, name, dob) 
    where do 
    (clients.sin == "#{sin}" unless sin.nil?) | 
    (clients.name =~ "%#{name}" unless name.nil?) | 
    (clients.dob == "#{dob}" unless dob.nil?) 
    end 

導致:

SELECT "clients".* FROM "clients" WHERE ('t') 

提出許多其他問題一樣,這是怎麼回事與「T」,但是這是一個切線。

爲每個排列寫入where子句的缺點,有沒有一種方法可以有條件地添加子句?

回答

3

因此,這不是有史以來最漂亮的事情,但它確實如此。

def self.by_any(sin, name, dob) 
    where do 
    [ 
     sin.presence && clients.sin == "#{sin}", 
     name.presence && clients.name =~ "%#{name}", 
     dob.presence && clients.dob == "#{dob}" 
    ].compact.reduce(:|) 
    # compact to remove the nils, reduce to combine the cases with | 
    end 
end 

基本上,[a, b, c].reduce(:f)返回(a.f(b)).f(c)。在這種情況下,調用的方法是管道,因此我們得到(a.|(b)).|(c),其中較不混淆的符號是(a | b) | c

它的工作原理,因爲在Squeel,謂語運營商(===~,等等)返回一個Predicate節點,所以我們可以|加入他們之前分別構建它們。

在全部三個都是nil的情況下,它返回所有記錄。

+0

更漂亮,我實際上想出了。從這裏偷了一下[鏈接](http://stackoverflow.com/questions/8045823/how-do-i-write-a-union-chain-with-activerelation)拿出我的答案,但我想我喜歡你更好。我將不得不緊湊和減少。 – erroric

+0

感謝您對「減少」的解釋 - 我將來會再次使用它。 – erroric

+0

我得到一個「語法錯誤,意想不到的modifier_if,期待']'」在數組創建循環中的if語句之前?任何指針讚賞。 – Conor

0

輾轉this related post後,我拆解@bradgonesurfing的交替圖案得出這樣的解決方案:

def self.by_any (sin, name, dob) 
    queries = Array.new 
    queries << self.by_sin(sin) unless sin.nil? 
    queries << self.by_name(name) unless name.nil? 
    queries << self.by_dob(dob) unless dob.nil? 

    self.where do 
    queries = queries.map { |q| id.in q.select{id} } 
    queries.inject { |s, i| s | i } 
    end 
end 

其中self.by_sinself.by_name,和self.by_dob是簡單的範圍與過濾器。這產生了一些沿線:

SELECT * 
FROM clients 
WHERE clients.id IN (<subquery for sin>) 
    OR clients.id IN (<subquery for name>) 
    OR clients.id IN (<subquery for dob>) 

其中子查詢只包括如果其相關的值不是零。

這有效地允許我將適當的作用域合併爲一個ActiveRecord :: Relation。

相關問題