Class: Sphinxsearchlogic::Search

Inherits:
Object
  • Object
show all
Includes:
Ordering, Pagination
Defined in:
lib/sphinxsearchlogic.rb

Defined Under Namespace

Modules: Implementation, Ordering, Pagination Classes: UnknownConditionError

Instance Attribute Summary collapse

Attributes included from Ordering

#order, #order_attribute, #order_direction

Attributes included from Pagination

#max_matches, #page, #per_page

Instance Method Summary collapse

Methods included from Ordering

#ordering_options

Methods included from Pagination

#default_per_page, #last_page, #offset, #pagination_options

Constructor Details

#initialize(klass, current_scope, params = {}) ⇒ Search

Creates a new search object for the given class. Ex:

Searchlogic::Search.new(User, {}, {:username_like => "bjohnson"})


133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/sphinxsearchlogic.rb', line 133

def initialize(klass, current_scope, params = {})
  @with = {}
  @without = {}
  @with_all = {}
  @conditions = {}
  @scopes = {}

  self.klass = klass
  raise "No Sphinx indexes found on #{klass.to_s}!" unless has_sphinx_index?
  self.current_scope = current_scope
  self.params = params if params.is_a?(Hash)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

Handles (in order):

  • with / without / with_all conditions (Filters)

  • field conditions (Regular searches)

  • scope conditions



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/sphinxsearchlogic.rb', line 181

def method_missing(name, *args, &block)
  name = name.to_s
  if name =~ /^(\w+)=$/ # If we have a setter
    name = $1
    if name =~ /^with(out|_all)?_(\w+)$/
      attribute_name = $2.to_sym
      if is_sphinx_attribute?(attribute_name)
        # Put in with / without / with_all depending on what the regexp matched.
        if $1 == 'out'
          without[attribute_name] = type_cast(args.first, cast_type(attribute_name))
        elsif $1 == '_all'
          with_all[attribute_name] = type_cast(args.first, cast_type(attribute_name))
        else
          with[attribute_name] = type_cast(args.first, cast_type(attribute_name))
        end
      else
        raise UnknownConditionError.new(attribute_name)
      end
    elsif is_sphinx_field?(name)
      conditions[name.to_sym] = args.first
    elsif is_sphinx_scope?(name)
      scopes[name.to_sym] = args.first
    else
      # If we have an unknown setter..
      # raise UnknownConditionError.new(attribute_name)
      super
    end
  else
    if name =~ /^with(out|_all)?_(\w+)$/
      attribute_name = $2.to_sym
      if is_sphinx_attribute?(attribute_name)
        # Put in with / without / with_all depending on what the regexp matched.
        if $1 == 'out'
          without[attribute_name]
        elsif $1 == '_all'
          with_all[attribute_name]
        else
          with[attribute_name]
        end
      else
        raise UnknownConditionError.new(attribute_name)
      end
    elsif is_sphinx_field?(name)
      conditions[name.to_sym]
    elsif is_sphinx_scope?(name)
      scopes[name.to_sym]
    else
      # If we have something else than a setter..
      # raise UnknownConditionError.new(attribute_name)
      super
    end
  end
end

Instance Attribute Details

#allObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def all
  @all
end

#conditionsObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def conditions
  @conditions
end

#current_scopeObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def current_scope
  @current_scope
end

#klassObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def klass
  @klass
end

#paramsObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def params
  @params
end

#scopesObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def scopes
  @scopes
end

#withObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def with
  @with
end

#with_allObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def with_all
  @with_all
end

#withoutObject

Accessors that define this Search object.



39
40
41
# File 'lib/sphinxsearchlogic.rb', line 39

def without
  @without
end

Instance Method Details

#attribute_filter_optionsObject

Returns a hash for the ThinkingSphinx search method. Eg.

{
  :with => {:year => 2001}
}


239
240
241
242
243
244
245
# File 'lib/sphinxsearchlogic.rb', line 239

def attribute_filter_options
  options = {}
  options[:with] = with unless with.blank?
  options[:without] = without unless without.blank?
  options[:with_all] = with_all unless with_all.blank? # See http://www.mailinglistarchive.com/[email protected]/msg00351.html
  options
end

#cast_type(name) ⇒ Object

