#199
[acts_as_ferret.git] / lib / instance_methods.rb
blob599a271a77cd8818ae15c8da7602b32df2cd4d15
1 module ActsAsFerret #:nodoc:
3   module InstanceMethods
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.
8     # 
9     # === Options
10     #
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
22     #                    match.  
23     # post_tag::         Default: "</em>". This tag should close the
24     #                    +:pre_tag+.
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
29     #                    character.
30     def highlight(query, options = {})
31       self.class.aaf_index.highlight(id, self.class.name, query, options)
32     end
33     
34     # re-eneable ferret indexing for this instance after a call to #disable_ferret
35     def enable_ferret  
36       @ferret_disabled = nil 
37     end
38     alias ferret_enable enable_ferret  # compatibility
39     
40     # returns true if ferret indexing is enabled for this record.
41     #
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.
44     #
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?)
51     end
53     # Returns the analyzer to use when adding this record to the index.
54     #
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
58     # call) is used.
59     def ferret_analyzer
60       nil
61     end
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. 
66     #
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 
69     # tell me.
70     #
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).
74     #
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.
81     #
82     def disable_ferret(option = :once)
83       if block_given?
84         @ferret_disabled = :always
85         result = yield
86         ferret_enable
87         ferret_update if option == :index_when_finished || (option == :index_when_true && result)
88         result
89       elsif [:once, :always].include?(option)
90         @ferret_disabled = option
91       else
92         raise ArgumentError.new("Invalid Argument #{option}")
93       end
94     end
96     # add to index
97     def ferret_create
98       if ferret_enabled?
99         logger.debug "ferret_create/update: #{self.class.name} : #{self.id}"
100         self.class.aaf_index << self
101       else
102         ferret_enable if @ferret_disabled == :once
103       end
104       true # signal success to AR
105     end
106     alias :ferret_update :ferret_create
107     
109     # remove from index
110     def ferret_destroy
111       logger.debug "ferret_destroy: #{self.class.name} : #{self.id}"
112       begin
113         self.class.aaf_index.remove self.id, self.class.name
114       rescue
115         logger.warn("Could not find indexed value for this object: #{$!}\n#{$!.backtrace}")
116       end
117       true # signal success to AR
118     end
119     
120     # turn this instance into a ferret document (which basically is a hash of
121     # fieldname => value pairs)
122     def to_doc
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
126         doc[:id] = self.id
128         # store the class name if configured to do so
129         doc[:class_name] = self.class.name if aaf_configuration[:store_class_name]
130       
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]
134         end
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
139           else
140             logger.error "boost option should point to an instance method: #{aaf_configuration[:boost]}"
141           end
142         end
143       end
144     end
146     def document_number
147       self.class.aaf_index.document_number(id, self.class.name)
148     end
150     def query_for_record
151       self.class.aaf_index.query_for_record(id, self.class.name)
152     end
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
160       end
161       field_data
162     end
165   end