1 module ActsAsFerret #:nodoc:
4 include ResultAttributes
6 # Returns an array of strings with the matches highlighted. The +query+ can
7 # either be a String or a Ferret::Search::Query object.
11 # field:: field to take the content from. This field has
12 # to have it's content stored in the index
13 # (:store => :yes in your call to aaf). If not
14 # given, all stored fields are searched, and the
15 # highlighted content found in all of them is returned.
16 # set :highlight => :no in the field options to
17 # avoid highlighting of contents from a :stored field.
18 # excerpt_length:: Default: 150. Length of excerpt to show. Highlighted
19 # terms will be in the centre of the excerpt.
20 # num_excerpts:: Default: 2. Number of excerpts to return.
21 # pre_tag:: Default: "<em>". Tag to place to the left of the
23 # post_tag:: Default: "</em>". This tag should close the
25 # ellipsis:: Default: "...". This is the string that is appended
26 # at the beginning and end of excerpts (unless the
27 # excerpt hits the start or end of the field. You'll
28 # probably want to change this to a Unicode elipsis
30 def highlight(query, options = {})
31 self.class.aaf_index.highlight(id, self.class.name, query, options)
34 # re-eneable ferret indexing for this instance after a call to #disable_ferret
36 @ferret_disabled = nil
38 alias ferret_enable enable_ferret # compatibility
40 # returns true if ferret indexing is enabled for this record.
42 # The optional is_bulk_index parameter will be true if the method is called
43 # by rebuild_index or bulk_index, and false otherwise.
45 # If is_bulk_index is true, the class level ferret_enabled state will be
46 # ignored by this method (per-instance ferret_enabled checks however will
47 # take place, so if you override this method to forbid indexing of certain
48 # records you're still safe).
49 def ferret_enabled?(is_bulk_index = false)
50 @ferret_disabled.nil? && (is_bulk_index || self.class.ferret_enabled?)
53 # Returns the analyzer to use when adding this record to the index.
55 # Override to return a specific analyzer for any record that is to be
56 # indexed, i.e. specify a different analyzer based on language. Returns nil
57 # by default so the global analyzer (specified with the acts_as_ferret
63 # Disable Ferret for this record for a specified amount of time. ::once will
64 # disable Ferret for the next call to #save (this is the default), ::always
65 # will do so for all subsequent calls.
67 # Note that this will turn off only the create and update hooks, but not the
68 # destroy hook. I think that's reasonable, if you think the opposite, please
71 # To manually trigger reindexing of a record after you're finished modifying
72 # it, you can call #ferret_update directly instead of #save (remember to
73 # enable ferret again before).
75 # When given a block, this will be executed without any ferret indexing of
76 # this object taking place. The optional argument in this case can be used
77 # to indicate if the object should be indexed after executing the block
78 # (::index_when_finished). Automatic Ferret indexing of this object will be
79 # turned on after the block has been executed. If passed ::index_when_true,
80 # the index will only be updated if the block evaluated not to false or nil.
82 def disable_ferret(option = :once)
84 @ferret_disabled = :always
87 ferret_update if option == :index_when_finished || (option == :index_when_true && result)
89 elsif [:once, :always].include?(option)
90 @ferret_disabled = option
92 raise ArgumentError.new("Invalid Argument #{option}")
99 logger.debug "ferret_create/update: #{self.class.name} : #{self.id}"
100 self.class.aaf_index << self
102 ferret_enable if @ferret_disabled == :once
104 true # signal success to AR
106 alias :ferret_update :ferret_create
111 logger.debug "ferret_destroy: #{self.class.name} : #{self.id}"
113 self.class.aaf_index.remove self.id, self.class.name
115 logger.warn("Could not find indexed value for this object: #{$!}\n#{$!.backtrace}")
117 true # signal success to AR
120 # turn this instance into a ferret document (which basically is a hash of
121 # fieldname => value pairs)
123 logger.debug "creating doc for class: #{self.class.name}, id: #{self.id}"
124 returning Ferret::Document.new do |doc|
125 # store the id of each item
128 # store the class name if configured to do so
129 doc[:class_name] = self.class.name if aaf_configuration[:store_class_name]
131 # iterate through the fields and add them to the document
132 aaf_configuration[:ferret_fields].each_pair do |field, config|
133 doc[field] = self.send("#{field}_to_ferret") unless config[:ignore]
135 if aaf_configuration[:boost]
136 if self.respond_to?(aaf_configuration[:boost])
137 boost = self.send aaf_configuration[:boost]
138 doc.boost = boost.to_i if boost
140 logger.error "boost option should point to an instance method: #{aaf_configuration[:boost]}"
147 self.class.aaf_index.document_number(id, self.class.name)
151 self.class.aaf_index.query_for_record(id, self.class.name)
154 def content_for_field_name(field, dynamic_boost = nil)
155 ar_field = aaf_configuration[:ferret_fields][field][:via]
156 field_data = self.send(ar_field) || self.instance_variable_get("@#{ar_field}")
157 if (dynamic_boost && boost_value = self.send(dynamic_boost))
158 field_data = Ferret::Field.new(field_data)
159 field_data.boost = boost_value.to_i