2009-09-27 20 views
7

(yerine AND ait) Ben bulmak istiyorum vücutlarınızın ya hepsi Annotations: "?"iki adlandırılmış kapsamları ile birleştirin veya

  • Eşit için
  • veya
  • gibi "[?]"

Bunu yapmanın en iyi yolu nedir?

Ben mümkünse SearchLogic kullanmak istiyorum fakat SearchLogic aşağıdakilerden her yapmanızı sağlar olsa:

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

ve her zaman onları birlikte zincir: Annotation.body_equals('?').body_like('[?]')

Bunlarıile nasıl birleştireceğimi bilmiyorum.

Annotation.body_equals_or_body_like('?') 

Ama bu yardımcı olmaz:

Note that you can combine named scopes with ORif their argument is the same. Ör yapabilirdim.

SearchLogic'e bağlı olmadığımı unutmayın, ancak soyutlamayı kesmeyi gerektirmeyen bir çözüm için harika olur.

+0

bakın Aynı soruya cevabım [işte] (http://stackoverflow.com/a/40269481/1876622). Ayrıca benzer sorulara [burada] dikkat edin (http://stackoverflow.com/questions/37445203/) ve [here] (http://stackoverflow.com/questions/16381619/) – HeyZiko

cevap

1

'Beğen' sonuçları da 'eşittir' sonuçlarını içermez mi?

Çok uzun bir kapsam oluşturmak için, bir başka adın sonunda adlandırılmış bir alan adı da kullanabilirsiniz.çiftleri kaldırılırken arama sonuç dizileri birleştirmek için bir birlik kullanabilirsiniz

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%'" 

Veya: Searchlogic Dokümanlar itibaren (bu şekilde bana longwinded biraz görünüyor)

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

yapamadım

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 

aşağıdaki kurulum düşünün::

herhangi basit çözümler bulmak, ama bu sorun ilgimi yüzden kendi çözüm haddelenmiş
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 

Aşağıdaki gibi kapsamları bir dizi isimlerle diyoruz:

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

Ayrıca diziler dizisi ile diyebilirsin:

Person.or_scopes(:women, :children) 

Bu böyle bir kapsam döndürür kapsam parametreleri gerektirdiğinde:

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

senin durumunda Horace, kullanmak olabilir Aşağıdaki:

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

Bunu bir eklenti olarak yayınlamaya karar verdim GitHub burada: http://github.com/rxcfc/or_scopes –

1

Raylar 2.x için, YA simüle aşağıdaki adlandırılmış kapsamı kullanabilirsiniz:

__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 

en yukarıdaki örneği kullanarak bu işlevi kullanalım.

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

Yukarıdaki kod yalnızca bir sorgu yürütür. q1 ve q2 sorgunun sonuçlarını tutmayın, bunun yerine, sınıfları ActiveRecord::NamedScope::Scope olur.

named_scope or bu sorguları birleştirir ve koşulları bir OR ile birleştirir.

Ayrıca yuva OO'lar gibi bu yapmacık örnekte olabilir:

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 Çünkü getiri bir ActiveRecord::NamedScope::Scope kendisi, gerçekten Çıldırabiliriz:

# 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...>] 

İnanıyorum ki bu örneklerin çoğunda denemedim rağmen Rails 3 scope s kullanarak iyi çalışır.

A Ufak olmayan kişisel tanıtım - Bu işlev fake_arel gem numaralı telefondan edinilebilir.

+0

Tam olarak ihtiyacım olan buydu, teşekkür ederim! –

1

Bu soruya, "ya da" iki tane adlandırılmış_sümün cevabını aradım ve tüm cevaplar bana çok karmaşık görünüyordu. Ben biraz araştırdım ve hile yapan "veya" adlı ek adlandırılmış bir_scope kullanarak bir çözüm buldum.Verilen örnekte ardından

: Her iki açıklama kayıtlarını şimdi

dönen seçer inşa bir named_scope nesneyi döndürmek

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

biz gibi parametreler olarak iki adlandırılmış kapsamları bekliyor başka adlandırılmış kapsamını tanımlamak:

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})})" 
}} 

Daha sonra kullanabilirsiniz:

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

Bu gibi bir sorgu oluşturur:

: Eğer

gibi

sonra ne olduğunu ya da bir named_scope Hangi

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

, diğer named_scopes başka veya dahil olan zincire mümkündür

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

Muhtemelen öyle

Annotation.body_equals_or_body_like(['?', '[?]']) 
İlgili konular