2011-06-29 47 views
5

一個模型有兩個感興趣的屬性,forename城鎮。我想尋找理查德(倫敦)和布賴恩(馬德里)。Rails/Arel - 組合AREL謂詞時的優先級

在長手,

p=Person.scoped 
pred1=p.table[:forename].eq('Richard').and(p.table[:town].eq('London')) 
pred2=p.table[:forename].eq('Brian').and(p.table[:town].eq('Madrid')) 
pred3=pred1.or(pred2) 

我希望這個包裹謂詞在括號中保持查詢的完整性。但看着pred3.to_sql給出了意想不到的迴應:

"(`person`.`forename` = 'Richard' AND `person`.`town` = 'London' OR `person`.`forename` = 'Brian' AND `person`.`town` = 'Madrid')" 

如何讓Arel生成正確的查詢?

+1

您是否曾經找到過解決方案?我遇到同樣的問題,建議的答案不起作用。 – CrashRoX

+0

@CrashRoX,我已經在下面發佈了一個答案。 – John

回答

2

這實際上正確的查詢,如,具有較高的運算符優先級高於或大多數SQL方言。如果要翻轉它並AND 2 OR謂詞,它會將OR謂詞包裝爲paren。

t = Arel::Table.new('blah') 

a = (t[:a].eq(nil).and(t[:b].eq(nil))) 
b = (t[:a].not_eq(nil).and(t[:b].not_eq(nil))) 
a.or(b).to_sql 

    => "(`blah`.`a` IS NULL AND `blah`.`b` IS NULL OR `blah`.`a` IS NOT NULL AND `blah`.`b` IS NOT NULL)" 

a = (t[:a].eq(nil).or(t[:b].eq(nil))) 
b = (t[:a].not_eq(nil).or(t[:b].not_eq(nil))) 
a.and(b).to_sql 

    => "(`blah`.`a` IS NULL OR `blah`.`b` IS NULL) AND (`blah`.`a` IS NOT NULL OR `blah`.`b` IS NOT NULL)" 
0

使用arel_table嘗試直接

person_table = Person.arel_table 
pred1 = person_table[:forename].eq("Richard").and(person_table[:town].eq("London")) 
pred2 = person_table[:forename].eq("Rick").and(person_table[:town].eq("US")) 
pred1.or(pred2).to_sql 

這就產生 (( 「人」。 「用的名字」= '理查' 和 「人」, 「鎮」= '倫敦')或( 「人」。 「用的名字」 =「裏克」和「人」,「鎮」 =「美國」))

1

For Rails 3.2.13,請使用Arel::Nodes::Grouping。另見FactoryMethods#grouping-instance_method RubyDoc

pt = Person.arel_table 
pred1 = person_table[:forename].eq("Richard").and(person_table[:town].eq("London")) 
pred2 = person_table[:forename].eq("Rick").and(person_table[:town].eq("US")) 
pt.grouping(pred1).or(pt.grouping(pred2)).to_sql 

=> (("people"."forename" = 'Richard' AND "people"."town" = 'London') OR ("people"."forename" = 'Rick' AND "people"."town" = 'US'))