Module ActsAsFerret::ClassMethods
In: lib/class_methods.rb

Methods

Public Instance methods

Retrieve the index instance for this model class. This can either be a LocalIndex, or a RemoteIndex instance.

[Source]

     # File lib/class_methods.rb, line 106
106:     def aaf_index
107:       @index ||= ActsAsFerret::get_index(aaf_configuration[:name])
108:     end

re-index a number records specified by the given ids. Use for large indexing jobs i.e. after modifying a lot of records with Ferret disabled. Please note that the state of Ferret (enabled or disabled at class or record level) is not checked by this method, so if you need to do so (e.g. because of a custom ferret_enabled? implementation), you have to do so yourself.

[Source]

    # File lib/class_methods.rb, line 37
37:     def bulk_index(*ids)
38:       options = Hash === ids.last ? ids.pop : {}
39:       ids = ids.first if ids.size == 1 && ids.first.is_a?(Enumerable)
40:       aaf_index.bulk_index(self.name, ids, options)
41:     end

Disables ferret index updates for this model. When a block is given, Ferret will be re-enabled again after executing the block.

[Source]

    # File lib/class_methods.rb, line 7
 7:     def disable_ferret
 8:       aaf_configuration[:enabled] = false
 9:       if block_given?
10:         yield
11:         enable_ferret
12:       end
13:     end

[Source]

    # File lib/class_methods.rb, line 15
15:     def enable_ferret
16:       aaf_configuration[:enabled] = true
17:     end

[Source]

    # File lib/class_methods.rb, line 19
19:     def ferret_enabled?
20:       aaf_configuration[:enabled]
21:     end

Finds instance model name, ids and scores by contents. Useful e.g. if you want to search across models or do not want to fetch all result records (yet).

Options are the same as for find_with_ferret

A block can be given too, it will be executed with every result: find_ids_with_ferret(q, options) do |model, id, score|

   id_array << id
   scores_by_id[id] = score

end NOTE: in case a block is given, only the total_hits value will be returned instead of the [total_hits, results] array!

[Source]

     # File lib/class_methods.rb, line 182
182:     def find_ids_with_ferret(q, options = {}, &block)
183:       aaf_index.find_ids(q, options, &block)
184:     end

Finds instances by searching the Ferret index. Terms are ANDed by default, use OR between terms for ORed queries. Or specify +:or_default => true+ in the +:ferret+ options hash of acts_as_ferret.

You may either use the offset and limit options to implement your own pagination logic, or use the page and per_page options to use the built in pagination support which is compatible with will_paginate‘s view helpers. If page and per_page are given, offset and limit will be ignored.

options:

page:page of search results to retrieve
per_page:number of search results that are displayed per page
offset:first hit to retrieve (useful for paging)
limit:number of hits to retrieve, or :all to retrieve all results
lazy:Array of field names whose contents should be read directly from the index. Those fields have to be marked +:store => :yes+ in their field options. Give true to get all stored fields. Note that if you have a shared index, you have to explicitly state the fields you want to fetch, true won‘t work here)

find_options is a hash passed on to active_record‘s find when retrieving the data from db, useful to i.e. prefetch relationships with :include or to specify additional filter criteria with :conditions.

This method returns a SearchResults instance, which really is an Array that has been decorated with a total_hits attribute holding the total number of hits. Additionally, SearchResults is compatible with the pagination helper methods of the will_paginate plugin.

Please keep in mind that the number of results delivered might be less than limit if you specify any active record conditions that further limit the result. Use limit and offset as AR find_options instead. page and per_page are supposed to work regardless of any conitions present in find_options.

[Source]

     # File lib/class_methods.rb, line 147
147:     def find_with_ferret(q, options = {}, find_options = {})
148:       if respond_to?(:scope) && scope(:find, :conditions)
149:         if find_options[:conditions]
150:           find_options[:conditions] = "(#{find_options[:conditions]}) AND (#{scope(:find, :conditions)})"
151:         else
152:           find_options[:conditions] = scope(:find, :conditions)
153:         end
154:       end
155:       return ActsAsFerret::find q, self, options, find_options
156:     end

rebuild the index from all data stored for this model, and any other model classes associated with the same index. This is called automatically when no index exists yet.

[Source]

    # File lib/class_methods.rb, line 27
27:     def rebuild_index
28:       aaf_index.rebuild_index
29:     end

yields the records with the given ids, in batches of batch_size

[Source]

     # File lib/class_methods.rb, line 91
 91:     def records_for_bulk_index(ids, batch_size = 1000)
 92:       transaction do
 93:         offset = 0
 94:         ids.each_slice(batch_size) do |id_slice|
 95:           records = find( :all, :conditions => ["id in (?)", id_slice] )
 96:           #yield records, offset
 97:           yield find( :all, :conditions => ["id in (?)", id_slice] ), offset
 98:           offset += batch_size
 99:         end