Returns the type we should type_cast a ThinkingSphinx::Attribute to, eg. :integer.



302
303
304
# File 'lib/sphinxsearchlogic.rb', line 302

def cast_type(name)
  sphinx_attribute(name).type
end

#has_sphinx_index?Boolean

Returns true if the class of this Search has a Sphinx index.

Returns:

  • (Boolean)


271
272
273
274
275
# File 'lib/sphinxsearchlogic.rb', line 271

def has_sphinx_index?
  sphinx_index.is_a?(ThinkingSphinx::Index)
rescue
  false
end

#is_sphinx_attribute?(attribute_name) ⇒ Boolean

Returns true if the class of this search has a public attribute with this name (or name_sort if field is :sortable).

Returns:

  • (Boolean)


285
286
287
# File 'lib/sphinxsearchlogic.rb', line 285

def is_sphinx_attribute?(attribute_name)
  !!sphinx_attribute(attribute_name)
end

#is_sphinx_field?(field_name) ⇒ Boolean

Returns true if the class of this search has a public field with this name.

Returns:

  • (Boolean)


290
291
292
293
294
# File 'lib/sphinxsearchlogic.rb', line 290

def is_sphinx_field?(field_name)
  !sphinx_index.fields.find do |index_field|
    index_field.public? && (index_field.unique_name.to_s == field_name.to_s)
  end.nil?
end

#is_sphinx_scope?(scope_name) ⇒ Boolean

Returns true if class of this search has a sphinx scope with this name.

Returns:

  • (Boolean)


297
298
299
# File 'lib/sphinxsearchlogic.rb', line 297

def is_sphinx_scope?(scope_name)
  klass.sphinx_scopes.include?(scope_name.to_sym)
end

#resultsObject

Returns actual search results.

Movie.sphinxsearchlogic.results


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/sphinxsearchlogic.rb', line 157

def results
  Rails.logger.debug("Sphinxsearchlogic: #{klass.to_s}.search('#{all}', #{search_options.inspect})")
  if scopes.empty?
    klass.search(all, search_options)
  else
    cloned_scopes = scopes.clone # Clone scopes since we're deleting form the hash.
    # Get the first scope and call all others on this one..
    first_scope = cloned_scopes.keys.first
    first_args = cloned_scopes.delete(first_scope)
    result = klass.send(first_scope, first_args)
    # Call remaining scopes on this scope.
    cloned_scopes.each do |scope, args|
      result = result.send(scope, args)
    end
    result.search(all, search_options)
  end
end

#search_optionsObject

Returns a hash for the ThinkingSphinx search method. Eg.

{
  :conditions => {:name => 'John'}
}


251
252
253
254
255
# File 'lib/sphinxsearchlogic.rb', line 251

def search_options
  options = {}
  options[:conditions] = conditions unless conditions.blank?
  options.merge(attribute_filter_options).merge(ordering_options).merge(pagination_options)
end

#sphinx_attribute(attribute_name) ⇒ Object

Returns particular ThinkingSphinx::Attribute.



278
279
280
281
282
# File 'lib/sphinxsearchlogic.rb', line 278

def sphinx_attribute(attribute_name)
  sphinx_index.attributes.find do |index_attribute|
    index_attribute.public? && index_attribute.unique_name.to_s =~ /^#{attribute_name}(_sort)?$/ # Also check for :sortable attributes (they are given prefix _sort)
  end
end

#sphinx_indexObject

Returns the ThinkingSphinx index for the klass we search on.



266
267
268
# File 'lib/sphinxsearchlogic.rb', line 266

def sphinx_index
  klass.sphinx_indexes.first
end

#type_cast(value, type) ⇒ Object

type_cast method of Searchlogic plugin



307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/sphinxsearchlogic.rb', line 307

def type_cast(value, type)
  case value
  when Array
    value.collect { |v| type_cast(v, type) }
  else
    # Let's leverage ActiveRecord's type casting, so that casting is consistent
    # with the other models.
    column_for_type_cast = ::ActiveRecord::ConnectionAdapters::Column.new("", nil)
    column_for_type_cast.instance_variable_set(:@type, type)
    value = column_for_type_cast.type_cast(value)
    Time.zone && value.is_a?(Time) ? value.in_time_zone : value
  end
end