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
234
# 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
      attribute_name = attribute_name.gsub(/_before_type_cast$/, '').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}
}


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

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.



304
305
306
# File 'lib/sphinxsearchlogic.rb', line 304

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)


273
274
275
276
277
# File 'lib/sphinxsearchlogic.rb', line 273

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)


287
288
289
# File 'lib/sphinxsearchlogic.rb', line 287

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)


292
293
294
295
296
# File 'lib/sphinxsearchlogic.rb', line 292

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)


299
300
301
# File 'lib/sphinxsearchlogic.rb', line 299

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'}
}


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

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.



280
281
282
283
284
# File 'lib/sphinxsearchlogic.rb', line 280

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.



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

def sphinx_index
  klass.define_indexes if klass.sphinx_indexes.blank?
  klass.sphinx_indexes.first
end

#type_cast(value, type) ⇒ Object

type_cast method of Searchlogic plugin



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

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