将两个命名范围与OR组合(而不是AND)

Tom*_*man 7 ruby-on-rails searchlogic

我想找到所有Annotations身体都是:

  • 等于 "?"
  • 要么
  • 喜欢 "[?]"

最好的方法是什么?

我想尽可能使用SearchLogic,但是SearchLogic允许您执行以下各项操作:

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

你总是可以将它们连在一起: Annotation.body_equals('?').body_like('[?]')

我不确定如何将它们结合起来OR.

请注意,如果命名范围与参数相同,OR 可以将它们组合在一起.我可以这样做:

 Annotation.body_equals_or_body_like('?')
Run Code Online (Sandbox Code Playgroud)

但这没有用.

请注意,我没有附加到SearchLogic,但对于不需要破坏其抽象的解决方案来说,它会很棒.

Pet*_*net 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
Run Code Online (Sandbox Code Playgroud)

请考虑以下设置:

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
Run Code Online (Sandbox Code Playgroud)

您可以使用一系列范围的名称来调用它:

Person.or_scopes(:women, :children)
Run Code Online (Sandbox Code Playgroud)

这将返回如下范围:

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

当范围需要参数时,您也可以使用数组数组调用它:

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

在您的情况下,您可以使用以下内容:

Annotation.or_scopes([:body_equals, '?'], [:body_like, '[?']).all
Run Code Online (Sandbox Code Playgroud)


小智 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%'"
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用并集来组合搜索结果数组,同时删除重复项:

@equal_results = Annotation.body_equals('?')
@like_results  = Annotation.body_like('[?]')
@results = @equal_results | @like_results
Run Code Online (Sandbox Code Playgroud)