7

Annotations体が次のいずれかであるすべてを見つけたい:

  • に等しい "?"
  • また
  • お気に入り "[?]"

これを行う最善の方法は何ですか?

可能であれば SearchLogicを使用したいのですが、SearchLogic では次のことを行うことができます。

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

そして、いつでもそれらを連鎖させることができます:Annotation.body_equals('?').body_like('[?]')

それらを と組み合わせる方法がわかりませんOR

引数が同じOR 場合、名前付きスコープを組み合わせることができることに注意してください。たとえば、次のことができます。

 Annotation.body_equals_or_body_like('?')

しかし、これは役に立ちません。

私は SearchLogic に執着していませんが、その抽象化を破る必要のないソリューションには最適です。

4

5 に答える 5

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
于 2009-10-03T22:51:57.200 に答える
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)

上記のコードは、1 つのクエリのみを実行します。 q1クエリの結果を保持しq2ません。むしろ、それらのクラスはActiveRecord::NamedScope::Scope.

named_scope は、これらのorクエリを結合し、条件を 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 ...>]

ora 自体を返すため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...>]

これらの例のほとんどはscope、Rails 3 で s を使用しても問題なく動作すると思いますが、試したことはありません。

ちょっとした恥知らずな自己宣伝 - この機能はfake_arel gemで利用できます。

于 2010-10-27T15:34:51.020 に答える
1

「または」2つのnamed_scopesへの答えを探してこの質問に出くわしましたが、すべての答えが複雑すぎるように見えました。少し調べたところ、「or」と呼ばれる追加のnamed_scopeを使用して解決策を見つけました。

与えられた例に従ってください:

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

両方とも、アノテーション レコードを返す select を構築する named_scope オブジェクトを返します。

次に、次のようなパラメーターとして 2 つの名前付きスコープを想定して、別の名前付きスコープを定義します。

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

その後、次を使用できます。

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

これにより、次のようなクエリが作成されます。

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

あなたが求めていたものはどれですか

or も named_scope であるため、別の or を含む他の named_scope とチェーンすることができます。

Annotation.or(Annotation.or(Annotation.body_equals('?'), 
                            Annotation.body_like('[?]')),
              Annotation.some_other)
于 2011-02-20T00:13:08.647 に答える
1

'like' の結果には 'equals' の結果も含まれませんか?

名前付きスコープを別のスコープの最後に使用して、非常に長い名前付きスコープを作成することもできます。Searchlogic Docs から (この方法は私には少し長ったらしく思えます):

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
于 2009-09-27T23:55:42.860 に答える
0

おそらくそれは

Annotation.body_equals_or_body_like(['?', '[?]'])
于 2011-05-12T11:58:50.863 に答える