3 # class methods for classes using acts_as_ferret :single_index => true
4 module SharedIndexClassMethods
6 def find_id_by_contents(q, options = {}, &block)
7 # add class name scoping to query if necessary
8 unless options[:models] == :all # search needs to be restricted by one or more class names
9 options[:models] ||= []
10 # add this class to the list of given models
11 options[:models] << self unless options[:models].include?(self)
15 if original_query.is_a? String
16 model_query = options[:models].map(&:name).join '|'
17 q += %{ +class_name:"#{model_query}"}
19 q = Ferret::Search::BooleanQuery.new
20 q.add_query(original_query, :must)
21 model_query = Ferret::Search::BooleanQuery.new
22 options[:models].each do |model|
23 model_query.add_query(Ferret::Search::TermQuery.new(:class_name, model.name), :should)
25 q.add_query(model_query, :must)
28 options.delete :models
30 super(q, options, &block)
33 # Overrides the standard find_by_contents for searching a shared index.
35 # please note that records from different models will be fetched in
36 # separate sql calls, so any sql order_by clause given with
37 # find_options[:order] will be ignored.
38 def find_by_contents(q, options = {}, find_options = {})
39 if order = find_options.delete(:order)
40 logger.warn "using a shared index, so ignoring order_by clause #{order}"
42 total_hits, result = find_records_lazy_or_not q, options, find_options
43 # sort so results have the same order they had when originally retrieved
45 return SearchResults.new(result, total_hits)
50 def ar_find_by_contents(q, options = {}, find_options = {})
51 total_hits, id_arrays = collect_results(q, options)
52 result = retrieve_records(id_arrays, find_options)
53 result.sort! { |a, b| id_arrays[a.class.name][a.id.to_s].first <=> id_arrays[b.class.name][b.id.to_s].first }
54 [ total_hits, result ]
57 def collect_results(q, options = {})
59 # get object ids for index hits
61 total_hits = find_id_by_contents(q, options) do |model, id, score, data|
62 id_arrays[model] ||= {}
63 # store result rank and score
64 id_arrays[model][id] = [ rank += 1, score ]
66 [ total_hits, id_arrays ]
70 # determine all field names in the shared index
72 # def single_index_field_names(models)
73 # @single_index_field_names ||= (
74 # searcher = Ferret::Search::Searcher.new(class_index_dir)
75 # if searcher.reader.respond_to?(:get_field_names)
76 # (searcher.reader.send(:get_field_names) - ['id', 'class_name']).to_a
79 #unable to retrieve field names for class #{self.name}, please
80 #consider naming all indexed fields in your call to acts_as_ferret!
82 # models.map { |m| m.content_columns.map { |col| col.name } }.flatten