lib/thinking_sphinx/configuration.rb in DrMark-thinking-sphinx-0.9.9 vs lib/thinking_sphinx/configuration.rb in DrMark-thinking-sphinx-1.1.6

- old
+ new

@@ -1,6 +1,7 @@ require 'erb' +require 'singleton' module ThinkingSphinx # This class both keeps track of the configuration settings for Sphinx and # also generates the resulting file for Sphinx to use. # @@ -39,145 +40,165 @@ # Keep in mind, if for some particular reason you're using a version of # Sphinx older than 0.9.8 r871 (that's prior to the proper 0.9.8 release), # don't set allow_star to true. # class Configuration + include Singleton + + SourceOptions = %w( mysql_connect_flags sql_range_step sql_query_pre + sql_query_post sql_ranged_throttle sql_query_post_index ) + + IndexOptions = %w( charset_table charset_type docinfo enable_star + exceptions html_index_attrs html_remove_elements html_strip ignore_chars + min_infix_len min_prefix_len min_word_len mlock morphology ngram_chars + ngram_len phrase_boundary phrase_boundary_step preopen stopwords + wordforms ) + attr_accessor :config_file, :searchd_log_file, :query_log_file, - :pid_file, :searchd_file_path, :address, :port, :enable_star, - :allow_star, :min_prefix_len, :min_infix_len, :mem_limit, :max_matches, - :morphology, :charset_type, :charset_table, :ignore_chars, :html_strip, - :html_remove_elements, :database_yml_file, :app_root + :pid_file, :searchd_file_path, :address, :port, :allow_star, + :database_yml_file, :app_root, :bin_path, :model_directories - attr_reader :environment + attr_accessor :source_options, :index_options + attr_reader :environment, :configuration + # Load in the configuration settings - this will look for config/sphinx.yml # and parse it according to the current environment. # def initialize(app_root = Dir.pwd) + self.reset + end + + def reset self.app_root = RAILS_ROOT if defined?(RAILS_ROOT) self.app_root = Merb.root if defined?(Merb) self.app_root ||= app_root + @configuration = Riddle::Configuration.new + @configuration.searchd.address = "127.0.0.1" + @configuration.searchd.port = 3312 + @configuration.searchd.pid_file = "#{self.app_root}/log/searchd.#{environment}.pid" + @configuration.searchd.log = "#{self.app_root}/log/searchd.log" + @configuration.searchd.query_log = "#{self.app_root}/log/searchd.query.log" + self.database_yml_file = "#{self.app_root}/config/database.yml" self.config_file = "#{self.app_root}/config/#{environment}.sphinx.conf" - self.searchd_log_file = "#{self.app_root}/log/searchd.log" - self.query_log_file = "#{self.app_root}/log/searchd.query.log" - self.pid_file = "#{self.app_root}/log/searchd.#{environment}.pid" self.searchd_file_path = "#{self.app_root}/db/sphinx/#{environment}" - self.address = "127.0.0.1" - self.port = 3312 self.allow_star = false - self.enable_star = false - self.min_prefix_len = nil - self.min_infix_len = nil - self.mem_limit = "64M" - self.max_matches = 1000 - self.morphology = "stem_en" - self.charset_type = "utf-8" - self.charset_table = nil - self.ignore_chars = nil - self.html_strip = false - self.html_remove_elements = "" + self.bin_path = "" + self.model_directories = ["#{app_root}/app/models/"] + + Dir.glob("#{app_root}/vendor/plugins/*/app/models/") + self.source_options = {} + self.index_options = { + :charset_type => "utf-8", + :morphology => "stem_en" + } + parse_config + + self end def self.environment @@environment ||= ( - defined?(Merb) ? ENV['MERB_ENV'] : ENV['RAILS_ENV'] + defined?(Merb) ? Merb.environment : ENV['RAILS_ENV'] ) || "development" end def environment self.class.environment end + def controller + @controller ||= Riddle::Controller.new(@configuration, self.config_file) + end + # Generate the config file for Sphinx by using all the settings defined and # looping through all the models with indexes to build the relevant # indexer and searchd configuration, and sources and indexes details. # def build(file_path=nil) + load_models file_path ||= "#{self.config_file}" - database_confs = YAML::load(ERB.new(IO.read("#{self.database_yml_file}")).result) - database_confs.symbolize_keys! - database_conf = database_confs[environment.to_sym] - database_conf.symbolize_keys! + @configuration.indexes.clear + + ThinkingSphinx.indexed_models.each_with_index do |model, model_index| + @configuration.indexes.concat model.constantize.to_riddle(model_index) + end + open(file_path, "w") do |file| - file.write <<-CONFIG -indexer -{ - mem_limit = #{self.mem_limit} -} - -searchd -{ - address = #{self.address} - port = #{self.port} - log = #{self.searchd_log_file} - query_log = #{self.query_log_file} - read_timeout = 5 - max_children = 30 - pid_file = #{self.pid_file} - max_matches = #{self.max_matches} -} - CONFIG - - ThinkingSphinx.indexed_models.each_with_index do |model, model_index| - model = model.constantize - sources = [] - delta_sources = [] - prefixed_fields = [] - infixed_fields = [] - - model.indexes.select { |index| index.model == model }.each_with_index do |index, i| - file.write index.to_config(model, i, database_conf, charset_type, model_index) - - create_array_accum if index.adapter == :postgres - sources << "#{ThinkingSphinx::Index.name(model)}_#{i}_core" - delta_sources << "#{ThinkingSphinx::Index.name(model)}_#{i}_delta" if index.delta? - end - - next if sources.empty? - - source_list = sources.collect { |s| "source = #{s}" }.join("\n") - delta_list = delta_sources.collect { |s| "source = #{s}" }.join("\n") - - file.write core_index_for_model(model, source_list) - unless delta_list.blank? - file.write delta_index_for_model(model, delta_list) - end - - file.write distributed_index_for_model(model) - end + file.write @configuration.render end end # Make sure all models are loaded - without reloading any that # ActiveRecord::Base is already aware of (otherwise we start to hit some # messy dependencies issues). # def load_models - base = "#{app_root}/app/models/" - Dir["#{base}**/*.rb"].each do |file| - model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1') + self.model_directories.each do |base| + Dir["#{base}**/*.rb"].each do |file| + model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1') - next if model_name.nil? - next if ::ActiveRecord::Base.send(:subclasses).detect { |model| - model.name == model_name - } + next if model_name.nil? + next if ::ActiveRecord::Base.send(:subclasses).detect { |model| + model.name == model_name + } - begin - model_name.camelize.constantize - rescue LoadError - model_name.gsub!(/.*[\/\\]/, '').nil? ? next : retry - rescue NameError - next + begin + model_name.camelize.constantize + rescue LoadError + model_name.gsub!(/.*[\/\\]/, '').nil? ? next : retry + rescue NameError + next + end end end end + def address + @configuration.searchd.address + end + + def address=(address) + @configuration.searchd.address = address + end + + def port + @configuration.searchd.port + end + + def port=(port) + @configuration.searchd.port = port + end + + def pid_file + @configuration.searchd.pid_file + end + + def pid_file=(pid_file) + @configuration.searchd.pid_file = pid_file + end + + def searchd_log_file + @configuration.searchd.log + end + + def searchd_log_file=(file) + @configuration.searchd.log = file + end + + def query_log_file + @configuration.searchd.query_log + end + + def query_log_file=(file) + @configuration.searchd.query_log = file + end + private # Parse the config/sphinx.yml file - if it exists - then use the attribute # accessors to set the appropriate values. Nothing too clever. # @@ -187,102 +208,30 @@ conf = YAML::load(ERB.new(IO.read(path)).result)[environment] conf.each do |key,value| self.send("#{key}=", value) if self.methods.include?("#{key}=") + + set_sphinx_setting self.source_options, key, value, SourceOptions + set_sphinx_setting self.index_options, key, value, IndexOptions + set_sphinx_setting @configuration.searchd, key, value + set_sphinx_setting @configuration.indexer, key, value end unless conf.nil? - end - - def core_index_for_model(model, sources) - output = <<-INDEX - -index #{ThinkingSphinx::Index.name(model)}_core -{ -#{sources} -path = #{self.searchd_file_path}/#{ThinkingSphinx::Index.name(model)}_core -charset_type = #{self.charset_type} -INDEX - morphology = model.indexes.inject(self.morphology) { |morph, index| - index.options[:morphology] || morph - } - output += " morphology = #{morphology}\n" unless morphology.blank? - output += " charset_table = #{self.charset_table}\n" unless self.charset_table.nil? - output += " ignore_chars = #{self.ignore_chars}\n" unless self.ignore_chars.nil? + self.bin_path += '/' unless self.bin_path.blank? if self.allow_star - # Ye Olde way of turning on enable_star - output += " enable_star = 1\n" - output += " min_prefix_len = #{self.min_prefix_len}\n" - else - # New, better way of turning on enable_star - thanks to James Healy - output += " enable_star = 1\n" if self.enable_star - output += " min_prefix_len = #{self.min_prefix_len}\n" unless self.min_prefix_len.nil? - output += " min_infix_len = #{self.min_infix_len}\n" unless self.min_infix_len.nil? + self.index_options[:enable_star] = true + self.index_options[:min_prefix_len] = 1 end - - - output += " html_strip = 1\n" if self.html_strip - output += " html_remove_elements = #{self.html_remove_elements}\n" unless self.html_remove_elements.blank? - - unless model.indexes.collect(&:prefix_fields).flatten.empty? - output += " prefix_fields = #{model.indexes.collect(&:prefix_fields).flatten.map(&:unique_name).join(', ')}\n" - else - output += " prefix_fields = _\n" unless model.indexes.collect(&:infix_fields).flatten.empty? - end - - unless model.indexes.collect(&:infix_fields).flatten.empty? - output += " infix_fields = #{model.indexes.collect(&:infix_fields).flatten.map(&:unique_name).join(', ')}\n" - else - output += " infix_fields = -\n" unless model.indexes.collect(&:prefix_fields).flatten.empty? - end - - output + "}\n" end - def delta_index_for_model(model, sources) - <<-INDEX -index #{ThinkingSphinx::Index.name(model)}_delta : #{ThinkingSphinx::Index.name(model)}_core -{ - #{sources} - path = #{self.searchd_file_path}/#{ThinkingSphinx::Index.name(model)}_delta -} - INDEX - end - - def distributed_index_for_model(model) - sources = ["local = #{ThinkingSphinx::Index.name(model)}_core"] - if model.indexes.any? { |index| index.delta? } - sources << "local = #{ThinkingSphinx::Index.name(model)}_delta" + def set_sphinx_setting(object, key, value, allowed = {}) + if object.is_a?(Hash) + object[key.to_sym] = value if allowed.include?(key.to_s) + else + object.send("#{key}=", value) if object.methods.include?("#{key}") + send("#{key}=", value) if self.methods.include?("#{key}") end - - <<-INDEX -index #{ThinkingSphinx::Index.name(model)} -{ - type = distributed - #{ sources.join("\n ") } - charset_type = #{self.charset_type} -} - INDEX - end - - def create_array_accum - ::ActiveRecord::Base.connection.execute "begin" - ::ActiveRecord::Base.connection.execute "savepoint ts" - begin - ::ActiveRecord::Base.connection.execute <<-SQL - CREATE AGGREGATE array_accum (anyelement) - ( - sfunc = array_append, - stype = anyarray, - initcond = '{}' - ); - SQL - rescue - raise unless $!.to_s =~ /already exists with same argument types/ - ::ActiveRecord::Base.connection.execute "rollback to savepoint ts" - end - ::ActiveRecord::Base.connection.execute "release savepoint ts" - ::ActiveRecord::Base.connection.execute "commit" end end end