Changeset 321
- Timestamp:
- 02/18/08 20:36:05 (8 months ago)
- Files:
-
- trunk/demo/app/models/comment.rb (modified) (1 diff)
- trunk/demo/app/models/content_base.rb (modified) (1 diff)
- trunk/demo/config/environment.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/act_methods.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/acts_as_ferret.rb (modified) (7 diffs)
- trunk/plugin/acts_as_ferret/lib/ferret_server.rb (modified) (2 diffs)
- trunk/plugin/acts_as_ferret/lib/multi_index.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/remote_functions.rb (added)
- trunk/plugin/acts_as_ferret/lib/remote_index.rb (modified) (5 diffs)
- trunk/plugin/acts_as_ferret/lib/remote_multi_index.rb (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/demo/app/models/comment.rb
r308 r321 28 28 # do this in our custom to_doc method) 29 29 acts_as_ferret( :store_class_name => true, 30 :remote => ENV['AAF_REMOTE'] == 'true', # for testing drb remote indexing31 :raise_drb_errors => ENV['RAISE_DRB_ERRORS'] == 'true',32 30 :fields => { 33 31 :content => { :store => :yes }, trunk/demo/app/models/content_base.rb
r317 r321 15 15 :description => { :boost => 1, :store => :yes }, 16 16 :special => {} }, 17 :store_class_name => true, 18 :boost => :record_boost, 19 :raise_drb_errors => ENV['RAISE_DRB_ERRORS'] == 'true', 20 :remote => ENV['AAF_REMOTE'] == 'true') 17 :boost => :record_boost) 21 18 22 19 def comment_count; 0 end trunk/demo/config/environment.rb
r318 r321 55 55 # Include your application configuration below 56 56 57 ActsAsFerret::remote = false if ENV['AAF_REMOTE'] != 'true' 58 ActsAsFerret::raise_drb_errors = ENV['RAISE_DRB_ERRORS'] == 'true' 59 57 60 # define the index shared by the SharedIndex1 and SharedIndex2 classes 58 61 ActsAsFerret::define_index 'shared', :remote => ENV['AAF_REMOTE'] == 'true', trunk/plugin/acts_as_ferret/lib/act_methods.rb
r320 r321 63 63 # last Hash argument. 64 64 def acts_as_ferret(options={}) 65 # default to DRb mode66 options[:remote] = true if options[:remote].nil?67 68 # force local mode if running *inside* the Ferret server - somewhere the69 # real indexing has to be done after all :-)70 # Usually the automatic detection of server mode works fine, however if you71 # require your model classes in environment.rb they will get loaded before the72 # DRb server is started, so this code is executed too early and detection won't73 # work. In this case you'll get endless loops resulting in "stack level too deep"74 # errors.75 # To get around this, start the DRb server with the environment variable76 # FERRET_USE_LOCAL_INDEX set to '1'.77 logger.debug "Asked for a remote server ? #{options[:remote].inspect}, ENV[\"FERRET_USE_LOCAL_INDEX\"] is #{ENV["FERRET_USE_LOCAL_INDEX"].inspect}, looks like we are#{ActsAsFerret::Remote::Server.running || ENV['FERRET_USE_LOCAL_INDEX'] ? '' : ' not'} the server"78 options.delete(:remote) if ENV["FERRET_USE_LOCAL_INDEX"] || ActsAsFerret::Remote::Server.running79 80 if options[:remote] && options[:remote] !~ /^druby/81 # read server location from config/ferret_server.yml82 options[:remote] = ActsAsFerret::Remote::Config.new.uri rescue nil83 end84 85 if options[:remote]86 logger.info "Will use remote index server which should be available at #{options[:remote]}"87 else88 logger.info "Will use local index."89 end90 65 91 66 extend ClassMethods trunk/plugin/acts_as_ferret/lib/acts_as_ferret.rb
r320 r321 26 26 27 27 require 'ferret_find_methods' 28 require 'remote_functions' 28 29 require 'blank_slate' 29 30 require 'bulk_indexer' … … 38 39 39 40 require 'multi_index' 41 require 'remote_multi_index' 40 42 require 'more_like_this' 41 43 … … 105 107 :boost => 1.0 106 108 } 109 110 @@raise_drb_errors = false 111 mattr_writer :raise_drb_errors 112 def self.raise_drb_errors?; @@raise_drb_errors end 113 114 @@remote = nil 115 mattr_accessor :remote 116 def self.remote? 117 if @@remote.nil? 118 if ENV["FERRET_USE_LOCAL_INDEX"] || ActsAsFerret::Remote::Server.running 119 @@remote = false 120 else 121 @@remote = ActsAsFerret::Remote::Config.new.uri rescue false 122 end 123 if @@remote 124 logger.info "Will use remote index server which should be available at #{@@remote}" 125 else 126 logger.info "Will use local index." 127 end 128 end 129 @@remote 130 end 131 remote? 132 107 133 108 134 # Globally declares an index. … … 153 179 154 180 155 unless index_definition[:remote]181 unless remote? 156 182 ActsAsFerret::ensure_directory index_definition[:index_dir] 157 183 index_definition[:index_base_dir] = index_definition[:index_dir] … … 185 211 # returns the index with the given name. 186 212 def self.get_index(name) 213 name = name.to_sym 187 214 raise IndexNotDefined.new(name) unless ferret_indexes.has_key?(name) 188 215 ferret_indexes[name] … … 284 311 # creates a new Index instance. 285 312 def self.create_index_instance(definition) 286 ( definition[:remote]? RemoteIndex : LocalIndex).new(definition)313 (remote? ? RemoteIndex : LocalIndex).new(definition) 287 314 end 288 315 … … 313 340 # returns a MultiIndex instance operating on a MultiReader 314 341 def self.multi_index(indexes) 315 key = indexes.map{ |i| i.index_name.to_s }.sort.join(",") 316 ActsAsFerret::multi_indexes[key] ||= MultiIndex.new(indexes) 342 index_names = indexes.dup 343 index_names = index_names.map(&:to_s) if Symbol === index_names.first 344 if String === index_names.first 345 indexes = index_names.map{ |name| get_index name } 346 else 347 index_names = index_names.map{ |i| i.index_name.to_s } 348 end 349 key = index_names.sort.join(",") 350 ActsAsFerret::multi_indexes[key] ||= (remote? ? ActsAsFerret::RemoteMultiIndex : ActsAsFerret::MultiIndex).new(indexes) 317 351 end 318 352 trunk/plugin/acts_as_ferret/lib/ferret_server.rb
r318 r321 97 97 98 98 ################################################################################# 99 # handles all incoming method calls, and sends them on to the LocalIndex100 # instance of the correct model class.99 # handles all incoming method calls, and sends them on to the correct local index 100 # instance. 101 101 # 102 # Calls are not queued atm, so this will block until the call returned.102 # Calls are not queued, so this will block until the call returned. 103 103 # 104 104 def method_missing(name, *args) 105 105 @logger.debug "\#method_missing(#{name.inspect}, #{args.inspect})" 106 retried = false 107 index_name = args.shift 108 index = ActsAsFerret::get_index(index_name) 106 107 index = if name.to_s =~ /^multi_(.+)/ 108 name = $1 109 index_names = args.shift 110 ActsAsFerret::multi_index(index_names) 111 else 112 index_name = args.shift 113 ActsAsFerret::get_index(index_name) 114 end 109 115 110 116 # TODO find another way to implement the reconnection logic (maybe in … … 130 136 end 131 137 138 def register_class(class_name) 139 @logger.debug "############ registerclass #{class_name}" 140 class_name.constantize 141 @logger.debug "index for class #{class_name}: #{ActsAsFerret::ferret_indexes[class_name.underscore.to_sym]}" 142 143 end 144 132 145 # make sure we have a versioned index in place, building one if necessary 133 146 def ensure_index_exists(index_name) 134 147 @logger.debug "DRb server: ensure_index_exists for index #{index_name}" 135 definition = ActsAsFerret:: index_definition(index_name)148 definition = ActsAsFerret::get_index(index_name).index_definition 136 149 dir = definition[:index_dir] 137 150 unless File.directory?(dir) && File.file?(File.join(dir, 'segments')) && dir =~ %r{/\d+(_\d+)?$} trunk/plugin/acts_as_ferret/lib/multi_index.rb
r320 r321 1 1 module ActsAsFerret #:nodoc: 2 3 class MultiIndexBase 4 include FerretFindMethods 5 attr_accessor :logger 6 7 def initialize(indexes, options = {}) 8 # ensure all models indexes exist 9 @indexes = indexes 10 indexes.each { |i| i.ensure_index_exists } 11 default_fields = indexes.inject([]) do |fields, idx| 12 fields + [ idx.index_definition[:ferret][:default_field] ] 13 end.flatten.uniq 14 @options = { 15 :default_field => default_fields 16 }.update(options) 17 @logger = IndexLogger.new(ActsAsFerret::logger, "multi: #{indexes.map(&:index_name).join(',')}") 18 end 19 20 def ar_find(query, options = {}, ar_options = {}) 21 limit = options.delete(:limit) 22 offset = options.delete(:offset) || 0 23 options[:limit] = :all 24 total_hits, result = super query, options, ar_options 25 total_hits = result.size if ar_options[:conditions] 26 if limit && limit != :all 27 result = result[offset..limit+offset-1] 28 end 29 [total_hits, result] 30 end 31 32 def determine_stored_fields(options) 33 return nil unless options.has_key?(:lazy) 34 stored_fields = [] 35 @indexes.each do |index| 36 stored_fields += index.determine_stored_fields(options) 37 end 38 return stored_fields.uniq 39 end 40 41 def shared? 42 false 43 end 44 45 end 2 46 3 # This class can be used to search multiple physical indexes at once. 4 class MultiIndex 5 include FerretFindMethods 6 attr_accessor :logger 7 8 def initialize(indexes, options = {}) 9 # ensure all models indexes exist 10 @indexes = indexes 11 indexes.each { |i| i.ensure_index_exists } 12 default_fields = indexes.inject([]) do |fields, idx| 13 fields + [ idx.index_definition[:ferret][:default_field] ] 14 end.flatten.uniq 15 @options = { 16 :default_field => default_fields 17 }.update(options) 18 @logger = IndexLogger.new(ActsAsFerret::logger, "multi: #{indexes.map(&:index_name).join(',')}") 47 # This class can be used to search multiple physical indexes at once. 48 class MultiIndex < MultiIndexBase 49 50 def extract_stored_fields(doc, stored_fields) 51 ActsAsFerret::get_index_for(doc[:class_name]).extract_stored_fields(doc, stored_fields) unless stored_fields.blank? 52 end 53 54 def total_hits(q, options = {}) 55 search(q, options).total_hits 56 end 57 58 def search(query, options={}) 59 query = process_query(query) 60 logger.debug "parsed query: #{query.to_s}" 61 searcher.search(query, options) 62 end 63 64 def search_each(query, options = {}, &block) 65 query = process_query(query) 66 searcher.search_each(query, options, &block) 67 end 68 69 # checks if all our sub-searchers still are up to date 70 def latest? 71 #return false unless @reader 72 # segfaults with 0.10.4 --> TODO report as bug @reader.latest? 73 @reader and @reader.latest? 74 #@sub_readers.each do |r| 75 # return false unless r.latest? 76 #end 77 #true 78 end 79 80 def searcher 81 ensure_searcher 82 @searcher 83 end 84 85 def doc(i) 86 searcher[i] 87 end 88 alias :[] :doc 89 90 def query_parser 91 @query_parser ||= Ferret::QueryParser.new(@options) 92 end 93 94 def process_query(query) 95 query = query_parser.parse(query) if query.is_a?(String) 96 return query 97 end 98 99 def close 100 @searcher.close if @searcher 101 @reader.close if @reader 102 end 103 104 protected 105 106 def ensure_searcher 107 unless latest? 108 @sub_readers = @indexes.map { |idx| 109 begin 110 reader = Ferret::Index::IndexReader.new(idx.index_definition[:index_dir]) 111 logger.debug "sub-reader opened: #{reader}" 112 reader 113 rescue Exception 114 raise "error opening reader on index for class #{clazz.inspect}: #{$!}" 115 end 116 } 117 close 118 @reader = Ferret::Index::IndexReader.new(@sub_readers) 119 @searcher = Ferret::Search::Searcher.new(@reader) 19 120 end 121 end 20 122 21 def ar_find(query, options = {}, ar_options = {}) 22 limit = options.delete(:limit) 23 offset = options.delete(:offset) || 0 24 options[:limit] = :all 25 total_hits, result = super query, options, ar_options 26 total_hits = result.size if ar_options[:conditions] 27 if limit && limit != :all 28 result = result[offset..limit+offset-1] 29 end 30 [total_hits, result] 31 end 32 33 def determine_stored_fields(options) 34 return nil unless options.has_key?(:lazy) 35 stored_fields = [] 36 @indexes.each do |index| 37 stored_fields += index.determine_stored_fields(options) 38 end 39 return stored_fields.uniq 40 end 41 42 def extract_stored_fields(doc, stored_fields) 43 ActsAsFerret::get_index_for(doc[:class_name]).extract_stored_fields(doc, stored_fields) unless stored_fields.blank? 44 end 45 46 def total_hits(q, options = {}) 47 search(q, options).total_hits 48 end 49 50 def search(query, options={}) 51 query = process_query(query) 52 logger.debug "parsed query: #{query.to_s}" 53 searcher.search(query, options) 54 end 55 56 def search_each(query, options = {}, &block) 57 query = process_query(query) 58 searcher.search_each(query, options, &block) 59 end 60 61 # checks if all our sub-searchers still are up to date 62 def latest? 63 #return false unless @reader 64 # segfaults with 0.10.4 --> TODO report as bug @reader.latest? 65 @reader and @reader.latest? 66 #@sub_readers.each do |r| 67 # return false unless r.latest? 68 #end 69 #true 70 end 71 72 def shared? 73 false 74 end 75 76 def searcher 77 ensure_searcher 78 @searcher 79 end 80 81 def doc(i) 82 searcher[i] 83 end 84 alias :[] :doc 85 86 def query_parser 87 @query_parser ||= Ferret::QueryParser.new(@options) 88 end 89 90 def process_query(query) 91 query = query_parser.parse(query) if query.is_a?(String) 92 return query 93 end 94 95 def close 96 @searcher.close if @searcher 97 @reader.close if @reader 98 end 99 100 protected 101 102 def ensure_searcher 103 unless latest? 104 @sub_readers = @indexes.map { |idx| 105 begin 106 reader = Ferret::Index::IndexReader.new(idx.index_definition[:index_dir]) 107 logger.debug "sub-reader opened: #{reader}" 108 reader 109 rescue Exception 110 raise "error opening reader on index for class #{clazz.inspect}: #{$!}" 111 end 112 } 113 close 114 @reader = Ferret::Index::IndexReader.new(@sub_readers) 115 @searcher = Ferret::Search::Searcher.new(@reader) 116 end 117 end 118 119 end # of class MultiIndex 123 end # of class MultiIndex 120 124 121 125 end trunk/plugin/acts_as_ferret/lib/remote_index.rb
r319 r321 5 5 # basically forwards all calls to the remote server. 6 6 class RemoteIndex < AbstractIndex 7 include RemoteFunctions 7 8 8 9 def initialize(config) 9 10 super 10 @server = DRbObject.new(nil, config[:remote]) 11 @server = DRbObject.new(nil, ActsAsFerret::remote) 12 end 13 14 # Cause model classes to be loaded (and indexes get declared) on the DRb 15 # side of things. 16 def register_class(clazz, options) 17 handle_drb_error { @server.register_class clazz.name } 11 18 end 12 19 13 20 def method_missing(method_name, *args) 14 args.unshift model_class_name21 args.unshift index_name 15 22 handle_drb_error { @server.send(method_name, *args) } 16 23 end … … 21 28 }.each do |method_name, default_result| 22 29 define_method method_name do |*args| 23 args.unshift model_class_name30 args.unshift index_name 24 31 handle_drb_error(default_result) { @server.send method_name, *args } 25 32 end … … 27 34 28 35 def find_ids(q, options = {}, &proc) 29 total_hits, results = handle_drb_error([0, []]) { @server.find_ids(model_class_name, q, options) } 30 block_given? ? yield_results(total_hits, results, &proc) : [ total_hits, results ] 31 end 32 33 def id_multi_search(query, models, options, &proc) 34 total_hits, results = handle_drb_error([0, []]) { @server.id_multi_search(model_class_name, query, models, options) } 36 total_hits, results = handle_drb_error([0, []]) { @server.find_ids(index_name, q, options) } 35 37 block_given? ? yield_results(total_hits, results, &proc) : [ total_hits, results ] 36 38 end … … 38 40 # add record to index 39 41 def add(record) 40 handle_drb_error { @server.add record.class.name, record.to_doc }42 handle_drb_error { @server.add index_name, record.to_doc } 41 43 end 42 44 alias << add … … 44 46 private 45 47 46 def handle_drb_error(return_value_in_case_of_error = false) 47 yield 48 rescue DRb::DRbConnError => e 49 logger.error "DRb connection error: #{e}" 50 logger.warn e.backtrace.join("\n") 51 raise e if index_definition[:raise_drb_errors] 52 return_value_in_case_of_error 53 end 54 55 def yield_results(total_hits, results) 56 results.each do |result| 57 yield result[:model], result[:id], result[:score], result[:data] 58 end 59 total_hits 60 end 61 62 def model_class_name 63 index_definition[:class_name] 64 end 48 #def model_class_name 49 # index_definition[:class_name] 50 #end 65 51 66 52 end