100:       end
101:     end

runs across all records yielding those to be indexed when the index is rebuilt

[Source]

    # File lib/class_methods.rb, line 73
73:     def records_for_rebuild(batch_size = 1000)
74:       transaction do
75:         if use_fast_batches?
76:           offset = 0
77:           while (rows = find :all, :conditions => [ "#{table_name}.id > ?", offset ], :limit => batch_size).any?
78:             offset = rows.last.id
79:             yield rows, offset
80:           end
81:         else
82:           order = "#{primary_key} ASC" # fixes #212
83:           0.step(self.count, batch_size) do |offset|
84:             yield find( :all, :limit => batch_size, :offset => offset, :order => order ), offset
85:           end
86:         end
87:       end
88:     end

Returns all records modified or created after the specified time. Used by the rake rebuild task to find models that need to be updated in the index after the rebuild finished because they changed while the rebuild was running. Override if your models don‘t stick to the created_at/updated_at convention.

[Source]

    # File lib/class_methods.rb, line 59
59:     def records_modified_since(time)
60:       condition = []
61:       %w(updated_at created_at).each do |col|
62:         condition << "#{col} >= ?" if column_names.include? col
63:       end
64:       if condition.empty?
65:         logger.warn "#{self.name}: Override records_modified_since(time) to keep the index up to date with records changed during rebuild."
66:         []
67:       else
68:         find :all, :conditions => [ condition.join(' AND '), *([time]*condition.size) ]
69:       end
70:     end

Returns the total number of hits for the given query

Note that since we don‘t query the database here, this method won‘t deliver the expected results when used on an AR association.

[Source]

     # File lib/class_methods.rb, line 164
164:     def total_hits(q, options={})
165:       aaf_index.total_hits(q, options)
166:     end

true if our db and table appear to be suitable for the mysql fast batch hack (see weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord)

[Source]

    # File lib/class_methods.rb, line 46
46:     def use_fast_batches?
47:       if connection.class.name =~ /Mysql/ && primary_key == 'id' && aaf_configuration[:mysql_fast_batches]
48:         logger.info "using mysql specific batched find :all. Turn off with  :mysql_fast_batches => false if you encounter problems (i.e. because of non-integer UUIDs in the id column)"
49:         true
50:       end
51:     end

Protected Instance methods

def find_records_lazy_or_not(q, options = {}, find_options = {})

  if options[:lazy]
    logger.warn "find_options #{find_options} are ignored because :lazy => true" unless find_options.empty?
    lazy_find_by_contents q, options
  else
    ar_find_by_contents q, options, find_options
  end

end

def ar_find_by_contents(q, options = {}, find_options = {})

  result_ids = {}
  total_hits = find_ids_with_ferret(q, options) do |model, id, score, data|
    # stores ids, index and score of each hit for later ordering of
    # results
    result_ids[id] = [ result_ids.size + 1, score ]
  end

  result = ActsAsFerret::retrieve_records( { self.name => result_ids }, find_options )

  # count total_hits via sql when using conditions or when we're called
  # from an ActiveRecord association.
  if find_options[:conditions] or caller.find{ |call| call =~ %r{active_record/associations} }
    # chances are the ferret result count is not our total_hits value, so
    # we correct this here.
    if options[:limit] != :all || options[:page] || options[:offset] || find_options[:limit] || find_options[:offset]
      # our ferret result has been limited, so we need to re-run that
      # search to get the full result set from ferret.
      result_ids = {}
      find_ids_with_ferret(q, options.update(:limit => :all, :offset => 0)) do |model, id, score, data|
        result_ids[id] = [ result_ids.size + 1, score ]
      end
      # Now ask the database for the total size of the final result set.
      total_hits = count_records( { self.name => result_ids }, find_options )
    else
      # what we got from the database is our full result set, so take
      # it's size
      total_hits = result.length
    end
  end

  [ total_hits, result ]

end

def lazy_find_by_contents(q, options = {})

  logger.debug "lazy_find_by_contents: #{q}"
  result = []
  rank   = 0
  total_hits = find_ids_with_ferret(q, options) do |model, id, score, data|
    logger.debug "model: #{model}, id: #{id}, data: #{data}"
    result << FerretResult.new(model, id, score, rank += 1, data)
  end
  [ total_hits, result ]

end

[Source]

     # File lib/class_methods.rb, line 244
244:     def model_find(model, id, find_options = {})
245:       model.constantize.find(id, find_options)
246:     end

[Validate]