Changeset 317
- Timestamp:
- 02/18/08 20:36:03 (8 months ago)
- Files:
-
- trunk/demo/app/models/content_base.rb (modified) (1 diff)
- trunk/demo/test/unit/shared_index1_test.rb (modified) (3 diffs)
- trunk/demo/test/unit/shared_index2_test.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/init.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/act_methods.rb (modified) (6 diffs)
- trunk/plugin/acts_as_ferret/lib/acts_as_ferret.rb (modified) (4 diffs)
- trunk/plugin/acts_as_ferret/lib/blank_slate.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/class_methods.rb (modified) (7 diffs)
- trunk/plugin/acts_as_ferret/lib/ferret_result.rb (modified) (2 diffs)
- trunk/plugin/acts_as_ferret/lib/ferret_server.rb (modified) (3 diffs)
- trunk/plugin/acts_as_ferret/lib/index.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/instance_methods.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/local_index.rb (modified) (2 diffs)
- trunk/plugin/acts_as_ferret/lib/multi_index.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/rdig_adapter.rb (added)
- trunk/plugin/acts_as_ferret/lib/without_ar.rb (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/demo/app/models/content_base.rb
r308 r317 11 11 # 'more like this' queries to find other content instances with similar 12 12 # descriptions 13 acts_as_ferret( :fields => { :comment_count => { },13 acts_as_ferret( :fields => { :comment_count => { :index => :untokenized }, 14 14 :title => { :boost => :title_boost }, 15 15 :description => { :boost => 1, :store => :yes }, trunk/demo/test/unit/shared_index1_test.rb
r169 r317 5 5 6 6 def setup 7 SharedIndex1.rebuild_index (SharedIndex2)7 SharedIndex1.rebuild_index 8 8 end 9 9 … … 74 74 75 75 def test_ferret_destroy 76 SharedIndex1.rebuild_index (SharedIndex2)76 SharedIndex1.rebuild_index 77 77 result = SharedIndex1.find_id_by_contents("first OR another", :models => :all) 78 78 assert_equal 4, result.first … … 83 83 84 84 def test_ferret_destroy_ticket_88 85 SharedIndex1.rebuild_index (SharedIndex2)85 SharedIndex1.rebuild_index 86 86 result = SharedIndex1.find_id_by_contents("first OR another", :models => :all) 87 87 assert_equal 4, result.first trunk/demo/test/unit/shared_index2_test.rb
r147 r317 5 5 6 6 def setup 7 SharedIndex1.rebuild_index (SharedIndex2)7 SharedIndex1.rebuild_index 8 8 end 9 9 trunk/plugin/acts_as_ferret/init.rb
r123 r317 21 21 require 'acts_as_ferret' 22 22 23 ActsAsFerret::logger = RAILS_DEFAULT_LOGGER trunk/plugin/acts_as_ferret/lib/act_methods.rb
r308 r317 1 1 module ActsAsFerret #:nodoc: 2 2 3 3 # This module defines the acts_as_ferret method and is included into 4 4 # ActiveRecord::Base … … 73 73 # For downwards compatibility reasons you can also specify the Ferret options in the 74 74 # last Hash argument. 75 def acts_as_ferret(options={} , ferret_options={})75 def acts_as_ferret(options={}) 76 76 # default to DRb mode 77 77 options[:remote] = true if options[:remote].nil? … … 100 100 end 101 101 102 103 102 extend ClassMethods 104 103 extend SharedIndexClassMethods if options[:single_index] … … 107 106 include MoreLikeThis::InstanceMethods 108 107 109 # AR hooks 110 after_create :ferret_create 111 after_update :ferret_update 112 after_destroy :ferret_destroy 108 if options[:rdig] 109 require 'rdig_adapter' 110 include ActsAsFerret::RdigAdapter 111 end 112 113 unless included_modules.include?(ActsAsFerret::WithoutAR) 114 # set up AR hooks 115 after_create :ferret_create 116 after_update :ferret_update 117 after_destroy :ferret_destroy 118 end 113 119 114 120 cattr_accessor :aaf_configuration 115 121 116 # default config 117 self.aaf_configuration = { 118 :index_dir => "#{ActsAsFerret::index_dir}/#{self.name.underscore}", 119 :store_class_name => false, 120 :name => self.table_name, 121 :class_name => self.name, 122 :single_index => false, 123 :reindex_batch_size => 1000, 124 :ferret => {}, # Ferret config Hash 125 :ferret_fields => {}, # list of indexed fields that will be filled later 126 :enabled => true, # used for class-wide disabling of Ferret 127 :mysql_fast_batches => true, # turn off to disable the faster, id based batching mechanism for MySQL 128 :raise_drb_errors => false # handle DRb connection errors by default 129 } 130 131 # merge aaf options with args 132 aaf_configuration.update(options) if options.is_a?(Hash) 133 # apply appropriate settings for shared index 134 if aaf_configuration[:single_index] 135 aaf_configuration[:index_dir] = "#{ActsAsFerret::index_dir}/shared" 136 aaf_configuration[:store_class_name] = true 122 # shared index defaults 123 if options.delete(:single_index) 124 options[:store_class_name] = true 125 options[:index] = 'shared' 137 126 end 138 127 139 # set ferret default options 140 aaf_configuration[:ferret].reverse_merge!( :or_default => false, 141 :handle_parse_errors => true, 142 :default_field => nil # will be set later on 143 #:max_clauses => 512, 144 #:analyzer => Ferret::Analysis::StandardAnalyzer.new, 145 # :wild_card_downcase => true 146 ) 147 148 # merge ferret options with those from second parameter hash 149 aaf_configuration[:ferret].update(ferret_options) if ferret_options.is_a?(Hash) 150 151 unless options[:remote] 152 ActsAsFerret::ensure_directory aaf_configuration[:index_dir] 153 aaf_configuration[:index_base_dir] = aaf_configuration[:index_dir] 154 aaf_configuration[:index_dir] = find_last_index_version(aaf_configuration[:index_dir]) 155 logger.debug "using index in #{aaf_configuration[:index_dir]}" 128 # apply default config for rdig based models 129 if options[:rdig] 130 options[:fields] ||= { :title => { :boost => 3, :store => :yes }, 131 :content => { :store => :yes } } 156 132 end 157 133 158 # these properties are somewhat vital to the plugin and shouldn't 159 # be overwritten by the user: 160 aaf_configuration[:ferret].update( 161 :key => (aaf_configuration[:single_index] ? [:id, :class_name] : :id), 162 :path => aaf_configuration[:index_dir], 163 :auto_flush => true, # slower but more secure in terms of locking problems TODO disable when running in drb mode? 164 :create_if_missing => true 165 ) 166 167 if aaf_configuration[:fields] 168 add_fields(aaf_configuration[:fields]) 169 else 170 add_fields(self.new.attributes.keys.map { |k| k.to_sym }) 171 add_fields(aaf_configuration[:additional_fields]) 172 end 134 # name of this index 135 index_name = options.delete(:index) || self.name.underscore 173 136 174 # now that all fields have been added, we can initialize the default 175 # field list to be used by the query parser. 176 # It will include all content fields *not* marked as :untokenized. 177 # This fixes the otherwise failing CommentTest#test_stopwords. Basically 178 # this means that by default only tokenized fields (which all fields are 179 # by default) will be searched. If you want to search inside the contents 180 # of an untokenized field, you'll have to explicitly specify it in your 181 # query. 182 # 183 # Unfortunately this is not very useful with a shared index (see 184 # http://projects.jkraemer.net/acts_as_ferret/ticket/85) 185 # You should consider specifying the default field list to search for as 186 # part of the ferret_options hash in your call to acts_as_ferret. 187 aaf_configuration[:ferret][:default_field] ||= if aaf_configuration[:single_index] 188 logger.warn "You really should set the acts_as_ferret :default_field option when using a shared index!" 189 '*' 190 else 191 aaf_configuration[:ferret_fields].keys.select do |f| 192 aaf_configuration[:ferret_fields][f][:index] != :untokenized 193 end 194 end 195 logger.info "default field list: #{aaf_configuration[:ferret][:default_field].inspect}" 137 self.aaf_configuration = ActsAsFerret::register_class_with_index(self, index_name, options) 138 logger.debug "configured index for class #{self.name}:\n#{aaf_configuration.inspect}" 196 139 197 if options[:remote] 198 aaf_index.ensure_index_exists 199 end 140 # update our copy of the global index config with options local to this class 141 aaf_configuration[:class_name] ||= self.name 142 143 # add methods for retrieving field values 144 add_fields options[:fields] 145 add_fields options[:additional_fields] 146 add_fields aaf_configuration[:fields] 147 add_fields aaf_configuration[:additional_fields] 148 149 # not good at class level, index might get initialized too early 150 #if options[:remote] 151 # aaf_index.ensure_index_exists 152 #end 200 153 end 201 154 … … 203 156 protected 204 157 205 # find the most recent version of an index206 def find_last_index_version(basedir)207 # check for versioned index208 versions = Dir.entries(basedir).select do |f|209 dir = File.join(basedir, f)210 File.directory?(dir) && File.file?(File.join(dir, 'segments')) && f =~ /^\d+(_\d+)?$/211 end212 if versions.any?213 # select latest version214 versions.sort!215 File.join basedir, versions.last216 else217 basedir218 end219 end220 158 221 222 # helper that defines a method that adds the given field to a ferret 159 # helper to defines a method which adds the given field to a ferret 223 160 # document instance 224 161 def define_to_field_method(field, options = {}) 225 if options[:boost].is_a?(Symbol) 226 dynamic_boost = options[:boost] 227 options.delete :boost 228 end 229 options.reverse_merge!( :store => :no, 230 :highlight => :yes, 231 :index => :yes, 232 :term_vector => :with_positions_offsets, 233 :via => field, 234 :boost => 1.0 ) 235 options[:term_vector] = :no if options[:index] == :no 236 aaf_configuration[:ferret_fields][field] = options 237 238 define_method("#{field}_to_ferret".to_sym) do 239 begin 240 val = content_for_field_name(field, dynamic_boost) 162 method_name = "#{field}_to_ferret" 163 return if instance_methods.include?(method_name) # already defined 164 dynamic_boost = options[:boost] if options[:boost].is_a?(Symbol) 165 via = options[:via] || field 166 define_method(method_name.to_sym) do 167 val = begin 168 content_for_field_name(field, via, dynamic_boost) 241 169 rescue 242 170 logger.warn("Error retrieving value for field #{field}: #{$!}") 243 val =''171 '' 244 172 end 245 173 logger.debug("Adding field #{field} with value '#{val}' to index") … … 249 177 250 178 def add_fields(field_config) 179 # TODO 180 #field_config.each do |*args| 181 # define_to_field_method *args 182 #end 251 183 if field_config.is_a? Hash 252 field_config.each_pair do | key,val|253 define_to_field_method (key,val)184 field_config.each_pair do |field, options| 185 define_to_field_method field, options 254 186 end 255 187 elsif field_config.respond_to?(:each) 256 188 field_config.each do |field| 257 define_to_field_method (field)189 define_to_field_method field 258 190 end 259 191 end trunk/plugin/acts_as_ferret/lib/acts_as_ferret.rb
r312 r317 34 34 require 'ferret_result' 35 35 require 'instance_methods' 36 require 'without_ar' 36 37 37 38 require 'multi_index' … … 45 46 require 'ferret_server' 46 47 48 require 'rdig_adapter' 47 49 48 50 # The Rails ActiveRecord Ferret Mixin. … … 70 72 # 71 73 module ActsAsFerret 74 75 class ActsAsFerretError < StandardError; end 76 class IndexNotDefined < ActsAsFerretError; end 77 class IndexAlreadyDefined < ActsAsFerretError; end 78 79 # default field list for use with a shared index. Set it globally to 80 # avoid having to specify the same :default_field value in every class using 81 # the shared index. 82 @@shared_index_default_fields = nil 83 mattr_accessor :shared_index_default_fields 84 85 @@logger = nil 86 mattr_accessor :logger 72 87 73 88 # global Hash containing all multi indexes created by all classes using the plugin … … 82 97 def self.ferret_indexes; @@ferret_indexes end 83 98 99 # holds per-index configuration, key is the index name 100 @@index_definitions = {} 101 # mapping from class name to index name 102 @@index_using_classes = {} 103 def self.index_definitions; @@index_definitions end 104 105 DEFAULT_FIELD_OPTIONS = { 106 :store => :no, 107 :highlight => :yes, 108 :index => :yes, 109 :term_vector => :with_positions_offsets, 110 :boost => 1.0 111 } 112 113 def self.field_config_for(fieldname, options = {}) 114 config = DEFAULT_FIELD_OPTIONS.merge options 115 config[:term_vector] = :no if config[:index] == :no 116 config.delete :via 117 config.delete :boost if config[:boost].is_a?(Symbol) # dynamic boosts aren't handled here 118 return config 119 end 120 121 def self.build_field_config(fields) 122 field_config = {} 123 case fields 124 when Array 125 fields.each { |name| field_config[name] = field_config_for name } 126 when Hash 127 fields.each { |name, options| field_config[name] = field_config_for name, options } 128 else raise InvalidArgumentError.new(":fields option must be Hash or Array") 129 end if fields 130 return field_config 131 end 132 133 # Globally declares an index. 134 # 135 # Use the index in your model classes with 136 # acts_as_ferret :index => :index_name 137 # 138 # This method is also used to implicitly declare an index when you use the 139 # acts_as_ferret call without the :index option as usual. 140 def self.define_index(name, options = {}) 141 name = name.to_sym 142 raise IndexAlreadyDefined.new(name) if index_definitions.has_key?(name) 143 index_definition = { 144 :index_dir => "#{ActsAsFerret::index_dir}/#{name}", 145 :store_class_name => false, 146 :name => name, 147 :single_index => false, 148 :reindex_batch_size => 1000, 149 :ferret => {}, 150 :ferret_fields => {}, # list of indexed fields that will be filled later 151 :enabled => true, # used for class-wide disabling of Ferret 152 :mysql_fast_batches => true, # turn off to disable the faster, id based batching mechanism for MySQL 153 :raise_drb_errors => false # handle DRb connection errors by default 154 }.update( options ) 155 156 index_definition[:registered_models] = [] 157 158 # build ferret configuration 159 index_definition[:ferret] = { 160 :or_default => false, 161 :handle_parse_errors => true, 162 :default_field => nil, # will be set later on 163 #:max_clauses => 512, 164 #:analyzer => Ferret::Analysis::StandardAnalyzer.new, 165 # :wild_card_downcase => true 166 }.update( options[:ferret] || {} ) 167 168 index_definition[:user_default_field] = index_definition[:ferret][:default_field] 169 170 # these properties are somewhat vital to the plugin and shouldn't 171 # be overwritten by the user: 172 index_definition[:ferret].update( 173 :key => (index_definition[:store_class_name] ? [:id, :class_name] : :id), 174 :path => index_definition[:index_dir], 175 :auto_flush => true, # slower but more secure in terms of locking problems TODO disable when running in drb mode? 176 :create_if_missing => true 177 ) 178 179 180 unless index_definition[:remote] 181 ActsAsFerret::ensure_directory index_definition[:index_dir] 182 index_definition[:index_base_dir] = index_definition[:index_dir] 183 index_definition[:index_dir] = find_last_index_version(index_definition[:index_dir]) 184 logger.debug "using index in #{index_definition[:index_dir]}" 185 end 186 187 # field config 188 index_definition[:ferret_fields] = build_field_config( options[:fields] ) 189 index_definition[:ferret_fields].update build_field_config( options[:additional_fields] ) 190 191 index_definitions[name] = index_definition 192 return index_definition 193 end 84 194 85 195 # called internally by the acts_as_ferret method 196 # 197 # TODO part of the given options which might influence the indexing of 198 # records of a special class (such as analyzer, field configuration(i.e. 199 # dynamic boosts) need to be copied to the returned per-class config so they 200 # are taken into account properly even when multiple classes use conflicting 201 # settings) 202 def self.register_class_with_index(clazz, index_name, options = {}) 203 index_name = index_name.to_sym 204 @@index_using_classes[clazz.name] = index_name 205 if definition = index_definitions[index_name] 206 definition[:shared_index] = true 207 # TODO: add class-declared options to the index definition? which? 208 # merge fields from this acts_as_ferret call with predefined fields 209 already_defined_fields = definition[:ferret_fields] 210 field_config = build_field_config options[:fields] 211 field_config.update build_field_config( options[:additional_fields] ) 212 field_config.each do |field, config| 213 if already_defined_fields.has_key?(field) 214 logger.info "ignoring redefinition of ferret field #{field}" 215 else 216 already_defined_fields[field] = config 217 logger.info "adding new field #{field} from class #{clazz.name} to index #{index_name}" 218 end 219 end 220 else 221 # index definition on the fly 222 # default to all attributes of this class 223 options[:fields] ||= clazz.new.attributes.keys.map { |k| k.to_sym } 224 define_index index_name, options 225 end 226 227 # update default field list to be used by the query parser, unless it 228 # was explicitly given by user. 229 # 230 # It will include all content fields *not* marked as :untokenized. 231 # This fixes the otherwise failing CommentTest#test_stopwords. Basically 232 # this means that by default only tokenized fields (which all fields are 233 # by default) will be searched. If you want to search inside the contents 234 # of an untokenized field, you'll have to explicitly specify it in your 235 # query. 236 definition = index_definitions[index_name] 237 unless definition[:user_default_field] 238 # grab all tokenized fields 239 definition[:ferret][:default_field] = definition[:ferret_fields].keys.select do |field| 240 definition[:ferret_fields][field][:index] != :untokenized 241 end 242 logger.info "default field list for index #{index_name}: #{definition[:ferret][:default_field].inspect}" 243 end 244 245 # TODO: duped definition more or less worthless... 246 definition[:registered_models] << clazz 247 return definition.dup 248 end 249 250 # returns the index with the given name. 251 def self.get_index(name) 252 definition = index_definitions[name] 253 path = definition[:index_dir] 254 ferret_indexes[path] ||= create_index_instance(definition) 255 end 256 257 # creates a new Index instance. 258 def self.create_index_instance(definition) 259 if definition[:remote] 260 RemoteIndex 261 elsif definition[:shared_index] 262 SharedIndex 263 else 264 LocalIndex 265 end.new(definition) 266 end 267 268 def self.rebuild_index(name) 269 idx = get_index(name) 270 idx.rebuild_index 271 end 272 273 # Switches the named index to a new index directory. 274 # Used by the DRb server when switching to a new index version. 275 def self.change_index_dir(name, new_dir) 276 logger.debug "[#{name}] changing index dir to #{new_dir}" 277 definition = @@index_definitions[name] 278 idx = get_index(name) 279 280 # store index with the new dir as key. This prevents the aaf_index method 281 # from opening another index instance later on. 282 ferret_indexes[new_dir] = idx 283 284 old_dir = definition[:index_dir] 285 definition[:index_dir] = definition[:ferret][:path] = new_dir 286 287 # clean old reference to index 288 ActsAsFerret::ferret_indexes.delete old_dir 289 idx.reopen! 290 logger.debug "[#{name}] index dir is now #{new_dir}" 291 end 292 293 # returns the index definition for the index used by the given class or 294 # index_name 295 def self.index_definition(clazz_or_index_name) 296 logger.debug "index_definition for #{clazz_or_index_name}" 297 # TODO: inheritance hochhangeln (Content, ContentBase) 298 index_name = clazz_or_index_name.is_a?(Class) ? 299 @@index_using_classes[clazz_or_index_name.name] : clazz_or_index_name 300 logger.debug "index_definition for #{index_name}" 301 index_definitions[index_name] 302 end 303 304 # find the most recent version of an index 305 def self.find_last_index_version(basedir) 306 # check for versioned index 307 versions = Dir.entries(basedir).select do |f| 308 dir = File.join(basedir, f) 309 File.directory?(dir) && File.file?(File.join(dir, 'segments')) && f =~ /^\d+(_\d+)?$/ 310 end 311 if versions.any? 312 # select latest version 313 versions.sort! 314 File.join basedir, versions.last 315 else 316 basedir 317 end 318 end 319 86 320 def self.ensure_directory(dir) 87 321 FileUtils.mkdir_p dir unless (File.directory?(dir) || File.symlink?(dir)) 88 322 end 323 324 89 325 90 326 # make sure the default index base dir exists. by default, all indexes are created trunk/plugin/acts_as_ferret/lib/blank_slate.rb
r312 r317 1 1 if defined?(BlankSlate) 2 # Rails 2.x has 2 # Rails 2.x has it already 3 3 module ActsAsFerret 4 4 class BlankSlate < ::BlankSlate trunk/plugin/acts_as_ferret/lib/class_methods.rb
r310 r317 21 21 end 22 22 23 # rebuild the index from all data stored for this model. 23 # rebuild the index from all data stored for this model, and any other 24 # model classes associated with the same index. 24 25 # This is called automatically when no index exists yet. 25 26 # 26 # When calling this method manually, you can give any additional 27 # model classes that should also go into this index as parameters. 28 # Useful when using the :single_index option. 29 # Note that attributes named the same in different models will share 30 # the same field options in the shared index. 31 def rebuild_index(*models) 32 models << self unless models.include?(self) 33 aaf_index.rebuild_index models.map(&:to_s) 34 self.index_dir = find_last_index_version(aaf_configuration[:index_base_dir]) unless aaf_configuration[:remote] 27 # TODO: move into index class and add a method taking an index name to 28 # ActsAsFerret module. 29 def rebuild_index 30 ActsAsFerret::rebuild_index(aaf_configuration[:name]) 35 31 end 36 32 … … 44 40 options = Hash === ids.last ? ids.pop : {} 45 41 ids = ids.first if ids.size == 1 && ids.first.is_a?(Enumerable) 46 aaf_index.bulk_index( ids, options)42 aaf_index.bulk_index(self.name, ids, options) 47 43 end 48 44 … … 100 96 offset = 0 101 97 ids.each_slice(batch_size) do |id_slice| 102 logger.debug "########## slice: #{id_slice.join(',')}"103 98 records = find( :all, :conditions => ["id in (?)", id_slice] ) 104 logger.debug "########## slice records: #{records.inspect}"105 99 #yield records, offset 106 100 yield find( :all, :conditions => ["id in (?)", id_slice] ), offset … … 110 104 end 111 105 112 # Switches this class to a new index located in dir.113 # Used by the DRb server when switching to a new index version.106 # TODO change any references to this method to use 107 # ActsAsFerret::change_index_dir 114 108 def index_dir=(dir) 115 logger.debug "changing index dir to #{dir}" 116 117 # store index with the new dir as key. This prevents the aaf_index method 118 # from opening another index instance later on. 119 ActsAsFerret::ferret_indexes[dir] = aaf_index 120 old_dir = aaf_configuration[:index_dir] 121 aaf_configuration[:index_dir] = aaf_configuration[:ferret][:path] = dir 122 # clean old reference to index 123 ActsAsFerret::ferret_indexes.delete old_dir 124 aaf_index.reopen! 125 logger.debug "index dir is now #{dir}" 109 logger.warn "DEPRECATED, use ActsAsFerret::change_index_dir instead if index_dir=" 110 ActsAsFerret::change_index_dir(aaf_configuration[:name], dir) 126 111 end 127 112 … … 133 118 # Index object, too. 134 119 def aaf_index 135 ActsAsFerret:: ferret_indexes[aaf_configuration[:index_dir]] ||= create_index_instance120 ActsAsFerret::get_index(aaf_configuration[:name]) 136 121 end 137 122 … … 352 337 353 338 def lazy_find_by_contents(q, options = {}) 339 logger.debug "lazy_find_by_contents: #{q}" 354 340 result = [] 355 341 rank = 0 356 342 total_hits = find_id_by_contents(q, options) do |model, id, score, data| 343 logger.debug "model: #{model}, id: #{id}, data: #{data}" 357 344 result << FerretResult.new(model, id, score, rank += 1, data) 358 345 end … … 467 454 end 468 455 469 # creates a new Index instance.470 def create_index_instance471 if aaf_configuration[:remote]472 RemoteIndex473 elsif aaf_configuration[:single_index]474 SharedIndex475 else476 LocalIndex477 end.new(aaf_configuration)478 end479 480 456 # combine our conditions with those given by user, if any 481 457 def combine_conditions(conditions, additional_conditions = []) trunk/plugin/acts_as_ferret/lib/ferret_result.rb
r312 r317 21 21 @ferret_rank = rank 22 22 @data = data 23 @use_record = false 23 24 end 24 25 26 def inspect 27 "#<FerretResult wrapper for #{@model} with id #{@id}" 28 end 29 25 30 def method_missing(method, *args, &block) 26 if @ar_record || !@data.has_key?(method) 31 # don't try to fetch attributes from RDig based records 32 if (@ar_record && @use_record) || !@data.has_key?(method) 33 RAILS_DEFAULT_LOGGER.debug "miss for key: #{method.inspect} in #{@data.keys.inspect}" 27 34 to_record.send method, *args, &block 28 35 else … … 40 47 @ar_record.ferret_rank = ferret_rank 41 48 @ar_record.ferret_score = ferret_score 49 @use_record = !@ar_record.class.included_modules.include?(ActsAsFerret::RdigAdapter) 42 50 end 43 51 @ar_record trunk/plugin/acts_as_ferret/lib/ferret_server.rb
r296 r317 145 145 146 146 # hides LocalIndex#rebuild_index to implement index versioning 147 def rebuild_index(clazz, *models) 147 def rebuild_index(clazz) 148 definition = ActsAsFerret::index_definition_for_class(clazz) 149 models = definition[:registered_models] 148 150 with_class clazz do |clazz| 149 models = models.flatten.uniq.map(&:constantize)150 models << clazz unless models.include?(clazz)151 151 index = new_index_for(clazz, models) 152 152 reconnect_when_needed(clazz) do … … 154 154 index.index_models models 155 155 end 156 new_version = File.join clazz.aaf_configuration[:index_base_dir], Time.now.utc.strftime('%Y%m%d%H%M%S')156 new_version = File.join definition[:index_base_dir], Time.now.utc.strftime('%Y%m%d%H%M%S') 157 157 # create a unique directory name (needed for unit tests where 158 158 # multiple rebuilds per second may occur) … … 164 164 165 165 File.rename index.options[:path], new_version 166 clazz.index_dir =new_version166 ActsAsFerret::change_index_dir definition[:name], new_version 167 167 end 168 168 end trunk/plugin/acts_as_ferret/lib/index.rb
r308 r317 10 10 @logger = Logger.new("#{RAILS_ROOT}/log/ferret_index.log") 11 11 @logger.level = ActiveRecord::Base.logger.level 12 @index_name = aaf_configuration[:name] 12 13 end 13 14 trunk/plugin/acts_as_ferret/lib/instance_methods.rb
r307 r317 152 152 end 153 153 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}") 154 def content_for_field_name(field, via = field, dynamic_boost = nil) 155 field_data = self.send(via) || self.instance_variable_get("@#{via}") 157 156 if (dynamic_boost && boost_value = self.send(dynamic_boost)) 158 157 field_data = Ferret::Field.new(field_data) trunk/plugin/acts_as_ferret/lib/local_index.rb
r307 r317 46 46 end 47 47 48 # rebuilds the index from all records of the model class this index belongs 49 # to. Arguments can be given in shared index scenarios to name multiple 50 # model classes to include in the index 51 def rebuild_index(*models) 52 models << aaf_configuration[:class_name] unless models.include?(aaf_configuration[:class_name]) 53 models = models.flatten.uniq.map(&:constantize) 48 # rebuilds the index from all records of the model classes associated with this index 49 def rebuild_index 50 definition = ActsAsFerret::index_definition(@index_name) 51 models = definition[:registered_models] 54 52 logger.debug "rebuild index: #{models.inspect}" 55 index = Ferret::Index::Index.new(aaf_configuration[:ferret].dup.update(:auto_flush => false, 56 :field_infos => ActsAsFerret::field_infos(models), 57 :create => true)) 58 index.batch_size = aaf_configuration[:reindex_batch_size] 53 self.close 54 index = Ferret::Index::Index.new(definition[:ferret].dup.update(:auto_flush => false, 55 # TODO fieldinfos sollten jetzt 56 # auch aus neuer config kommen! 57 :field_infos => ActsAsFerret::field_infos(models), 58 :create => true)) 59 index.batch_size = definition[:reindex_batch_size] 59 60 index.logger = logger 60 61 index.index_models models 61 end 62 63 def bulk_index(ids, options) 64 ferret_index.bulk_index(aaf_configuration[:class_name].constantize, ids, options) 62 ActsAsFerret::change_index_dir @index_name, definition[:ferret][:path] 63 end 64 65 def bulk_index(class_name, ids, options) 66 ferret_index.bulk_index(class_name.constantize, ids, options) 65 67 end 66 68 … … 109 111 logger.debug "query: #{ferret_index.process_query query}" if logger.debug? 110 112 lazy_fields = determine_lazy_fields options 113 logger.debug "lazy_fields: #{lazy_fields}" 111 114 112 115 total_hits = index.search_each(query, options) do |hit, score| trunk/plugin/acts_as_ferret/lib/multi_index.rb
r198 r317 68 68 @sub_readers = @model_classes.map { |clazz| 69 69 begin 70 reader = Ferret::Index::IndexReader.new( clazz.aaf_configuration[:index_dir])70 reader = Ferret::Index::IndexReader.new(ActsAsFerret::index_definition(clazz)[:index_dir]) 71 71 rescue Exception 72 raise "error opening #{clazz.aaf_configuration[:index_dir]}: #{$!}"72 raise "error opening reader on index for class #{clazz.inspect}: #{$!}" 73 73 end 74 74 }
