2009-09-27 23 views
7

我想找到所有Annotations其機構或者:「」組合兩個命名範圍與OR(而不是AND)

  • 等於
  • 像 「[?]」

什麼是做到這一點的最好方法是什麼?

我想如果可能的話用SearchLogic,但儘管SearchLogic允許你做以下每一項:

  • Annotation.body_equals('?')
  • Annotation.body_like('[?]')

,你可以隨時把它們連在一起: Annotation.body_equals('?').body_like('[?]')

我不知道如何將它們與OR

Note that you can combine named scopes with ORif their argument is the same.例如,我可以這樣做:

Annotation.body_equals_or_body_like('?') 

但是,這不會幫助。

請注意,我沒有附加到SearchLogic,但對於不需要破壞其抽象的解決方案而言,這非常棒。

+0

見我對同一個問題的回答[這裏](http://stackoverflow.com/a/40269481/1876622)。另外請注意類似的問題[這裏](http://stackoverflow.com/questions/37445203/)和[這裏](http://stackoverflow.com/questions/16381619/) – HeyZiko 2016-10-26 20:54:44

回答

1

「喜歡」結果是否也包含「等於」結果?

您也可以在另一個末尾使用命名範圍來創建一個非常長的命名範圍。從Searchlogic文件(這樣似乎有點冗長我):

User.username_or_first_name_like("ben") 
=> "username LIKE '%ben%' OR first_name like'%ben%'" 

User.id_or_age_lt_or_username_or_first_name_begins_with(10) 
=> "id < 10 OR age < 10 OR username LIKE 'ben%' OR first_name like'ben%'" 

或者你可以使用一個聯盟的搜索結果陣列組合,同時刪除重複項:

@equal_results = Annotation.body_equals('?') 
@like_results = Annotation.body_like('[?]') 
@results = @equal_results | @like_results 
9

我做不到找到任何簡單的解決方案,但這個問題吸引了我,讓我滾我自己的解決方案:

class ActiveRecord::Base 

    def self.or_scopes(*scopes) 
    # Cleanup input 
    scopes.map! do |scope| 
     scope = scope.respond_to?(:to_a) ? scope.to_a : [*scope] 
     scope.unshift(scope.shift.to_sym) 
    end 

    # Check for existence of scopes 
    scopes.each{|scope| raise ArgumentError, "invalid scope: #{scope.first}" unless self.scopes.has_key?(scope.first) } 

    conditions = scopes.map do |scope| 
     scope = self.scopes[scope.first].call(self, *scope[1..-1]) 
     self.merge_conditions(scope.proxy_options[:conditions]) 
    end 

    or_conditions = conditions.compact.join(" OR ") 

    merged_scopes = scopes.inject(self){|merged, scope| merged.scopes[scope.first].call(self, *scope[1..-1]) } 

    # We ignore other scope types but so does named_scopes 
    find_options = merged_scopes.scope(:find).merge(:conditions => or_conditions) 

    self.scoped(find_options) 
    end 

end 

考慮以下設置:

class Person < ActiveRecord::Base 
    named_scope :men,  :conditions => { :sex => 'M' } 
    named_scope :women, :conditions => { :sex => 'F' } 
    named_scope :children, :conditions => "age < 18" 
    named_scope :named, lambda{|name| 
    { :conditions => { :name => name } } 
    } 
end 

你用系列示波器的名字稱呼它爲這樣:

Person.or_scopes(:women, :children) 

這將返回這樣一個範圍:

Person.or_scopes(:women, :children).proxy_options 
# => {:conditions=>"(`people`.`sex` = 'F') OR (age < 18)"} 

您也可以使用數組的數組叫它當範圍需要參數:

Person.or_scopes(:women, [:named, 'Sue']).proxy_options 
# => {:conditions=>"(`people`.`sex` = 'F') OR (`people`.`name` = 'Sue')"} 

在你的情況下,您可以使用Horace以下內容:

Annotation.or_scopes([:body_equals, '?'], [:body_like, '[?']).all 
+0

我決定發佈這個插件在GitHub上:http://github.com/rxcfc/or_scopes – 2009-10-03 23:23:57

1

對於Rails的2.X,你可以使用下面的命名範圍模擬OR:

__or_fn = lambda do |*scopes| 
    where = [] 
    joins = [] 
    includes = [] 

    # for some reason, flatten is actually executing the scope 
    scopes = scopes[0] if scopes.size == 1 
    scopes.each do |s| 
     s = s.proxy_options 
     begin 
     where << merge_conditions(s[:conditions]) 
     rescue NoMethodError 
     where << scopes[0].first.class.merge_conditions(s[:conditions]) 
     end 
     joins << s[:joins] unless s[:joins].nil? 
     includes << s[:include] unless s[:include].nil? 
    end 
    scoped = self 
    scoped = scoped.includes(includes.uniq.flatten) unless includes.blank? 
    scoped = scoped.joins(joins.uniq.flatten) unless joins.blank? 
    scoped.where(where.join(" OR ")) 
    end 
    named_scope :or, __or_fn 

讓我們使用此功能使用你上面的例子。

q1 = Annotation.body_equals('?') 
q2 = Annotation.body_like('[?]') 
Annotation.or(q1,q2) 

上面的代碼只執行一個查詢。 q1q2不保留查詢的結果,而是他們的類是ActiveRecord::NamedScope::Scope

or named_scope結合了這些查詢並將條件與OR結合。

您還巢OR值,就像這個人爲的例子:

rabbits = Animal.rabbits 
#<Animal id: 1 ...> 
puppies = Animal.puppies 
#<Animal id: 2 ...> 
snakes = Animal.snakes 
#<Animal id: 3 ...> 
lizards = Animal.lizards 
#<Animal id: 4 ...> 

Animal.or(rabbits, puppies) 
[#<Animal id: 1 ...>, #<Animal id: 2 ...>] 
Animal.or(rabbits, puppies, snakes) 
[#<Animal id: 1 ...>, #<Animal id: 2 ...>, #<Animal id: 3 ...>] 

因爲or回報ActiveRecord::NamedScope::Scope本身,我們可以去真的瘋了:

# now let's get crazy 
or1 = Animal.or(rabbits, puppies) 
or2 = Animal.or(snakes, lizards) 
Animal.or(or1, or2) 
[#<Animal id: 1 ...>, #<Animal id: 2 ...>, #<Animal id: 3 ...>, #<Animal id: 4...>] 

我相信大多數的這些例子在Rails 3中使用scope會很好,儘管我還沒有嘗試過。

一點無恥的自我推銷 - 該功能可在fake_arel gem

+0

這正是我需要的,謝謝! – 2011-02-18 09:56:54

1

我過來了這個問題尋找「或」兩個named_scopes的答案,所有的答案看起來太複雜了。我調查了一下,並找到了一個解決方案,使用一個叫做「或」的額外的named_scope來實現。

按照給定的例子:

Annotation.body_equals('?') 
Annotation.body_like('[?]') 

都返回一個named_scope對象構造選擇返回註釋記錄

現在我們定義了另一個名爲範圍預計兩個命名的範圍作爲參數,如:

named_scope :or, lambda { |l, r| { 
    :conditions => 
     "annotations.id IN (#{l.send(:construct_finder_sql,{:select => :id})}) or " + 
     "annotations.id IN (#{r.send(:construct_finder_sql,{:select => :id})})" 
}} 
您可以隨後使用:

這將創建一個類似的查詢:

select * from annotations 
where (annotations.id IN (select id from annotations where body='?') or 
     (annotations.id IN (select id from annotations where body like '%?%') 

哪個是你是什麼

由於後或也named_scope,也可以與其他named_scopes包括另一個或鏈:

Annotation.or(Annotation.or(Annotation.body_equals('?'), 
          Annotation.body_like('[?]')), 
       Annotation.some_other) 
0

也許這是

Annotation.body_equals_or_body_like(['?', '[?]'])