#199
[acts_as_ferret.git] / lib / acts_as_ferret.rb
blobeac80ea4e72c4c6312eed7b6d804cc3026e529a4
1 # Copyright (c) 2006 Kasper Weibel Nielsen-Refs, Thomas Lockney, Jens Krämer
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
10 # The above copyright notice and this permission notice shall be included in all
11 # copies or substantial portions of the Software.
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 # SOFTWARE.
21 require 'active_support'
22 require 'active_record'
23 require 'set'
24 require 'enumerator'
25 require 'ferret'
27 require 'blank_slate'
28 require 'bulk_indexer'
29 require 'ferret_extensions'
30 require 'act_methods'
31 require 'search_results'
32 require 'class_methods'
33 require 'shared_index_class_methods'
34 require 'ferret_result'
35 require 'instance_methods'
37 require 'multi_index'
38 require 'more_like_this'
40 require 'index'
41 require 'local_index'
42 require 'shared_index'
43 require 'remote_index'
45 require 'ferret_server'
48 # The Rails ActiveRecord Ferret Mixin.
50 # This mixin adds full text search capabilities to any Rails model.
52 # The current version emerged from on the original acts_as_ferret plugin done by
53 # Kasper Weibel and a modified version done by Thomas Lockney, which  both can be 
54 # found on the Ferret Wiki: http://ferret.davebalmain.com/trac/wiki/FerretOnRails.
56 # basic usage:
57 # include the following in your model class (specifiying the fields you want to get indexed):
58 # acts_as_ferret :fields => [ :title, :description ]
60 # now you can use ModelClass.find_by_contents(query) to find instances of your model
61 # whose indexed fields match a given query. All query terms are required by default, but 
62 # explicit OR queries are possible. This differs from the ferret default, but imho is the more
63 # often needed/expected behaviour (more query terms result in less results).
65 # Released under the MIT license.
67 # Authors: 
68 # Kasper Weibel Nielsen-Refs (original author)
69 # Jens Kraemer <jk@jkraemer.net> (active maintainer)
71 module ActsAsFerret
73   # global Hash containing all multi indexes created by all classes using the plugin
74   # key is the concatenation of alphabetically sorted names of the classes the
75   # searcher searches.
76   @@multi_indexes = Hash.new
77   def self.multi_indexes; @@multi_indexes end
79   # global Hash containing the ferret indexes of all classes using the plugin
80   # key is the index directory.
81   @@ferret_indexes = Hash.new
82   def self.ferret_indexes; @@ferret_indexes end
85   
86   def self.ensure_directory(dir)
87     FileUtils.mkdir_p dir unless (File.directory?(dir) || File.symlink?(dir))
88   end
89   
90   # make sure the default index base dir exists. by default, all indexes are created
91   # under RAILS_ROOT/index/RAILS_ENV
92   def self.init_index_basedir
93     index_base = "#{RAILS_ROOT}/index"
94     @@index_dir = "#{index_base}/#{RAILS_ENV}"
95   end
96   
97   mattr_accessor :index_dir
98   init_index_basedir
99   
100   def self.append_features(base)
101     super
102     base.extend(ClassMethods)
103   end
104   
105   # builds a FieldInfos instance for creation of an index containing fields
106   # for the given model classes.
107   def self.field_infos(models)
108     # default attributes for fields
109     fi = Ferret::Index::FieldInfos.new(:store => :no, 
110                                         :index => :yes, 
111                                         :term_vector => :no,
112                                         :boost => 1.0)
113     # primary key
114     fi.add_field(:id, :store => :yes, :index => :untokenized) 
115     fields = {}
116     have_class_name = false
117     models.each do |model|
118       fields.update(model.aaf_configuration[:ferret_fields])
119       # class_name
120       if !have_class_name && model.aaf_configuration[:store_class_name]
121         fi.add_field(:class_name, :store => :yes, :index => :untokenized) 
122         have_class_name = true
123       end
124     end
125     fields.each_pair do |field, options|
126       options = options.dup
127       options.delete(:boost) if options[:boost].is_a?(Symbol)
128       options.delete(:via)
129       fi.add_field(field, { :store => :no, 
130                             :index => :yes }.update(options)) 
131     end
132     return fi
133   end
135   def self.close_multi_indexes
136     # close combined index readers, just in case
137     # this seems to fix a strange test failure that seems to relate to a
138     # multi_index looking at an old version of the content_base index.
139     multi_indexes.each_pair do |key, index|
140       # puts "#{key} -- #{self.name}"
141       # TODO only close those where necessary (watch inheritance, where
142       # self.name is base class of a class where key is made from)
143       index.close #if key =~ /#{self.name}/
144     end
145     multi_indexes.clear
146   end
150 # include acts_as_ferret method into ActiveRecord::Base
151 ActiveRecord::Base.extend ActsAsFerret::ActMethods