From 7379daad1c73bd3610ed296436250b417ac3673d Mon Sep 17 00:00:00 2001 From: hukl Date: Thu, 10 Feb 2011 14:19:00 +0100 Subject: removed thinking_sphinx plugin and replaced it with gem. also tuned dependencies --- .../plugins/thinking-sphinx/lib/thinking_sphinx.rb | 180 ----- .../lib/thinking_sphinx/active_record.rb | 260 ------- .../lib/thinking_sphinx/active_record/delta.rb | 78 --- .../active_record/has_many_association.rb | 29 - .../lib/thinking_sphinx/active_record/search.rb | 57 -- .../thinking_sphinx/adapters/abstract_adapter.rb | 42 -- .../lib/thinking_sphinx/adapters/mysql_adapter.rb | 54 -- .../thinking_sphinx/adapters/postgresql_adapter.rb | 130 ---- .../lib/thinking_sphinx/association.rb | 161 ----- .../lib/thinking_sphinx/attribute.rb | 358 ---------- .../lib/thinking_sphinx/class_facet.rb | 15 - .../lib/thinking_sphinx/collection.rb | 147 ---- .../lib/thinking_sphinx/configuration.rb | 237 ------- .../lib/thinking_sphinx/core/string.rb | 15 - .../thinking-sphinx/lib/thinking_sphinx/deltas.rb | 27 - .../lib/thinking_sphinx/deltas/datetime_delta.rb | 50 -- .../lib/thinking_sphinx/deltas/default_delta.rb | 67 -- .../lib/thinking_sphinx/deltas/delayed_delta.rb | 25 - .../deltas/delayed_delta/delta_job.rb | 24 - .../deltas/delayed_delta/flag_as_deleted_job.rb | 27 - .../thinking_sphinx/deltas/delayed_delta/job.rb | 26 - .../thinking-sphinx/lib/thinking_sphinx/facet.rb | 58 -- .../lib/thinking_sphinx/facet_collection.rb | 60 -- .../thinking-sphinx/lib/thinking_sphinx/field.rb | 172 ----- .../thinking-sphinx/lib/thinking_sphinx/index.rb | 423 ----------- .../lib/thinking_sphinx/index/builder.rb | 264 ------- .../lib/thinking_sphinx/index/faux_column.rb | 110 --- .../lib/thinking_sphinx/rails_additions.rb | 136 ---- .../thinking-sphinx/lib/thinking_sphinx/search.rb | 780 --------------------- .../thinking-sphinx/lib/thinking_sphinx/tasks.rb | 128 ---- 30 files changed, 4140 deletions(-) delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/delta.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/has_many_association.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/search.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/abstract_adapter.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/mysql_adapter.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/postgresql_adapter.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/association.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/attribute.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/class_facet.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/collection.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/configuration.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/core/string.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/datetime_delta.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/default_delta.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/job.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/facet.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/facet_collection.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/field.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index/builder.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index/faux_column.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/rails_additions.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/search.rb delete mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/tasks.rb (limited to 'vendor/plugins/thinking-sphinx/lib') diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx.rb deleted file mode 100644 index 745643f..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx.rb +++ /dev/null @@ -1,180 +0,0 @@ -Dir[File.join(File.dirname(__FILE__), '../vendor/*/lib')].each do |path| - $LOAD_PATH.unshift path -end - -require 'active_record' -require 'riddle' -require 'after_commit' - -require 'thinking_sphinx/core/string' -require 'thinking_sphinx/active_record' -require 'thinking_sphinx/association' -require 'thinking_sphinx/attribute' -require 'thinking_sphinx/collection' -require 'thinking_sphinx/configuration' -require 'thinking_sphinx/facet' -require 'thinking_sphinx/class_facet' -require 'thinking_sphinx/facet_collection' -require 'thinking_sphinx/field' -require 'thinking_sphinx/index' -require 'thinking_sphinx/rails_additions' -require 'thinking_sphinx/search' -require 'thinking_sphinx/deltas' - -require 'thinking_sphinx/adapters/abstract_adapter' -require 'thinking_sphinx/adapters/mysql_adapter' -require 'thinking_sphinx/adapters/postgresql_adapter' - -ActiveRecord::Base.send(:include, ThinkingSphinx::ActiveRecord) - -Merb::Plugins.add_rakefiles( - File.join(File.dirname(__FILE__), "thinking_sphinx", "tasks") -) if defined?(Merb) - -module ThinkingSphinx - module Version #:nodoc: - Major = 1 - Minor = 1 - Tiny = 6 - - String = [Major, Minor, Tiny].join('.') - end - - # A ConnectionError will get thrown when a connection to Sphinx can't be - # made. - class ConnectionError < StandardError - end - - # A StaleIdsException is thrown by Collection.instances_from_matches if there - # are records in Sphinx but not in the database, so the search can be retried. - class StaleIdsException < StandardError - attr_accessor :ids - def initialize(ids) - self.ids = ids - end - end - - # The collection of indexed models. Keep in mind that Rails lazily loads - # its classes, so this may not actually be populated with _all_ the models - # that have Sphinx indexes. - def self.indexed_models - @@indexed_models ||= [] - end - - def self.unique_id_expression(offset = nil) - "* #{ThinkingSphinx.indexed_models.size} + #{offset || 0}" - end - - # Check if index definition is disabled. - # - def self.define_indexes? - @@define_indexes = true unless defined?(@@define_indexes) - @@define_indexes == true - end - - # Enable/disable indexes - you may want to do this while migrating data. - # - # ThinkingSphinx.define_indexes = false - # - def self.define_indexes=(value) - @@define_indexes = value - end - - @@deltas_enabled = nil - - # Check if delta indexing is enabled. - # - def self.deltas_enabled? - @@deltas_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@deltas_enabled.nil? - @@deltas_enabled - end - - # Enable/disable all delta indexing. - # - # ThinkingSphinx.deltas_enabled = false - # - def self.deltas_enabled=(value) - @@deltas_enabled = value - end - - @@updates_enabled = nil - - # Check if updates are enabled. True by default, unless within the test - # environment. - # - def self.updates_enabled? - @@updates_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@updates_enabled.nil? - @@updates_enabled - end - - # Enable/disable updates to Sphinx - # - # ThinkingSphinx.updates_enabled = false - # - def self.updates_enabled=(value) - @@updates_enabled = value - end - - @@suppress_delta_output = false - - def self.suppress_delta_output? - @@suppress_delta_output - end - - def self.suppress_delta_output=(value) - @@suppress_delta_output = value - end - - # Checks to see if MySQL will allow simplistic GROUP BY statements. If not, - # or if not using MySQL, this will return false. - # - def self.use_group_by_shortcut? - !!( - mysql? && ::ActiveRecord::Base.connection.select_all( - "SELECT @@global.sql_mode, @@session.sql_mode;" - ).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? } - ) - end - - def self.sphinx_running? - !!sphinx_pid && pid_active?(sphinx_pid) - end - - def self.sphinx_pid - pid_file = ThinkingSphinx::Configuration.instance.pid_file - cat_command = 'cat' - return nil unless File.exists?(pid_file) - - if microsoft? - pid_file.gsub!('/', '\\') - cat_command = 'type' - end - - `#{cat_command} #{pid_file}`[/\d+/] - end - - def self.pid_active?(pid) - return true if microsoft? - - begin - # In JRuby this returns -1 if the process doesn't exist - Process.getpgid(pid.to_i) != -1 - rescue Exception => e - false - end - end - - def self.microsoft? - RUBY_PLATFORM =~ /mswin/ - end - - def self.jruby? - defined?(JRUBY_VERSION) - end - - def self.mysql? - ::ActiveRecord::Base.connection.class.name.demodulize == "MysqlAdapter" || ( - jruby? && ::ActiveRecord::Base.connection.config[:adapter] == "jdbcmysql" - ) - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record.rb deleted file mode 100644 index 3e85b50..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record.rb +++ /dev/null @@ -1,260 +0,0 @@ -require 'thinking_sphinx/active_record/delta' -require 'thinking_sphinx/active_record/search' -require 'thinking_sphinx/active_record/has_many_association' - -module ThinkingSphinx - # Core additions to ActiveRecord models - define_index for creating indexes - # for models. If you want to interrogate the index objects created for the - # model, you can use the class-level accessor :sphinx_indexes. - # - module ActiveRecord - def self.included(base) - base.class_eval do - class_inheritable_array :sphinx_indexes, :sphinx_facets - class << self - # Allows creation of indexes for Sphinx. If you don't do this, there - # isn't much point trying to search (or using this plugin at all, - # really). - # - # An example or two: - # - # define_index - # indexes :id, :as => :model_id - # indexes name - # end - # - # You can also grab fields from associations - multiple levels deep - # if necessary. - # - # define_index do - # indexes tags.name, :as => :tag - # indexes articles.content - # indexes orders.line_items.product.name, :as => :product - # end - # - # And it will automatically concatenate multiple fields: - # - # define_index do - # indexes [author.first_name, author.last_name], :as => :author - # end - # - # The #indexes method is for fields - if you want attributes, use - # #has instead. All the same rules apply - but keep in mind that - # attributes are for sorting, grouping and filtering, not searching. - # - # define_index do - # # fields ... - # - # has created_at, updated_at - # end - # - # One last feature is the delta index. This requires the model to - # have a boolean field named 'delta', and is enabled as follows: - # - # define_index do - # # fields ... - # # attributes ... - # - # set_property :delta => true - # end - # - # Check out the more detailed documentation for each of these methods - # at ThinkingSphinx::Index::Builder. - # - def define_index(&block) - return unless ThinkingSphinx.define_indexes? - - self.sphinx_indexes ||= [] - index = Index.new(self, &block) - - self.sphinx_indexes << index - unless ThinkingSphinx.indexed_models.include?(self.name) - ThinkingSphinx.indexed_models << self.name - end - - if index.delta? - before_save :toggle_delta - after_commit :index_delta - end - - after_destroy :toggle_deleted - - index - end - alias_method :sphinx_index, :define_index - - def sphinx_index_options - sphinx_indexes.last.options - end - - # Generate a unique CRC value for the model's name, to use to - # determine which Sphinx documents belong to which AR records. - # - # Really only written for internal use - but hey, if it's useful to - # you in some other way, awesome. - # - def to_crc32 - self.name.to_crc32 - end - - def to_crc32s - (subclasses << self).collect { |klass| klass.to_crc32 } - end - - def source_of_sphinx_index - possible_models = self.sphinx_indexes.collect { |index| index.model } - return self if possible_models.include?(self) - - parent = self.superclass - while !possible_models.include?(parent) && parent != ::ActiveRecord::Base - parent = parent.superclass - end - - return parent - end - - def to_riddle(offset) - sphinx_database_adapter.setup - - indexes = [to_riddle_for_core(offset)] - indexes << to_riddle_for_delta(offset) if sphinx_delta? - indexes << to_riddle_for_distributed - end - - def sphinx_database_adapter - @sphinx_database_adapter ||= - ThinkingSphinx::AbstractAdapter.detect(self) - end - - private - - def sphinx_name - self.name.underscore.tr(':/\\', '_') - end - - def sphinx_delta? - self.sphinx_indexes.any? { |index| index.delta? } - end - - def to_riddle_for_core(offset) - index = Riddle::Configuration::Index.new("#{sphinx_name}_core") - index.path = File.join( - ThinkingSphinx::Configuration.instance.searchd_file_path, index.name - ) - - set_configuration_options_for_indexes index - set_field_settings_for_indexes index - - self.sphinx_indexes.select { |ts_index| - ts_index.model == self - }.each_with_index do |ts_index, i| - index.sources << ts_index.to_riddle_for_core(offset, i) - end - - index - end - - def to_riddle_for_delta(offset) - index = Riddle::Configuration::Index.new("#{sphinx_name}_delta") - index.parent = "#{sphinx_name}_core" - index.path = File.join(ThinkingSphinx::Configuration.instance.searchd_file_path, index.name) - - self.sphinx_indexes.each_with_index do |ts_index, i| - index.sources << ts_index.to_riddle_for_delta(offset, i) if ts_index.delta? - end - - index - end - - def to_riddle_for_distributed - index = Riddle::Configuration::DistributedIndex.new(sphinx_name) - index.local_indexes << "#{sphinx_name}_core" - index.local_indexes.unshift "#{sphinx_name}_delta" if sphinx_delta? - index - end - - def set_configuration_options_for_indexes(index) - ThinkingSphinx::Configuration.instance.index_options.each do |key, value| - index.send("#{key}=".to_sym, value) - end - - self.sphinx_indexes.each do |ts_index| - ts_index.options.each do |key, value| - index.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) && !value.nil? - end - end - end - - def set_field_settings_for_indexes(index) - field_names = lambda { |field| field.unique_name.to_s } - - self.sphinx_indexes.each do |ts_index| - index.prefix_field_names += ts_index.prefix_fields.collect(&field_names) - index.infix_field_names += ts_index.infix_fields.collect(&field_names) - end - end - end - end - - base.send(:include, ThinkingSphinx::ActiveRecord::Delta) - base.send(:include, ThinkingSphinx::ActiveRecord::Search) - - ::ActiveRecord::Associations::HasManyAssociation.send( - :include, ThinkingSphinx::ActiveRecord::HasManyAssociation - ) - ::ActiveRecord::Associations::HasManyThroughAssociation.send( - :include, ThinkingSphinx::ActiveRecord::HasManyAssociation - ) - end - - def in_index?(suffix) - self.class.search_for_id self.sphinx_document_id, sphinx_index_name(suffix) - end - - def in_core_index? - in_index? "core" - end - - def in_delta_index? - in_index? "delta" - end - - def in_both_indexes? - in_core_index? && in_delta_index? - end - - def toggle_deleted - return unless ThinkingSphinx.updates_enabled? && ThinkingSphinx.sphinx_running? - - config = ThinkingSphinx::Configuration.instance - client = Riddle::Client.new config.address, config.port - - client.update( - "#{self.class.sphinx_indexes.first.name}_core", - ['sphinx_deleted'], - {self.sphinx_document_id => 1} - ) if self.in_core_index? - - client.update( - "#{self.class.sphinx_indexes.first.name}_delta", - ['sphinx_deleted'], - {self.sphinx_document_id => 1} - ) if ThinkingSphinx.deltas_enabled? && - self.class.sphinx_indexes.any? { |index| index.delta? } && - self.toggled_delta? - rescue ::ThinkingSphinx::ConnectionError - # nothing - end - - def sphinx_document_id - (self.id * ThinkingSphinx.indexed_models.size) + - ThinkingSphinx.indexed_models.index(self.class.source_of_sphinx_index.name) - end - - private - - def sphinx_index_name(suffix) - "#{self.class.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_#{suffix}" - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/delta.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/delta.rb deleted file mode 100644 index f21aba2..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/delta.rb +++ /dev/null @@ -1,78 +0,0 @@ -module ThinkingSphinx - module ActiveRecord - # This module contains all the delta-related code for models. There isn't - # really anything you need to call manually in here - except perhaps - # index_delta, but not sure what reason why. - # - module Delta - # Code for after_commit callback is written by Eli Miller: - # http://elimiller.blogspot.com/2007/06/proper-cache-expiry-with-aftercommit.html - # with slight modification from Joost Hietbrink. - # - def self.included(base) - base.class_eval do - class << self - # Temporarily disable delta indexing inside a block, then perform a single - # rebuild of index at the end. - # - # Useful when performing updates to batches of models to prevent - # the delta index being rebuilt after each individual update. - # - # In the following example, the delta index will only be rebuilt once, - # not 10 times. - # - # SomeModel.suspended_delta do - # 10.times do - # SomeModel.create( ... ) - # end - # end - # - def suspended_delta(reindex_after = true, &block) - original_setting = ThinkingSphinx.deltas_enabled? - ThinkingSphinx.deltas_enabled = false - begin - yield - ensure - ThinkingSphinx.deltas_enabled = original_setting - self.index_delta if reindex_after - end - end - - # Build the delta index for the related model. This won't be called - # if running in the test environment. - # - def index_delta(instance = nil) - delta_object.index(self, instance) - end - - def delta_object - self.sphinx_indexes.first.delta_object - end - end - - def toggled_delta? - self.class.delta_object.toggled(self) - end - - private - - # Set the delta value for the model to be true. - def toggle_delta - self.class.delta_object.toggle(self) if should_toggle_delta? - end - - # Build the delta index for the related model. This won't be called - # if running in the test environment. - # - def index_delta - self.class.index_delta(self) if self.class.delta_object.toggled(self) - end - - def should_toggle_delta? - !self.respond_to?(:changed?) || self.changed? || self.new_record? - end - end - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/has_many_association.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/has_many_association.rb deleted file mode 100644 index 44b25c0..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/has_many_association.rb +++ /dev/null @@ -1,29 +0,0 @@ -module ThinkingSphinx - module ActiveRecord - module HasManyAssociation - def search(*args) - foreign_key = @reflection.primary_key_name - stack = [@reflection.options[:through]].compact - - attribute = nil - (@reflection.klass.sphinx_indexes || []).each do |index| - attribute = index.attributes.detect { |attrib| - attrib.columns.length == 1 && - attrib.columns.first.__name == foreign_key.to_sym && - attrib.columns.first.__stack == stack - } - break if attribute - end - - raise "Missing Attribute for Foreign Key #{foreign_key}" unless attribute - - options = args.extract_options! - options[:with] ||= {} - options[:with][attribute.unique_name] = @owner.id - - args << options - @reflection.klass.search(*args) - end - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/search.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/search.rb deleted file mode 100644 index fc3f2b4..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/active_record/search.rb +++ /dev/null @@ -1,57 +0,0 @@ -module ThinkingSphinx - module ActiveRecord - # This module covers the specific model searches - but the syntax is - # exactly the same as the core Search class - so use that as your refence - # point. - # - module Search - def self.included(base) - base.class_eval do - class << self - # Searches for results that match the parameters provided. Will only - # return the ids for the matching objects. See - # ThinkingSphinx::Search#search for syntax examples. - # - def search_for_ids(*args) - options = args.extract_options! - options[:class] = self - args << options - ThinkingSphinx::Search.search_for_ids(*args) - end - - # Searches for results limited to a single model. See - # ThinkingSphinx::Search#search for syntax examples. - # - def search(*args) - options = args.extract_options! - options[:class] = self - args << options - ThinkingSphinx::Search.search(*args) - end - - def search_count(*args) - options = args.extract_options! - options[:class] = self - args << options - ThinkingSphinx::Search.count(*args) - end - - def search_for_id(*args) - options = args.extract_options! - options[:class] = self - args << options - ThinkingSphinx::Search.search_for_id(*args) - end - - def facets(*args) - options = args.extract_options! - options[:class] = self - args << options - ThinkingSphinx::Search.facets(*args) - end - end - end - end - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/abstract_adapter.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/abstract_adapter.rb deleted file mode 100644 index b68b75e..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/abstract_adapter.rb +++ /dev/null @@ -1,42 +0,0 @@ -module ThinkingSphinx - class AbstractAdapter - def initialize(model) - @model = model - end - - def setup - # Deliberately blank - subclasses should do something though. Well, if - # they need to. - end - - def self.detect(model) - case model.connection.class.name - when "ActiveRecord::ConnectionAdapters::MysqlAdapter", - "ActiveRecord::ConnectionAdapters::MysqlplusAdapter" - ThinkingSphinx::MysqlAdapter.new model - when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter" - ThinkingSphinx::PostgreSQLAdapter.new model - when "ActiveRecord::ConnectionAdapters::JdbcAdapter" - if model.connection.config[:adapter] == "jdbcmysql" - ThinkingSphinx::MysqlAdapter.new model - elsif model.connection.config[:adapter] == "jdbcpostgresql" - ThinkingSphinx::PostgreSQLAdapter.new model - else - raise "Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL" - end - else - raise "Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL, not #{model.connection.class.name}" - end - end - - def quote_with_table(column) - "#{@model.quoted_table_name}.#{@model.connection.quote_column_name(column)}" - end - - protected - - def connection - @connection ||= @model.connection - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/mysql_adapter.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/mysql_adapter.rb deleted file mode 100644 index 597d4b6..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/mysql_adapter.rb +++ /dev/null @@ -1,54 +0,0 @@ -module ThinkingSphinx - class MysqlAdapter < AbstractAdapter - def setup - # Does MySQL actually need to do anything? - end - - def sphinx_identifier - "mysql" - end - - def concatenate(clause, separator = ' ') - "CONCAT_WS('#{separator}', #{clause})" - end - - def group_concatenate(clause, separator = ' ') - "GROUP_CONCAT(DISTINCT #{clause} SEPARATOR '#{separator}')" - end - - def cast_to_string(clause) - "CAST(#{clause} AS CHAR)" - end - - def cast_to_datetime(clause) - "UNIX_TIMESTAMP(#{clause})" - end - - def cast_to_unsigned(clause) - "CAST(#{clause} AS UNSIGNED)" - end - - def convert_nulls(clause, default = '') - default = "'#{default}'" if default.is_a?(String) - - "IFNULL(#{clause}, #{default})" - end - - def boolean(value) - value ? 1 : 0 - end - - def crc(clause, blank_to_null = false) - clause = "NULLIF(#{clause},'')" if blank_to_null - "CRC32(#{clause})" - end - - def utf8_query_pre - "SET NAMES utf8" - end - - def time_difference(diff) - "DATE_SUB(NOW(), INTERVAL #{diff} SECOND)" - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/postgresql_adapter.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/postgresql_adapter.rb deleted file mode 100644 index 625971d..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/postgresql_adapter.rb +++ /dev/null @@ -1,130 +0,0 @@ -module ThinkingSphinx - class PostgreSQLAdapter < AbstractAdapter - def setup - create_array_accum_function - create_crc32_function - end - - def sphinx_identifier - "pgsql" - end - - def concatenate(clause, separator = ' ') - clause.split(', ').collect { |field| - "COALESCE(CAST(#{field} as varchar), '')" - }.join(" || '#{separator}' || ") - end - - def group_concatenate(clause, separator = ' ') - "array_to_string(array_accum(#{clause}), '#{separator}')" - end - - def cast_to_string(clause) - clause - end - - def cast_to_datetime(clause) - "cast(extract(epoch from #{clause}) as int)" - end - - def cast_to_unsigned(clause) - clause - end - - def convert_nulls(clause, default = '') - default = "'#{default}'" if default.is_a?(String) - - "COALESCE(#{clause}, #{default})" - end - - def boolean(value) - value ? 'TRUE' : 'FALSE' - end - - def crc(clause, blank_to_null = false) - clause = "NULLIF(#{clause},'')" if blank_to_null - "crc32(#{clause})" - end - - def utf8_query_pre - nil - end - - def time_difference(diff) - "current_timestamp - interval '#{diff} seconds'" - end - - private - - def execute(command, output_error = false) - connection.execute "begin" - connection.execute "savepoint ts" - begin - connection.execute command - rescue StandardError => err - puts err if output_error - connection.execute "rollback to savepoint ts" - end - connection.execute "release savepoint ts" - connection.execute "commit" - end - - def create_array_accum_function - if connection.raw_connection.respond_to?(:server_version) && connection.raw_connection.server_version > 80200 - execute <<-SQL - CREATE AGGREGATE array_accum (anyelement) - ( - sfunc = array_append, - stype = anyarray, - initcond = '{}' - ); - SQL - else - execute <<-SQL - CREATE AGGREGATE array_accum - ( - basetype = anyelement, - sfunc = array_append, - stype = anyarray, - initcond = '{}' - ); - SQL - end - end - - def create_crc32_function - execute "CREATE LANGUAGE 'plpgsql';" - function = <<-SQL - CREATE OR REPLACE FUNCTION crc32(word text) - RETURNS bigint AS $$ - DECLARE tmp bigint; - DECLARE i int; - DECLARE j int; - DECLARE word_array bytea; - BEGIN - i = 0; - tmp = 4294967295; - word_array = decode(replace(word, E'\\\\', E'\\\\\\\\'), 'escape'); - LOOP - tmp = (tmp # get_byte(word_array, i))::bigint; - i = i + 1; - j = 0; - LOOP - tmp = ((tmp >> 1) # (3988292384 * (tmp & 1)))::bigint; - j = j + 1; - IF j >= 8 THEN - EXIT; - END IF; - END LOOP; - IF i >= char_length(word) THEN - EXIT; - END IF; - END LOOP; - return (tmp # 4294967295); - END - $$ IMMUTABLE STRICT LANGUAGE plpgsql; - SQL - execute function, true - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/association.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/association.rb deleted file mode 100644 index b057035..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/association.rb +++ /dev/null @@ -1,161 +0,0 @@ -module ThinkingSphinx - # Association tracks a specific reflection and join to reference data that - # isn't in the base model. Very much an internal class for Thinking Sphinx - - # perhaps because I feel it's not as strong (or simple) as most of the rest. - # - class Association - attr_accessor :parent, :reflection, :join - - # Create a new association by passing in the parent association, and the - # corresponding reflection instance. If there is no parent, pass in nil. - # - # top = Association.new nil, top_reflection - # child = Association.new top, child_reflection - # - def initialize(parent, reflection) - @parent, @reflection = parent, reflection - @children = {} - end - - # Get the children associations for a given association name. The only time - # that there'll actually be more than one association is when the - # relationship is polymorphic. To keep things simple though, it will always - # be an Array that gets returned (an empty one if no matches). - # - # # where pages is an association on the class tied to the reflection. - # association.children(:pages) - # - def children(assoc) - @children[assoc] ||= Association.children(@reflection.klass, assoc, self) - end - - # Get the children associations for a given class, association name and - # parent association. Much like the instance method of the same name, it - # will return an empty array if no associations have the name, and only - # have multiple association instances if the underlying relationship is - # polymorphic. - # - # Association.children(User, :pages, user_association) - # - def self.children(klass, assoc, parent=nil) - ref = klass.reflect_on_association(assoc) - - return [] if ref.nil? - return [Association.new(parent, ref)] unless ref.options[:polymorphic] - - # association is polymorphic - create associations for each - # non-polymorphic reflection. - polymorphic_classes(ref).collect { |klass| - Association.new parent, ::ActiveRecord::Reflection::AssociationReflection.new( - ref.macro, - "#{ref.name}_#{klass.name}".to_sym, - casted_options(klass, ref), - ref.active_record - ) - } - end - - # Link up the join for this model from a base join - and set parent - # associations' joins recursively. - # - def join_to(base_join) - parent.join_to(base_join) if parent && parent.join.nil? - - @join ||= ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.new( - @reflection, base_join, parent ? parent.join : base_join.joins.first - ) - end - - # Returns the association's join SQL statements - and it replaces - # ::ts_join_alias:: with the aliased table name so the generated reflection - # join conditions avoid column name collisions. - # - def to_sql - @join.association_join.gsub(/::ts_join_alias::/, - "#{@reflection.klass.connection.quote_table_name(@join.parent.aliased_table_name)}" - ) - end - - # Returns true if the association - or a parent - is a has_many or - # has_and_belongs_to_many. - # - def is_many? - case @reflection.macro - when :has_many, :has_and_belongs_to_many - true - else - @parent ? @parent.is_many? : false - end - end - - # Returns an array of all the associations that lead to this one - starting - # with the top level all the way to the current association object. - # - def ancestors - (parent ? parent.ancestors : []) << self - end - - def has_column?(column) - @reflection.klass.column_names.include?(column.to_s) - end - - def primary_key_from_reflection - if @reflection.options[:through] - @reflection.source_reflection.options[:foreign_key] || - @reflection.source_reflection.primary_key_name - else - nil - end - end - - def table - if @reflection.options[:through] - @join.aliased_join_table_name - else - @join.aliased_table_name - end - end - - private - - # Returns all the objects that could be currently instantiated from a - # polymorphic association. This is pretty damn fast if there's an index on - # the foreign type column - but if there isn't, it can take a while if you - # have a lot of data. - # - def self.polymorphic_classes(ref) - ref.active_record.connection.select_all( - "SELECT DISTINCT #{ref.options[:foreign_type]} " + - "FROM #{ref.active_record.table_name} " + - "WHERE #{ref.options[:foreign_type]} IS NOT NULL" - ).collect { |row| - row[ref.options[:foreign_type]].constantize - } - end - - # Returns a new set of options for an association that mimics an existing - # polymorphic relationship for a specific class. It adds a condition to - # filter by the appropriate object. - # - def self.casted_options(klass, ref) - options = ref.options.clone - options[:polymorphic] = nil - options[:class_name] = klass.name - options[:foreign_key] ||= "#{ref.name}_id" - - quoted_foreign_type = klass.connection.quote_column_name ref.options[:foreign_type] - case options[:conditions] - when nil - options[:conditions] = "::ts_join_alias::.#{quoted_foreign_type} = '#{klass.name}'" - when Array - options[:conditions] << "::ts_join_alias::.#{quoted_foreign_type} = '#{klass.name}'" - when Hash - options[:conditions].merge!(ref.options[:foreign_type] => klass.name) - else - options[:conditions] << " AND ::ts_join_alias::.#{quoted_foreign_type} = '#{klass.name}'" - end - - options - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/attribute.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/attribute.rb deleted file mode 100644 index 1d45b2e..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/attribute.rb +++ /dev/null @@ -1,358 +0,0 @@ -module ThinkingSphinx - # Attributes - eternally useful when it comes to filtering, sorting or - # grouping. This class isn't really useful to you unless you're hacking - # around with the internals of Thinking Sphinx - but hey, don't let that - # stop you. - # - # One key thing to remember - if you're using the attribute manually to - # generate SQL statements, you'll need to set the base model, and all the - # associations. Which can get messy. Use Index.link!, it really helps. - # - class Attribute - attr_accessor :alias, :columns, :associations, :model, :faceted, :source - - # To create a new attribute, you'll need to pass in either a single Column - # or an array of them, and some (optional) options. - # - # Valid options are: - # - :as => :alias_name - # - :type => :attribute_type - # - :source => :field, :query, :ranged_query - # - # Alias is only required in three circumstances: when there's - # another attribute or field with the same name, when the column name is - # 'id', or when there's more than one column. - # - # Type is not required, unless you want to force a column to be a certain - # type (but keep in mind the value will not be CASTed in the SQL - # statements). The only time you really need to use this is when the type - # can't be figured out by the column - ie: when not actually using a - # database column as your source. - # - # Source is only used for multi-value attributes (MVA). By default this will - # use a left-join and a group_concat to obtain the values. For better performance - # during indexing it can be beneficial to let Sphinx use a separate query to retrieve - # all document,value-pairs. - # Either :query or :ranged_query will enable this feature, where :ranged_query will cause - # the query to be executed incremental. - # - # Example usage: - # - # Attribute.new( - # Column.new(:created_at) - # ) - # - # Attribute.new( - # Column.new(:posts, :id), - # :as => :post_ids - # ) - # - # Attribute.new( - # Column.new(:posts, :id), - # :as => :post_ids, - # :source => :ranged_query - # ) - # - # Attribute.new( - # [Column.new(:pages, :id), Column.new(:articles, :id)], - # :as => :content_ids - # ) - # - # Attribute.new( - # Column.new("NOW()"), - # :as => :indexed_at, - # :type => :datetime - # ) - # - # If you're creating attributes for latitude and longitude, don't forget - # that Sphinx expects these values to be in radians. - # - def initialize(columns, options = {}) - @columns = Array(columns) - @associations = {} - - raise "Cannot define a field with no columns. Maybe you are trying to index a field with a reserved name (id, name). You can fix this error by using a symbol rather than a bare name (:id instead of id)." if @columns.empty? || @columns.any? { |column| !column.respond_to?(:__stack) } - - @alias = options[:as] - @type = options[:type] - @faceted = options[:facet] - @source = options[:source] - @crc = options[:crc] - - @type ||= :multi unless @source.nil? - @type = :integer if @type == :string && @crc - end - - # Get the part of the SELECT clause related to this attribute. Don't forget - # to set your model and associations first though. - # - # This will concatenate strings and arrays of integers, and convert - # datetimes to timestamps, as needed. - # - def to_select_sql - return nil unless include_as_association? - - clause = @columns.collect { |column| - column_with_prefix(column) - }.join(', ') - - separator = all_ints? ? ',' : ' ' - - clause = adapter.concatenate(clause, separator) if concat_ws? - clause = adapter.group_concatenate(clause, separator) if is_many? - clause = adapter.cast_to_datetime(clause) if type == :datetime - clause = adapter.convert_nulls(clause) if type == :string - clause = adapter.crc(clause) if @crc - - "#{clause} AS #{quote_column(unique_name)}" - end - - # Get the part of the GROUP BY clause related to this attribute - if one is - # needed. If not, all you'll get back is nil. The latter will happen if - # there isn't actually a real column to get data from, or if there's - # multiple data values (read: a has_many or has_and_belongs_to_many - # association). - # - def to_group_sql - case - when is_many?, is_string?, ThinkingSphinx.use_group_by_shortcut? - nil - else - @columns.collect { |column| - column_with_prefix(column) - } - end - end - - def type_to_config - { - :multi => :sql_attr_multi, - :datetime => :sql_attr_timestamp, - :string => :sql_attr_str2ordinal, - :float => :sql_attr_float, - :boolean => :sql_attr_bool, - :integer => :sql_attr_uint - }[type] - end - - def include_as_association? - ! (type == :multi && (source == :query || source == :ranged_query)) - end - - # Returns the configuration value that should be used for - # the attribute. - # Special case is the multi-valued attribute that needs some - # extra configuration. - # - def config_value(offset = nil) - if type == :multi - multi_config = include_as_association? ? "field" : - source_value(offset).gsub(/\n\s*/, " ") - "uint #{unique_name} from #{multi_config}" - else - unique_name - end - end - - # Returns the unique name of the attribute - which is either the alias of - # the attribute, or the name of the only column - if there is only one. If - # there isn't, there should be an alias. Else things probably won't work. - # Consider yourself warned. - # - def unique_name - if @columns.length == 1 - @alias || @columns.first.__name - else - @alias - end - end - - # Returns the type of the column. If that's not already set, it returns - # :multi if there's the possibility of more than one value, :string if - # there's more than one association, otherwise it figures out what the - # actual column's datatype is and returns that. - # - def type - @type ||= begin - base_type = case - when is_many?, is_many_ints? - :multi - when @associations.values.flatten.length > 1 - :string - else - translated_type_from_database - end - - if base_type == :string && @crc - :integer - else - @crc = false - base_type - end - end - end - - def to_facet - return nil unless @faceted - - ThinkingSphinx::Facet.new(self) - end - - private - - def source_value(offset) - if is_string? - "#{source.to_s.dasherize}; #{columns.first.__name}" - elsif source == :ranged_query - "ranged-query; #{query offset} #{query_clause}; #{range_query}" - else - "query; #{query offset}" - end - end - - def query(offset) - assoc = association_for_mva - raise "Could not determine SQL for MVA" if assoc.nil? - - <<-SQL -SELECT #{foreign_key_for_mva assoc} - #{ThinkingSphinx.unique_id_expression(offset)} AS #{quote_column('id')}, - #{primary_key_for_mva(assoc)} AS #{quote_column(unique_name)} -FROM #{quote_table_name assoc.table} - SQL - end - - def query_clause - foreign_key = foreign_key_for_mva association_for_mva - "WHERE #{foreign_key} >= $start AND #{foreign_key} <= $end" - end - - def range_query - assoc = association_for_mva - foreign_key = foreign_key_for_mva assoc - "SELECT MIN(#{foreign_key}), MAX(#{foreign_key}) FROM #{quote_table_name assoc.table}" - end - - def primary_key_for_mva(assoc) - quote_with_table( - assoc.table, assoc.primary_key_from_reflection || columns.first.__name - ) - end - - def foreign_key_for_mva(assoc) - quote_with_table assoc.table, assoc.reflection.primary_key_name - end - - def association_for_mva - @association_for_mva ||= associations[columns.first].detect { |assoc| - assoc.has_column?(columns.first.__name) - } - end - - def adapter - @adapter ||= @model.sphinx_database_adapter - end - - def quote_with_table(table, column) - "#{quote_table_name(table)}.#{quote_column(column)}" - end - - def quote_column(column) - @model.connection.quote_column_name(column) - end - - def quote_table_name(table_name) - @model.connection.quote_table_name(table_name) - end - - # Indication of whether the columns should be concatenated with a space - # between each value. True if there's either multiple sources or multiple - # associations. - # - def concat_ws? - multiple_associations? || @columns.length > 1 - end - - # Checks whether any column requires multiple associations (which only - # happens for polymorphic situations). - # - def multiple_associations? - associations.any? { |col,assocs| assocs.length > 1 } - end - - # Builds a column reference tied to the appropriate associations. This - # dives into the associations hash and their corresponding joins to - # figure out how to correctly reference a column in SQL. - # - def column_with_prefix(column) - if column.is_string? - column.__name - elsif associations[column].empty? - "#{@model.quoted_table_name}.#{quote_column(column.__name)}" - else - associations[column].collect { |assoc| - assoc.has_column?(column.__name) ? - "#{quote_table_name(assoc.join.aliased_table_name)}" + - ".#{quote_column(column.__name)}" : - nil - }.compact.join(', ') - end - end - - # Could there be more than one value related to the parent record? If so, - # then this will return true. If not, false. It's that simple. - # - def is_many? - associations.values.flatten.any? { |assoc| assoc.is_many? } - end - - def is_many_ints? - concat_ws? && all_ints? - end - - # Returns true if any of the columns are string values, instead of database - # column references. - def is_string? - columns.all? { |col| col.is_string? } - end - - def all_ints? - @columns.all? { |col| - klasses = @associations[col].empty? ? [@model] : - @associations[col].collect { |assoc| assoc.reflection.klass } - klasses.all? { |klass| - column = klass.columns.detect { |column| column.name == col.__name.to_s } - !column.nil? && column.type == :integer - } - } - end - - def type_from_database - klass = @associations.values.flatten.first ? - @associations.values.flatten.first.reflection.klass : @model - - klass.columns.detect { |col| - @columns.collect { |c| c.__name.to_s }.include? col.name - }.type - end - - def translated_type_from_database - case type_from_db = type_from_database - when :datetime, :string, :float, :boolean, :integer - type_from_db - when :decimal - :float - when :timestamp, :date - :datetime - else - raise <<-MESSAGE - -Cannot automatically map column type #{type_from_db} to an equivalent Sphinx -type (integer, float, boolean, datetime, string as ordinal). You could try to -explicitly convert the column's value in your define_index block: - has "CAST(column AS INT)", :type => :integer, :as => :column - MESSAGE - end - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/class_facet.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/class_facet.rb deleted file mode 100644 index cb301b8..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/class_facet.rb +++ /dev/null @@ -1,15 +0,0 @@ -module ThinkingSphinx - class ClassFacet < ThinkingSphinx::Facet - def name - :class - end - - def attribute_name - "class_crc" - end - - def value(object, attribute_value) - object.class.name - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/collection.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/collection.rb deleted file mode 100644 index 406c2ae..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/collection.rb +++ /dev/null @@ -1,147 +0,0 @@ -module ThinkingSphinx - class Collection < ::Array - attr_reader :total_entries, :total_pages, :current_page, :per_page - attr_accessor :results - - # Compatibility with older versions of will_paginate - alias_method :page_count, :total_pages - - def initialize(page, per_page, entries, total_entries) - @current_page, @per_page, @total_entries = page, per_page, total_entries - - @total_pages = (entries / @per_page.to_f).ceil - end - - def self.ids_from_results(results, page, limit, options) - collection = self.new(page, limit, - results[:total] || 0, results[:total_found] || 0 - ) - collection.results = results - collection.replace results[:matches].collect { |match| - match[:attributes]["sphinx_internal_id"] - } - return collection - end - - def self.create_from_results(results, page, limit, options) - collection = self.new(page, limit, - results[:total] || 0, results[:total_found] || 0 - ) - collection.results = results - collection.replace instances_from_matches(results[:matches], options) - return collection - end - - def self.instances_from_matches(matches, options = {}) - if klass = options[:class] - instances_from_class klass, matches, options - else - instances_from_classes matches, options - end - end - - def self.instances_from_class(klass, matches, options = {}) - index_options = klass.sphinx_index_options - - ids = matches.collect { |match| match[:attributes]["sphinx_internal_id"] } - instances = ids.length > 0 ? klass.find( - :all, - :conditions => {klass.primary_key.to_sym => ids}, - :include => (options[:include] || index_options[:include]), - :select => (options[:select] || index_options[:select]), - :order => (options[:sql_order] || index_options[:sql_order]) - ) : [] - - # Raise an exception if we find records in Sphinx but not in the DB, so - # the search method can retry without them. See - # ThinkingSphinx::Search.retry_search_on_stale_index. - if options[:raise_on_stale] && instances.length < ids.length - stale_ids = ids - instances.map {|i| i.id } - raise StaleIdsException, stale_ids - end - - # if the user has specified an SQL order, return the collection - # without rearranging it into the Sphinx order - return instances if options[:sql_order] - - ids.collect { |obj_id| - instances.detect { |obj| obj.id == obj_id } - } - end - - # Group results by class and call #find(:all) once for each group to reduce - # the number of #find's in multi-model searches. - # - def self.instances_from_classes(matches, options = {}) - groups = matches.group_by { |match| match[:attributes]["class_crc"] } - groups.each do |crc, group| - group.replace( - instances_from_class(class_from_crc(crc), group, options) - ) - end - - matches.collect do |match| - groups.detect { |crc, group| - crc == match[:attributes]["class_crc"] - }[1].detect { |obj| - obj.id == match[:attributes]["sphinx_internal_id"] - } - end - end - - def self.class_from_crc(crc) - @@models_by_crc ||= ThinkingSphinx.indexed_models.inject({}) do |hash, model| - hash[model.constantize.to_crc32] = model - model.constantize.subclasses.each { |subclass| - hash[subclass.to_crc32] = subclass.name - } - hash - end - @@models_by_crc[crc].constantize - end - - def previous_page - current_page > 1 ? (current_page - 1) : nil - end - - def next_page - current_page < total_pages ? (current_page + 1): nil - end - - def offset - (current_page - 1) * @per_page - end - - def method_missing(method, *args, &block) - super unless method.to_s[/^each_with_.*/] - - each_with_attribute method.to_s.gsub(/^each_with_/, ''), &block - end - - def each_with_groupby_and_count(&block) - results[:matches].each_with_index do |match, index| - yield self[index], match[:attributes]["@groupby"], match[:attributes]["@count"] - end - end - - def each_with_attribute(attribute, &block) - results[:matches].each_with_index do |match, index| - yield self[index], (match[:attributes][attribute] || match[:attributes]["@#{attribute}"]) - end - end - - def each_with_weighting(&block) - results[:matches].each_with_index do |match, index| - yield self[index], match[:weight] - end - end - - def inject_with_groupby_and_count(initial = nil, &block) - index = -1 - results[:matches].inject(initial) do |memo, match| - index += 1 - yield memo, self[index], match[:attributes]["@groupby"], match[:attributes]["@count"] - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/configuration.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/configuration.rb deleted file mode 100644 index 31543f5..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/configuration.rb +++ /dev/null @@ -1,237 +0,0 @@ -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. - # - # Here are the default settings, relative to RAILS_ROOT where relevant: - # - # config file:: config/#{environment}.sphinx.conf - # searchd log file:: log/searchd.log - # query log file:: log/searchd.query.log - # pid file:: log/searchd.#{environment}.pid - # searchd files:: db/sphinx/#{environment}/ - # address:: 127.0.0.1 - # port:: 3312 - # allow star:: false - # min prefix length:: 1 - # min infix length:: 1 - # mem limit:: 64M - # max matches:: 1000 - # morphology:: stem_en - # charset type:: utf-8 - # charset table:: nil - # ignore chars:: nil - # html strip:: false - # html remove elements:: '' - # - # If you want to change these settings, create a YAML file at - # config/sphinx.yml with settings for each environment, in a similar - # fashion to database.yml - using the following keys: config_file, - # searchd_log_file, query_log_file, pid_file, searchd_file_path, port, - # allow_star, enable_star, min_prefix_len, min_infix_len, mem_limit, - # max_matches, # morphology, charset_type, charset_table, ignore_chars, - # html_strip, # html_remove_elements. I think you've got the idea. - # - # Each setting in the YAML file is optional - so only put in the ones you - # want to change. - # - # 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, :allow_star, - :database_yml_file, :app_root, :bin_path, :model_directories - - 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_file_path = "#{self.app_root}/db/sphinx/#{environment}" - self.allow_star = false - 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) ? 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}" - - @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 @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 - 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 - } - - 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. - # - def parse_config - path = "#{app_root}/config/sphinx.yml" - return unless File.exists?(path) - - 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? - - self.bin_path += '/' unless self.bin_path.blank? - - if self.allow_star - self.index_options[:enable_star] = true - self.index_options[:min_prefix_len] = 1 - end - end - - 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 - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/core/string.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/core/string.rb deleted file mode 100644 index 7ab3e62..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/core/string.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'zlib' - -module ThinkingSphinx - module Core - module String - def to_crc32 - Zlib.crc32 self - end - end - end -end - -class String - include ThinkingSphinx::Core::String -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas.rb deleted file mode 100644 index 57c396e..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'thinking_sphinx/deltas/default_delta' -require 'thinking_sphinx/deltas/delayed_delta' -require 'thinking_sphinx/deltas/datetime_delta' - -module ThinkingSphinx - module Deltas - def self.parse(index, options) - delta_option = options.delete(:delta) - case delta_option - when TrueClass, :default - DefaultDelta.new index, options - when :delayed - DelayedDelta.new index, options - when :datetime - DatetimeDelta.new index, options - when FalseClass, nil - nil - else - if delta_option.ancestors.include?(ThinkingSphinx::Deltas::DefaultDelta) - delta_option.new index, options - else - raise "Unknown delta type" - end - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/datetime_delta.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/datetime_delta.rb deleted file mode 100644 index 2ee46d4..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/datetime_delta.rb +++ /dev/null @@ -1,50 +0,0 @@ -module ThinkingSphinx - module Deltas - class DatetimeDelta < ThinkingSphinx::Deltas::DefaultDelta - attr_accessor :column, :threshold - - def initialize(index, options) - @index = index - @column = options.delete(:delta_column) || :updated_at - @threshold = options.delete(:threshold) || 1.day - end - - def index(model, instance = nil) - # do nothing - true - end - - def delayed_index(model) - config = ThinkingSphinx::Configuration.instance - rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : "" - - output = `#{config.bin_path}indexer --config #{config.config_file} #{rotate} #{delta_index_name model}` - output += `#{config.bin_path}indexer --config #{config.config_file} #{rotate} --merge #{core_index_name model} #{delta_index_name model} --merge-dst-range sphinx_deleted 0 0` - puts output unless ThinkingSphinx.suppress_delta_output? - - true - end - - def toggle(instance) - # do nothing - end - - def toggled(instance) - instance.send(@column) > @threshold.ago - end - - def reset_query(model) - nil - end - - def clause(model, toggled) - if toggled - "#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}" + - " > #{adapter.time_difference(@threshold)}" - else - nil - end - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/default_delta.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/default_delta.rb deleted file mode 100644 index c973612..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/default_delta.rb +++ /dev/null @@ -1,67 +0,0 @@ -module ThinkingSphinx - module Deltas - class DefaultDelta - attr_accessor :column - - def initialize(index, options) - @index = index - @column = options.delete(:delta_column) || :delta - end - - def index(model, instance = nil) - return true unless ThinkingSphinx.updates_enabled? && - ThinkingSphinx.deltas_enabled? - return true if instance && !toggled(instance) - - config = ThinkingSphinx::Configuration.instance - client = Riddle::Client.new config.address, config.port - rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : "" - - output = `#{config.bin_path}indexer --config #{config.config_file} #{rotate} #{delta_index_name model}` - puts(output) unless ThinkingSphinx.suppress_delta_output? - - client.update( - core_index_name(model), - ['sphinx_deleted'], - {instance.sphinx_document_id => [1]} - ) if instance && ThinkingSphinx.sphinx_running? && instance.in_both_indexes? - - true - end - - def toggle(instance) - instance.delta = true - end - - def toggled(instance) - instance.delta - end - - def reset_query(model) - "UPDATE #{model.quoted_table_name} SET " + - "#{@index.quote_column(@column.to_s)} = #{adapter.boolean(false)}" - end - - def clause(model, toggled) - "#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}" + - " = #{adapter.boolean(toggled)}" - end - - protected - - def core_index_name(model) - "#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_core" - end - - def delta_index_name(model) - "#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta" - end - - private - - def adapter - @adapter = @index.model.sphinx_database_adapter - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta.rb deleted file mode 100644 index e95298b..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'delayed/job' - -require 'thinking_sphinx/deltas/delayed_delta/delta_job' -require 'thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job' -require 'thinking_sphinx/deltas/delayed_delta/job' - -module ThinkingSphinx - module Deltas - class DelayedDelta < ThinkingSphinx::Deltas::DefaultDelta - def index(model, instance = nil) - ThinkingSphinx::Deltas::Job.enqueue( - ThinkingSphinx::Deltas::DeltaJob.new(delta_index_name(model)) - ) - - Delayed::Job.enqueue( - ThinkingSphinx::Deltas::FlagAsDeletedJob.new( - core_index_name(model), instance.sphinx_document_id - ) - ) if instance - - true - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb deleted file mode 100644 index f9511ec..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +++ /dev/null @@ -1,24 +0,0 @@ -module ThinkingSphinx - module Deltas - class DeltaJob - attr_accessor :index - - def initialize(index) - @index = index - end - - def perform - return true unless ThinkingSphinx.updates_enabled? && - ThinkingSphinx.deltas_enabled? - - config = ThinkingSphinx::Configuration.instance - client = Riddle::Client.new config.address, config.port - - output = `#{config.bin_path}indexer --config #{config.config_file} --rotate #{index}` - puts output unless ThinkingSphinx.suppress_delta_output? - - true - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb deleted file mode 100644 index d6afd27..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +++ /dev/null @@ -1,27 +0,0 @@ -module ThinkingSphinx - module Deltas - class FlagAsDeletedJob - attr_accessor :index, :document_id - - def initialize(index, document_id) - @index, @document_id = index, document_id - end - - def perform - return true unless ThinkingSphinx.updates_enabled? - - config = ThinkingSphinx::Configuration.instance - client = Riddle::Client.new config.address, config.port - - client.update( - @index, - ['sphinx_deleted'], - {@document_id => [1]} - ) if ThinkingSphinx.sphinx_running? && - ThinkingSphinx::Search.search_for_id(@document_id, @index) - - true - end - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/job.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/job.rb deleted file mode 100644 index de0a7cb..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/deltas/delayed_delta/job.rb +++ /dev/null @@ -1,26 +0,0 @@ -module ThinkingSphinx - module Deltas - class Job < Delayed::Job - def self.enqueue(object, priority = 0) - super unless duplicates_exist(object) - end - - def self.cancel_thinking_sphinx_jobs - if connection.tables.include?("delayed_jobs") - delete_all("handler LIKE '--- !ruby/object:ThinkingSphinx::Deltas::%'") - end - end - - private - - def self.duplicates_exist(object) - count( - :conditions => { - :handler => object.to_yaml, - :locked_at => nil - } - ) > 0 - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/facet.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/facet.rb deleted file mode 100644 index e2db449..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/facet.rb +++ /dev/null @@ -1,58 +0,0 @@ -module ThinkingSphinx - class Facet - attr_reader :reference - - def initialize(reference) - @reference = reference - - if reference.columns.length != 1 - raise "Can't translate Facets on multiple-column field or attribute" - end - end - - def name - reference.unique_name - end - - def attribute_name - # @attribute_name ||= case @reference - # when Attribute - # @reference.unique_name.to_s - # when Field - @attribute_name ||= @reference.unique_name.to_s + "_facet" - # end - end - - def value(object, attribute_value) - return translate(object, attribute_value) if @reference.is_a?(Field) - - case @reference.type - when :string - translate(object, attribute_value) - when :datetime - Time.at(attribute_value) - when :boolean - attribute_value > 0 - else - attribute_value - end - end - - def to_s - name - end - - private - - def translate(object, attribute_value) - column.__stack.each { |method| - object = object.send(method) - } - object.send(column.__name) - end - - def column - @reference.columns.first - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/facet_collection.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/facet_collection.rb deleted file mode 100644 index 1ad9d1a..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/facet_collection.rb +++ /dev/null @@ -1,60 +0,0 @@ -module ThinkingSphinx - class FacetCollection < Hash - attr_accessor :arguments - - def initialize(arguments) - @arguments = arguments.clone - @attribute_values = {} - @facets = [] - end - - def add_from_results(facet, results) - facet = facet_from_object(results.first, facet) if facet.is_a?(String) - - self[facet.name] ||= {} - @attribute_values[facet.name] ||= {} - @facets << facet - - results.each_with_groupby_and_count { |result, group, count| - facet_value = facet.value(result, group) - - self[facet.name][facet_value] ||= 0 - self[facet.name][facet_value] += count - @attribute_values[facet.name][facet_value] = group - } - end - - def for(hash = {}) - arguments = @arguments.clone - options = arguments.extract_options! - options[:with] ||= {} - - hash.each do |key, value| - attrib = facet_for_key(key).attribute_name - options[:with][attrib] = underlying_value key, value - end - - arguments << options - ThinkingSphinx::Search.search *arguments - end - - private - - def underlying_value(key, value) - case value - when Array - value.collect { |item| underlying_value(key, item) } - else - @attribute_values[key][value] - end - end - - def facet_for_key(key) - @facets.detect { |facet| facet.name == key } - end - - def facet_from_object(object, name) - object.sphinx_facets.detect { |facet| facet.attribute_name == name } - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/field.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/field.rb deleted file mode 100644 index 9edaede..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/field.rb +++ /dev/null @@ -1,172 +0,0 @@ -module ThinkingSphinx - # Fields - holding the string data which Sphinx indexes for your searches. - # This class isn't really useful to you unless you're hacking around with the - # internals of Thinking Sphinx - but hey, don't let that stop you. - # - # One key thing to remember - if you're using the field manually to - # generate SQL statements, you'll need to set the base model, and all the - # associations. Which can get messy. Use Index.link!, it really helps. - # - class Field - attr_accessor :alias, :columns, :sortable, :associations, :model, :infixes, - :prefixes, :faceted - - # To create a new field, you'll need to pass in either a single Column - # or an array of them, and some (optional) options. The columns are - # references to the data that will make up the field. - # - # Valid options are: - # - :as => :alias_name - # - :sortable => true - # - :infixes => true - # - :prefixes => true - # - # Alias is only required in three circumstances: when there's - # another attribute or field with the same name, when the column name is - # 'id', or when there's more than one column. - # - # Sortable defaults to false - but is quite useful when set to true, as - # it creates an attribute with the same string value (which Sphinx converts - # to an integer value), which can be sorted by. Thinking Sphinx is smart - # enough to realise that when you specify fields in sort statements, you - # mean their respective attributes. - # - # If you have partial matching enabled (ie: enable_star), then you can - # specify certain fields to have their prefixes and infixes indexed. Keep - # in mind, though, that Sphinx's default is _all_ fields - so once you - # highlight a particular field, no other fields in the index will have - # these partial indexes. - # - # Here's some examples: - # - # Field.new( - # Column.new(:name) - # ) - # - # Field.new( - # [Column.new(:first_name), Column.new(:last_name)], - # :as => :name, :sortable => true - # ) - # - # Field.new( - # [Column.new(:posts, :subject), Column.new(:posts, :content)], - # :as => :posts, :prefixes => true - # ) - # - def initialize(columns, options = {}) - @columns = Array(columns) - @associations = {} - - raise "Cannot define a field with no columns. Maybe you are trying to index a field with a reserved name (id, name). You can fix this error by using a symbol rather than a bare name (:id instead of id)." if @columns.empty? || @columns.any? { |column| !column.respond_to?(:__stack) } - - @alias = options[:as] - @sortable = options[:sortable] || false - @infixes = options[:infixes] || false - @prefixes = options[:prefixes] || false - @faceted = options[:facet] || false - end - - # Get the part of the SELECT clause related to this field. Don't forget - # to set your model and associations first though. - # - # This will concatenate strings if there's more than one data source or - # multiple data values (has_many or has_and_belongs_to_many associations). - # - def to_select_sql - clause = @columns.collect { |column| - column_with_prefix(column) - }.join(', ') - - clause = adapter.concatenate(clause) if concat_ws? - clause = adapter.group_concatenate(clause) if is_many? - - "#{adapter.cast_to_string clause } AS #{quote_column(unique_name)}" - end - - # Get the part of the GROUP BY clause related to this field - if one is - # needed. If not, all you'll get back is nil. The latter will happen if - # there's multiple data values (read: a has_many or has_and_belongs_to_many - # association). - # - def to_group_sql - case - when is_many?, ThinkingSphinx.use_group_by_shortcut? - nil - else - @columns.collect { |column| - column_with_prefix(column) - } - end - end - - # Returns the unique name of the field - which is either the alias of - # the field, or the name of the only column - if there is only one. If - # there isn't, there should be an alias. Else things probably won't work. - # Consider yourself warned. - # - def unique_name - if @columns.length == 1 - @alias || @columns.first.__name - else - @alias - end - end - - def to_facet - return nil unless @faceted - - ThinkingSphinx::Facet.new(self) - end - - private - - def adapter - @adapter ||= @model.sphinx_database_adapter - end - - def quote_column(column) - @model.connection.quote_column_name(column) - end - - # Indication of whether the columns should be concatenated with a space - # between each value. True if there's either multiple sources or multiple - # associations. - # - def concat_ws? - @columns.length > 1 || multiple_associations? - end - - # Checks whether any column requires multiple associations (which only - # happens for polymorphic situations). - # - def multiple_associations? - associations.any? { |col,assocs| assocs.length > 1 } - end - - # Builds a column reference tied to the appropriate associations. This - # dives into the associations hash and their corresponding joins to - # figure out how to correctly reference a column in SQL. - # - def column_with_prefix(column) - if column.is_string? - column.__name - elsif associations[column].empty? - "#{@model.quoted_table_name}.#{quote_column(column.__name)}" - else - associations[column].collect { |assoc| - assoc.has_column?(column.__name) ? - "#{@model.connection.quote_table_name(assoc.join.aliased_table_name)}" + - ".#{quote_column(column.__name)}" : - nil - }.compact.join(', ') - end - end - - # Could there be more than one value related to the parent record? If so, - # then this will return true. If not, false. It's that simple. - # - def is_many? - associations.values.flatten.any? { |assoc| assoc.is_many? } - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index.rb deleted file mode 100644 index 30cfe38..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index.rb +++ /dev/null @@ -1,423 +0,0 @@ -require 'thinking_sphinx/index/builder' -require 'thinking_sphinx/index/faux_column' - -module ThinkingSphinx - # The Index class is a ruby representation of a Sphinx source (not a Sphinx - # index - yes, I know it's a little confusing. You'll manage). This is - # another 'internal' Thinking Sphinx class - if you're using it directly, - # you either know what you're doing, or messing with things beyond your ken. - # Enjoy. - # - class Index - attr_accessor :model, :fields, :attributes, :conditions, :groupings, - :delta_object, :options - - # Create a new index instance by passing in the model it is tied to, and - # a block to build it with (optional but recommended). For documentation - # on the syntax for inside the block, the Builder class is what you want. - # - # Quick Example: - # - # Index.new(User) do - # indexes login, email - # - # has created_at - # - # set_property :delta => true - # end - # - def initialize(model, &block) - @model = model - @associations = {} - @fields = [] - @attributes = [] - @conditions = [] - @groupings = [] - @options = {} - @delta_object = nil - - initialize_from_builder(&block) if block_given? - end - - def name - self.class.name(@model) - end - - def self.name(model) - model.name.underscore.tr(':/\\', '_') - end - - def to_riddle_for_core(offset, index) - add_internal_attributes_and_facets - link! - - source = Riddle::Configuration::SQLSource.new( - "#{name}_core_#{index}", adapter.sphinx_identifier - ) - - set_source_database_settings source - set_source_attributes source, offset - set_source_sql source, offset - set_source_settings source - - source - end - - def to_riddle_for_delta(offset, index) - add_internal_attributes_and_facets - link! - - source = Riddle::Configuration::SQLSource.new( - "#{name}_delta_#{index}", adapter.sphinx_identifier - ) - source.parent = "#{name}_core_#{index}" - - set_source_database_settings source - set_source_attributes source, offset - set_source_sql source, offset, true - - source - end - - # Link all the fields and associations to their corresponding - # associations and joins. This _must_ be called before interrogating - # the index's fields and associations for anything that may reference - # their SQL structure. - # - def link! - base = ::ActiveRecord::Associations::ClassMethods::JoinDependency.new( - @model, [], nil - ) - - @fields.each { |field| - field.model ||= @model - field.columns.each { |col| - field.associations[col] = associations(col.__stack.clone) - field.associations[col].each { |assoc| assoc.join_to(base) } - } - } - - @attributes.each { |attribute| - attribute.model ||= @model - attribute.columns.each { |col| - attribute.associations[col] = associations(col.__stack.clone) - attribute.associations[col].each { |assoc| assoc.join_to(base) } - } - } - end - - # Flag to indicate whether this index has a corresponding delta index. - # - def delta? - !@delta_object.nil? - end - - def adapter - @adapter ||= @model.sphinx_database_adapter - end - - def prefix_fields - @fields.select { |field| field.prefixes } - end - - def infix_fields - @fields.select { |field| field.infixes } - end - - def index_options - all_index_options = ThinkingSphinx::Configuration.instance.index_options.clone - @options.keys.select { |key| - ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) - }.each { |key| all_index_options[key.to_sym] = @options[key] } - all_index_options - end - - def quote_column(column) - @model.connection.quote_column_name(column) - end - - private - - def utf8? - self.index_options[:charset_type] == "utf-8" - end - - # Does all the magic with the block provided to the base #initialize. - # Creates a new class subclassed from Builder, and evaluates the block - # on it, then pulls all relevant settings - fields, attributes, conditions, - # properties - into the new index. - # - # Also creates a CRC attribute for the model. - # - def initialize_from_builder(&block) - builder = Class.new(Builder) - builder.setup - - builder.instance_eval &block - - unless @model.descends_from_active_record? - stored_class = @model.store_full_sti_class ? @model.name : @model.name.demodulize - builder.where("#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)} = '#{stored_class}'") - end - - set_model = Proc.new { |item| item.model = @model } - - @fields = builder.fields &set_model - @attributes = builder.attributes.each &set_model - @conditions = builder.conditions - @groupings = builder.groupings - @delta_object = ThinkingSphinx::Deltas.parse self, builder.properties - @options = builder.properties - - is_faceted = Proc.new { |item| item.faceted } - add_facet = Proc.new { |item| @model.sphinx_facets << item.to_facet } - - @model.sphinx_facets ||= [] - @fields.select( &is_faceted).each &add_facet - @attributes.select(&is_faceted).each &add_facet - - # We want to make sure that if the database doesn't exist, then Thinking - # Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop - # and db:migrate). It's a bit hacky, but I can't think of a better way. - rescue StandardError => err - case err.class.name - when "Mysql::Error", "Java::JavaSql::SQLException", "ActiveRecord::StatementInvalid" - return - else - raise err - end - end - - # Returns all associations used amongst all the fields and attributes. - # This includes all associations between the model and what the actual - # columns are from. - # - def all_associations - @all_associations ||= ( - # field associations - @fields.collect { |field| - field.associations.values - }.flatten + - # attribute associations - @attributes.collect { |attrib| - attrib.associations.values if attrib.include_as_association? - }.compact.flatten - ).uniq.collect { |assoc| - # get ancestors as well as column-level associations - assoc.ancestors - }.flatten.uniq - end - - # Gets a stack of associations for a specific path. - # - def associations(path, parent = nil) - assocs = [] - - if parent.nil? - assocs = association(path.shift) - else - assocs = parent.children(path.shift) - end - - until path.empty? - point = path.shift - assocs = assocs.collect { |assoc| - assoc.children(point) - }.flatten - end - - assocs - end - - # Gets the association stack for a specific key. - # - def association(key) - @associations[key] ||= Association.children(@model, key) - end - - def crc_column - if @model.column_names.include?(@model.inheritance_column) - adapter.cast_to_unsigned(adapter.convert_nulls( - adapter.crc(adapter.quote_with_table(@model.inheritance_column), true), - @model.to_crc32 - )) - else - @model.to_crc32.to_s - end - end - - def add_internal_attributes_and_facets - add_internal_attribute :sphinx_internal_id, :integer, @model.primary_key.to_sym - add_internal_attribute :class_crc, :integer, crc_column, true - add_internal_attribute :subclass_crcs, :multi, subclasses_to_s - add_internal_attribute :sphinx_deleted, :integer, "0" - - add_internal_facet :class_crc - end - - def add_internal_attribute(name, type, contents, facet = false) - return unless attribute_by_alias(name).nil? - - @attributes << Attribute.new( - FauxColumn.new(contents), - :type => type, - :as => name, - :facet => facet - ) - end - - def add_internal_facet(name) - return unless facet_by_alias(name).nil? - - @model.sphinx_facets << ClassFacet.new(attribute_by_alias(name)) - end - - def attribute_by_alias(attr_alias) - @attributes.detect { |attrib| attrib.alias == attr_alias } - end - - def facet_by_alias(name) - @model.sphinx_facets.detect { |facet| facet.name == name } - end - - def subclasses_to_s - "'" + (@model.send(:subclasses).collect { |klass| - klass.to_crc32.to_s - } << @model.to_crc32.to_s).join(",") + "'" - end - - def set_source_database_settings(source) - config = @model.connection.instance_variable_get(:@config) - - source.sql_host = config[:host] || "localhost" - source.sql_user = config[:username] || config[:user] || "" - source.sql_pass = (config[:password].to_s || "").gsub('#', '\#') - source.sql_db = config[:database] - source.sql_port = config[:port] - source.sql_sock = config[:socket] - end - - def set_source_attributes(source, offset = nil) - attributes.each do |attrib| - source.send(attrib.type_to_config) << attrib.config_value(offset) - end - end - - def set_source_sql(source, offset, delta = false) - source.sql_query = to_sql(:offset => offset, :delta => delta).gsub(/\n/, ' ') - source.sql_query_range = to_sql_query_range(:delta => delta) - source.sql_query_info = to_sql_query_info(offset) - - source.sql_query_pre += send(!delta ? :sql_query_pre_for_core : :sql_query_pre_for_delta) - - if @options[:group_concat_max_len] - source.sql_query_pre << "SET SESSION group_concat_max_len = #{@options[:group_concat_max_len]}" - end - - source.sql_query_pre += [adapter.utf8_query_pre].compact if utf8? - end - - def set_source_settings(source) - ThinkingSphinx::Configuration.instance.source_options.each do |key, value| - source.send("#{key}=".to_sym, value) - end - - @options.each do |key, value| - source.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::SourceOptions.include?(key.to_s) && !value.nil? - end - end - - def sql_query_pre_for_core - if self.delta? && !@delta_object.reset_query(@model).blank? - [@delta_object.reset_query(@model)] - else - [] - end - end - - def sql_query_pre_for_delta - [""] - end - - # Generates the big SQL statement to get the data back for all the fields - # and attributes, using all the relevant association joins. If you want - # the version filtered for delta values, send through :delta => true in the - # options. Won't do much though if the index isn't set up to support a - # delta sibling. - # - # Examples: - # - # index.to_sql - # index.to_sql(:delta => true) - # - def to_sql(options={}) - assocs = all_associations - - where_clause = "" - if self.delta? && !@delta_object.clause(@model, options[:delta]).blank? - where_clause << " AND #{@delta_object.clause(@model, options[:delta])}" - end - unless @conditions.empty? - where_clause << " AND " << @conditions.join(" AND ") - end - - internal_groupings = [] - if @model.column_names.include?(@model.inheritance_column) - internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}" - end - - unique_id_expr = ThinkingSphinx.unique_id_expression(options[:offset]) - - sql = <<-SQL -SELECT #{ ( - ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] + - @fields.collect { |field| field.to_select_sql } + - @attributes.collect { |attribute| attribute.to_select_sql } -).compact.join(", ") } -FROM #{ @model.table_name } - #{ assocs.collect { |assoc| assoc.to_sql }.join(' ') } -WHERE #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} >= $start - AND #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} <= $end - #{ where_clause } -GROUP BY #{ ( - ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] + - @fields.collect { |field| field.to_group_sql }.compact + - @attributes.collect { |attribute| attribute.to_group_sql }.compact + - @groupings + internal_groupings -).join(", ") } - SQL - - sql += " ORDER BY NULL" if adapter.sphinx_identifier == "mysql" - sql - end - - # Simple helper method for the query info SQL - which is a statement that - # returns the single row for a corresponding id. - # - def to_sql_query_info(offset) - "SELECT * FROM #{@model.quoted_table_name} WHERE " + - " #{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})" - end - - # Simple helper method for the query range SQL - which is a statement that - # returns minimum and maximum id values. These can be filtered by delta - - # so pass in :delta => true to get the delta version of the SQL. - # - def to_sql_query_range(options={}) - min_statement = adapter.convert_nulls( - "MIN(#{quote_column(@model.primary_key)})", 1 - ) - max_statement = adapter.convert_nulls( - "MAX(#{quote_column(@model.primary_key)})", 1 - ) - - sql = "SELECT #{min_statement}, #{max_statement} " + - "FROM #{@model.quoted_table_name} " - if self.delta? && !@delta_object.clause(@model, options[:delta]).blank? - sql << "WHERE #{@delta_object.clause(@model, options[:delta])}" - end - - sql - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index/builder.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index/builder.rb deleted file mode 100644 index dbd2ba0..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index/builder.rb +++ /dev/null @@ -1,264 +0,0 @@ -module ThinkingSphinx - class Index - # The Builder class is the core for the index definition block processing. - # There are four methods you really need to pay attention to: - # - indexes (aliased to includes and attribute) - # - has (aliased to attribute) - # - where - # - set_property (aliased to set_properties) - # - # The first two of these methods allow you to define what data makes up - # your indexes. #where provides a method to add manual SQL conditions, and - # set_property allows you to set some settings on a per-index basis. Check - # out each method's documentation for better ideas of usage. - # - class Builder - class << self - # No idea where this is coming from - haven't found it in any ruby or - # rails documentation. It's not needed though, so it gets undef'd. - # Hopefully the list of methods that get in the way doesn't get too - # long. - HiddenMethods = [:parent, :name, :id, :type].each { |method| - define_method(method) { - caller.grep(/irb.completion/).empty? ? method_missing(method) : super - } - } - - attr_accessor :fields, :attributes, :properties, :conditions, - :groupings - - # Set up all the collections. Consider this the equivalent of an - # instance's initialize method. - # - def setup - @fields = [] - @attributes = [] - @properties = {} - @conditions = [] - @groupings = [] - end - - # This is how you add fields - the strings Sphinx looks at - to your - # index. Technically, to use this method, you need to pass in some - # columns and options - but there's some neat method_missing stuff - # happening, so lets stick to the expected syntax within a define_index - # block. - # - # Expected options are :as, which points to a column alias in symbol - # form, and :sortable, which indicates whether you want to sort by this - # field. - # - # Adding Single-Column Fields: - # - # You can use symbols or methods - and can chain methods together to - # get access down the associations tree. - # - # indexes :id, :as => :my_id - # indexes :name, :sortable => true - # indexes first_name, last_name, :sortable => true - # indexes users.posts.content, :as => :post_content - # indexes users(:id), :as => :user_ids - # - # Keep in mind that if any keywords for Ruby methods - such as id or - # name - clash with your column names, you need to use the symbol - # version (see the first, second and last examples above). - # - # If you specify multiple columns (example #2), a field will be created - # for each. Don't use the :as option in this case. If you want to merge - # those columns together, continue reading. - # - # Adding Multi-Column Fields: - # - # indexes [first_name, last_name], :as => :name - # indexes [location, parent.location], :as => :location - # - # To combine multiple columns into a single field, you need to wrap - # them in an Array, as shown by the above examples. There's no - # limitations on whether they're symbols or methods or what level of - # associations they come from. - # - # Adding SQL Fragment Fields - # - # You can also define a field using an SQL fragment, useful for when - # you would like to index a calculated value. - # - # indexes "age < 18", :as => :minor - # - def indexes(*args) - options = args.extract_options! - args.each do |columns| - field = Field.new(FauxColumn.coerce(columns), options) - fields << field - - add_sort_attribute field, options if field.sortable - add_facet_attribute field, options if field.faceted - end - end - alias_method :field, :indexes - alias_method :includes, :indexes - - # This is the method to add attributes to your index (hence why it is - # aliased as 'attribute'). The syntax is the same as #indexes, so use - # that as starting point, but keep in mind the following points. - # - # An attribute can have an alias (the :as option), but it is always - # sortable - so you don't need to explicitly request that. You _can_ - # specify the data type of the attribute (the :type option), but the - # code's pretty good at figuring that out itself from peering into the - # database. - # - # Attributes are limited to the following types: integers, floats, - # datetimes (converted to timestamps), booleans and strings. Don't - # forget that Sphinx converts string attributes to integers, which are - # useful for sorting, but that's about it. - # - # You can also have a collection of integers for multi-value attributes - # (MVAs). Generally these would be through a has_many relationship, - # like in this example: - # - # has posts(:id), :as => :post_ids - # - # This allows you to filter on any of the values tied to a specific - # record. Might be best to read through the Sphinx documentation to get - # a better idea of that though. - # - # Adding SQL Fragment Attributes - # - # You can also define an attribute using an SQL fragment, useful for - # when you would like to index a calculated value. Don't forget to set - # the type of the attribute though: - # - # has "age < 18", :as => :minor, :type => :boolean - # - # If you're creating attributes for latitude and longitude, don't - # forget that Sphinx expects these values to be in radians. - # - def has(*args) - options = args.extract_options! - args.each do |columns| - attribute = Attribute.new(FauxColumn.coerce(columns), options) - attributes << attribute - - add_facet_attribute attribute, options if attribute.faceted - end - end - alias_method :attribute, :has - - def facet(*args) - options = args.extract_options! - options[:facet] = true - - args.each do |columns| - attribute = Attribute.new(FauxColumn.coerce(columns), options) - attributes << attribute - - add_facet_attribute attribute, options - end - end - - # Use this method to add some manual SQL conditions for your index - # request. You can pass in as many strings as you like, they'll get - # joined together with ANDs later on. - # - # where "user_id = 10" - # where "parent_type = 'Article'", "created_at < NOW()" - # - def where(*args) - @conditions += args - end - - # Use this method to add some manual SQL strings to the GROUP BY - # clause. You can pass in as many strings as you'd like, they'll get - # joined together with commas later on. - # - # group_by "lat", "lng" - # - def group_by(*args) - @groupings += args - end - - # This is what to use to set properties on the index. Chief amongst - # those is the delta property - to allow automatic updates to your - # indexes as new models are added and edited - but also you can - # define search-related properties which will be the defaults for all - # searches on the model. - # - # set_property :delta => true - # set_property :field_weights => {"name" => 100} - # set_property :order => "name ASC" - # set_property :include => :picture - # set_property :select => 'name' - # - # Also, the following two properties are particularly relevant for - # geo-location searching - latitude_attr and longitude_attr. If your - # attributes for these two values are named something other than - # lat/latitude or lon/long/longitude, you can dictate what they are - # when defining the index, so you don't need to specify them for every - # geo-related search. - # - # set_property :latitude_attr => "lt", :longitude_attr => "lg" - # - # Please don't forget to add a boolean field named 'delta' to your - # model's database table if enabling the delta index for it. - # Valid options for the delta property are: - # - # true - # false - # :default - # :delayed - # :datetime - # - # You can also extend ThinkingSphinx::Deltas::DefaultDelta to implement - # your own handling for delta indexing. - - def set_property(*args) - options = args.extract_options! - if options.empty? - @properties[args[0]] = args[1] - else - @properties.merge!(options) - end - end - alias_method :set_properties, :set_property - - # Handles the generation of new columns for the field and attribute - # definitions. - # - def method_missing(method, *args) - FauxColumn.new(method, *args) - end - - # A method to allow adding fields from associations which have names - # that clash with method names in the Builder class (ie: properties, - # fields, attributes). - # - # Example: indexes assoc(:properties).column - # - def assoc(assoc, *args) - FauxColumn.new(assoc, *args) - end - - private - - def add_sort_attribute(field, options) - add_internal_attribute field, options, "_sort" - end - - def add_facet_attribute(resource, options) - add_internal_attribute resource, options, "_facet", true - end - - def add_internal_attribute(resource, options, suffix, crc = false) - @attributes << Attribute.new( - resource.columns.collect { |col| col.clone }, - options.merge( - :type => resource.is_a?(Field) ? :string : nil, - :as => resource.unique_name.to_s.concat(suffix).to_sym, - :crc => crc - ).except(:facet) - ) - end - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index/faux_column.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index/faux_column.rb deleted file mode 100644 index 84068de..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/index/faux_column.rb +++ /dev/null @@ -1,110 +0,0 @@ -module ThinkingSphinx - class Index - # Instances of this class represent database columns and the stack of - # associations that lead from the base model to them. - # - # The name and stack are accessible through methods starting with __ to - # avoid conflicting with the method_missing calls that build the stack. - # - class FauxColumn - # Create a new column with a pre-defined stack. The top element in the - # stack will get shifted to be the name value. - # - def initialize(*stack) - @name = stack.pop - @stack = stack - end - - def self.coerce(columns) - case columns - when Symbol, String - FauxColumn.new(columns) - when Array - columns.collect { |col| FauxColumn.coerce(col) } - when FauxColumn - columns - else - nil - end - end - - # Can't use normal method name, as that could be an association or - # column name. - # - def __name - @name - end - - # Can't use normal method name, as that could be an association or - # column name. - # - def __stack - @stack - end - - # Returns true if the stack is empty *and* if the name is a string - - # which is an indication that of raw SQL, as opposed to a value from a - # table's column. - # - def is_string? - @name.is_a?(String) && @stack.empty? - end - - # This handles any 'invalid' method calls and sets them as the name, - # and pushing the previous name into the stack. The object returns - # itself. - # - # If there's a single argument, it becomes the name, and the method - # symbol goes into the stack as well. Multiple arguments means new - # columns with the original stack and new names (from each argument) gets - # returned. - # - # Easier to explain with examples: - # - # col = FauxColumn.new :a, :b, :c - # col.__name #=> :c - # col.__stack #=> [:a, :b] - # - # col.whatever #=> col - # col.__name #=> :whatever - # col.__stack #=> [:a, :b, :c] - # - # col.something(:id) #=> col - # col.__name #=> :id - # col.__stack #=> [:a, :b, :c, :whatever, :something] - # - # cols = col.short(:x, :y, :z) - # cols[0].__name #=> :x - # cols[0].__stack #=> [:a, :b, :c, :whatever, :something, :short] - # cols[1].__name #=> :y - # cols[1].__stack #=> [:a, :b, :c, :whatever, :something, :short] - # cols[2].__name #=> :z - # cols[2].__stack #=> [:a, :b, :c, :whatever, :something, :short] - # - # Also, this allows method chaining to build up a relevant stack: - # - # col = FauxColumn.new :a, :b - # col.__name #=> :b - # col.__stack #=> [:a] - # - # col.one.two.three #=> col - # col.__name #=> :three - # col.__stack #=> [:a, :b, :one, :two] - # - def method_missing(method, *args) - @stack << @name - @name = method - - if (args.empty?) - self - elsif (args.length == 1) - method_missing(args.first) - else - args.collect { |arg| - FauxColumn.new(@stack + [@name, arg]) - } - end - end - end - end -end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/rails_additions.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/rails_additions.rb deleted file mode 100644 index c7db0a1..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/rails_additions.rb +++ /dev/null @@ -1,136 +0,0 @@ -module ThinkingSphinx - module HashExcept - # Returns a new hash without the given keys. - def except(*keys) - rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys) - reject { |key,| rejected.include?(key) } - end - - # Replaces the hash without only the given keys. - def except!(*keys) - replace(except(*keys)) - end - end -end - -Hash.send( - :include, ThinkingSphinx::HashExcept -) unless Hash.instance_methods.include?("except") - -module ThinkingSphinx - module ArrayExtractOptions - def extract_options! - last.is_a?(::Hash) ? pop : {} - end - end -end - -Array.send( - :include, ThinkingSphinx::ArrayExtractOptions -) unless Array.instance_methods.include?("extract_options!") - -module ThinkingSphinx - module AbstractQuotedTableName - def quote_table_name(name) - quote_column_name(name) - end - end -end - -ActiveRecord::ConnectionAdapters::AbstractAdapter.send( - :include, ThinkingSphinx::AbstractQuotedTableName -) unless ActiveRecord::ConnectionAdapters::AbstractAdapter.instance_methods.include?("quote_table_name") - -module ThinkingSphinx - module MysqlQuotedTableName - def quote_table_name(name) #:nodoc: - quote_column_name(name).gsub('.', '`.`') - end - end -end - -if ActiveRecord::ConnectionAdapters.constants.include?("MysqlAdapter") or ActiveRecord::Base.respond_to?(:jdbcmysql_connection) - adapter = ActiveRecord::ConnectionAdapters.const_get( - defined?(JRUBY_VERSION) ? :JdbcAdapter : :MysqlAdapter - ) - unless adapter.instance_methods.include?("quote_table_name") - adapter.send(:include, ThinkingSphinx::MysqlQuotedTableName) - end -end - -module ThinkingSphinx - module ActiveRecordQuotedName - def quoted_table_name - self.connection.quote_table_name(self.table_name) - end - end -end - -ActiveRecord::Base.extend( - ThinkingSphinx::ActiveRecordQuotedName -) unless ActiveRecord::Base.respond_to?("quoted_table_name") - -module ThinkingSphinx - module ActiveRecordStoreFullSTIClass - def store_full_sti_class - false - end - end -end - -ActiveRecord::Base.extend( - ThinkingSphinx::ActiveRecordStoreFullSTIClass -) unless ActiveRecord::Base.respond_to?(:store_full_sti_class) - -module ThinkingSphinx - module ClassAttributeMethods - def cattr_reader(*syms) - syms.flatten.each do |sym| - next if sym.is_a?(Hash) - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} - @@#{sym} = nil - end - - def self.#{sym} - @@#{sym} - end - - def #{sym} - @@#{sym} - end - EOS - end - end - - def cattr_writer(*syms) - options = syms.extract_options! - syms.flatten.each do |sym| - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} - @@#{sym} = nil - end - - def self.#{sym}=(obj) - @@#{sym} = obj - end - - #{" - def #{sym}=(obj) - @@#{sym} = obj - end - " unless options[:instance_writer] == false } - EOS - end - end - - def cattr_accessor(*syms) - cattr_reader(*syms) - cattr_writer(*syms) - end - end -end - -Class.extend( - ThinkingSphinx::ClassAttributeMethods -) unless Class.respond_to?(:cattr_reader) diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/search.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/search.rb deleted file mode 100644 index d476787..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/search.rb +++ /dev/null @@ -1,780 +0,0 @@ -module ThinkingSphinx - # Once you've got those indexes in and built, this is the stuff that - # matters - how to search! This class provides a generic search - # interface - which you can use to search all your indexed models at once. - # Most times, you will just want a specific model's results - to search and - # search_for_ids methods will do the job in exactly the same manner when - # called from a model. - # - class Search - GlobalFacetOptions = { - :all_attributes => false, - :class_facet => true - } - - class << self - # Searches for results that match the parameters provided. Will only - # return the ids for the matching objects. See #search for syntax - # examples. - # - # Note that this only searches the Sphinx index, with no ActiveRecord - # queries. Thus, if your index is not in sync with the database, this - # method may return ids that no longer exist there. - # - def search_for_ids(*args) - results, client = search_results(*args.clone) - - options = args.extract_options! - page = options[:page] ? options[:page].to_i : 1 - - ThinkingSphinx::Collection.ids_from_results(results, page, client.limit, options) - end - - # Searches through the Sphinx indexes for relevant matches. There's - # various ways to search, sort, group and filter - which are covered - # below. - # - # Also, if you have WillPaginate installed, the search method can be used - # just like paginate. The same parameters - :page and :per_page - work as - # expected, and the returned result set can be used by the will_paginate - # helper. - # - # == Basic Searching - # - # The simplest way of searching is straight text. - # - # ThinkingSphinx::Search.search "pat" - # ThinkingSphinx::Search.search "google" - # User.search "pat", :page => (params[:page] || 1) - # Article.search "relevant news issue of the day" - # - # If you specify :include, like in an #find call, this will be respected - # when loading the relevant models from the search results. - # - # User.search "pat", :include => :posts - # - # == Match Modes - # - # Sphinx supports 5 different matching modes. By default Thinking Sphinx - # uses :all, which unsurprisingly requires all the supplied search terms - # to match a result. - # - # Alternative modes include: - # - # User.search "pat allan", :match_mode => :any - # User.search "pat allan", :match_mode => :phrase - # User.search "pat | allan", :match_mode => :boolean - # User.search "@name pat | @username pat", :match_mode => :extended - # - # Any will find results with any of the search terms. Phrase treats the search - # terms a single phrase instead of individual words. Boolean and extended allow - # for more complex query syntax, refer to the sphinx documentation for further - # details. - # - # == Weighting - # - # Sphinx has support for weighting, where matches in one field can be considered - # more important than in another. Weights are integers, with 1 as the default. - # They can be set per-search like this: - # - # User.search "pat allan", :field_weights => { :alias => 4, :aka => 2 } - # - # If you're searching multiple models, you can set per-index weights: - # - # ThinkingSphinx::Search.search "pat", :index_weights => { User => 10 } - # - # See http://sphinxsearch.com/doc.html#weighting for further details. - # - # == Searching by Fields - # - # If you want to step it up a level, you can limit your search terms to - # specific fields: - # - # User.search :conditions => {:name => "pat"} - # - # This uses Sphinx's extended match mode, unless you specify a different - # match mode explicitly (but then this way of searching won't work). Also - # note that you don't need to put in a search string. - # - # == Searching by Attributes - # - # Also known as filters, you can limit your searches to documents that - # have specific values for their attributes. There are three ways to do - # this. The first two techniques work in all scenarios - using the :with - # or :with_all options. - # - # ThinkingSphinx::Search.search :with => {:tag_ids => 10} - # ThinkingSphinx::Search.search :with => {:tag_ids => [10,12]} - # ThinkingSphinx::Search.search :with_all => {:tag_ids => [10,12]} - # - # The first :with search will match records with a tag_id attribute of 10. - # The second :with will match records with a tag_id attribute of 10 OR 12. - # If you need to find records that are tagged with ids 10 AND 12, you - # will need to use the :with_all search parameter. This is particuarly - # useful in conjunction with Multi Value Attributes (MVAs). - # - # The third filtering technique is only viable if you're searching with a - # specific model (not multi-model searching). With a single model, - # Thinking Sphinx can figure out what attributes and fields are available, - # so you can put it all in the :conditions hash, and it will sort it out. - # - # Node.search :conditions => {:parent_id => 10} - # - # Filters can be single values, arrays of values, or ranges. - # - # Article.search "East Timor", :conditions => {:rating => 3..5} - # - # == Excluding by Attributes - # - # Sphinx also supports negative filtering - where the filters are of - # attribute values to exclude. This is done with the :without option: - # - # User.search :without => {:role_id => 1} - # - # == Excluding by Primary Key - # - # There is a shortcut to exclude records by their ActiveRecord primary key: - # - # User.search :without_ids => 1 - # - # Pass an array or a single value. - # - # The primary key must be an integer as a negative filter is used. Note - # that for multi-model search, an id may occur in more than one model. - # - # == Infix (Star) Searching - # - # By default, Sphinx uses English stemming, e.g. matching "shoes" if you - # search for "shoe". It won't find "Melbourne" if you search for - # "elbourn", though. - # - # Enable infix searching by something like this in config/sphinx.yml: - # - # development: - # enable_star: 1 - # min_infix_length: 2 - # - # Note that this will make indexing take longer. - # - # With those settings (and after reindexing), wildcard asterisks can be used - # in queries: - # - # Location.search "*elbourn*" - # - # To automatically add asterisks around every token (but not operators), - # pass the :star option: - # - # Location.search "elbourn -ustrali", :star => true, :match_mode => :boolean - # - # This would become "*elbourn* -*ustrali*". The :star option only adds the - # asterisks. You need to make the config/sphinx.yml changes yourself. - # - # By default, the tokens are assumed to match the regular expression /\w+/u. - # If you've modified the charset_table, pass another regular expression, e.g. - # - # User.search("oo@bar.c", :star => /[\w@.]+/u) - # - # to search for "*oo@bar.c*" and not "*oo*@*bar*.*c*". - # - # == Sorting - # - # Sphinx can only sort by attributes, so generally you will need to avoid - # using field names in your :order option. However, if you're searching - # on a single model, and have specified some fields as sortable, you can - # use those field names and Thinking Sphinx will interpret accordingly. - # Remember: this will only happen for single-model searches, and only - # through the :order option. - # - # Location.search "Melbourne", :order => :state - # User.search :conditions => {:role_id => 2}, :order => "name ASC" - # - # Keep in mind that if you use a string, you *must* specify the direction - # (ASC or DESC) else Sphinx won't return any results. If you use a symbol - # then Thinking Sphinx assumes ASC, but if you wish to state otherwise, - # use the :sort_mode option: - # - # Location.search "Melbourne", :order => :state, :sort_mode => :desc - # - # Of course, there are other sort modes - check out the Sphinx - # documentation[http://sphinxsearch.com/doc.html] for that level of - # detail though. - # - # If desired, you can sort by a column in your model instead of a sphinx - # field or attribute. This sort only applies to the current page, so is - # most useful when performing a search with a single page of results. - # - # User.search("pat", :sql_order => "name") - # - # == Grouping - # - # For this you can use the group_by, group_clause and group_function - # options - which are all directly linked to Sphinx's expectations. No - # magic from Thinking Sphinx. It can get a little tricky, so make sure - # you read all the relevant - # documentation[http://sphinxsearch.com/doc.html#clustering] first. - # - # Grouping is done via three parameters within the options hash - # * :group_function determines the way grouping is done - # * :group_by determines the field which is used for grouping - # * :group_clause determines the sorting order - # - # === group_function - # - # Valid values for :group_function are - # * :day, :week, :month, :year - Grouping is done by the respective timeframes. - # * :attr, :attrpair - Grouping is done by the specified attributes(s) - # - # === group_by - # - # This parameter denotes the field by which grouping is done. Note that the - # specified field must be a sphinx attribute or index. - # - # === group_clause - # - # This determines the sorting order of the groups. In a grouping search, - # the matches within a group will sorted by the :sort_mode and :order parameters. - # The group matches themselves however, will be sorted by :group_clause. - # - # The syntax for this is the same as an order parameter in extended sort mode. - # Namely, you can specify an SQL-like sort expression with up to 5 attributes - # (including internal attributes), eg: "@relevance DESC, price ASC, @id DESC" - # - # === Grouping by timestamp - # - # Timestamp grouping groups off items by the day, week, month or year of the - # attribute given. In order to do this you need to define a timestamp attribute, - # which pretty much looks like the standard defintion for any attribute. - # - # define_index do - # # - # # All your other stuff - # # - # has :created_at - # end - # - # When you need to fire off your search, it'll go something to the tune of - # - # Fruit.search "apricot", :group_function => :day, :group_by => 'created_at' - # - # The @groupby special attribute will contain the date for that group. - # Depending on the :group_function parameter, the date format will be - # - # * :day - YYYYMMDD - # * :week - YYYYNNN (NNN is the first day of the week in question, - # counting from the start of the year ) - # * :month - YYYYMM - # * :year - YYYY - # - # - # === Grouping by attribute - # - # The syntax is the same as grouping by timestamp, except for the fact that the - # :group_function parameter is changed - # - # Fruit.search "apricot", :group_function => :attr, :group_by => 'size' - # - # - # == Geo/Location Searching - # - # Sphinx - and therefore Thinking Sphinx - has the facility to search - # around a geographical point, using a given latitude and longitude. To - # take advantage of this, you will need to have both of those values in - # attributes. To search with that point, you can then use one of the - # following syntax examples: - # - # Address.search "Melbourne", :geo => [1.4, -2.217], :order => "@geodist asc" - # Address.search "Australia", :geo => [-0.55, 3.108], :order => "@geodist asc" - # :latitude_attr => "latit", :longitude_attr => "longit" - # - # The first example applies when your latitude and longitude attributes - # are named any of lat, latitude, lon, long or longitude. If that's not - # the case, you will need to explicitly state them in your search, _or_ - # you can do so in your model: - # - # define_index do - # has :latit # Float column, stored in radians - # has :longit # Float column, stored in radians - # - # set_property :latitude_attr => "latit" - # set_property :longitude_attr => "longit" - # end - # - # Now, geo-location searching really only has an affect if you have a - # filter, sort or grouping clause related to it - otherwise it's just a - # normal search, and _will not_ return a distance value otherwise. To - # make use of the positioning difference, use the special attribute - # "@geodist" in any of your filters or sorting or grouping clauses. - # - # And don't forget - both the latitude and longitude you use in your - # search, and the values in your indexes, need to be stored as a float in radians, - # _not_ degrees. Keep in mind that if you do this conversion in SQL - # you will need to explicitly declare a column type of :float. - # - # define_index do - # has 'RADIANS(lat)', :as => :lat, :type => :float - # # ... - # end - # - # Once you've got your results set, you can access the distances as - # follows: - # - # @results.each_with_geodist do |result, distance| - # # ... - # end - # - # The distance value is returned as a float, representing the distance in - # metres. - # - # == Handling a Stale Index - # - # Especially if you don't use delta indexing, you risk having records in the - # Sphinx index that are no longer in the database. By default, those will simply - # come back as nils: - # - # >> pat_user.delete - # >> User.search("pat") - # Sphinx Result: [1,2] - # => [nil, <#User id: 2>] - # - # (If you search across multiple models, you'll get ActiveRecord::RecordNotFound.) - # - # You can simply Array#compact these results or handle the nils in some other way, but - # Sphinx will still report two results, and the missing records may upset your layout. - # - # If you pass :retry_stale => true to a single-model search, missing records will - # cause Thinking Sphinx to retry the query but excluding those records. Since search - # is paginated, the new search could potentially include missing records as well, so by - # default Thinking Sphinx will retry three times. Pass :retry_stale => 5 to retry five - # times, and so on. If there are still missing ids on the last retry, they are - # shown as nils. - # - def search(*args) - query = args.clone # an array - options = query.extract_options! - - retry_search_on_stale_index(query, options) do - results, client = search_results(*(query + [options])) - - ::ActiveRecord::Base.logger.error( - "Sphinx Error: #{results[:error]}" - ) if results[:error] - - klass = options[:class] - page = options[:page] ? options[:page].to_i : 1 - - ThinkingSphinx::Collection.create_from_results(results, page, client.limit, options) - end - end - - def retry_search_on_stale_index(query, options, &block) - stale_ids = [] - stale_retries_left = case options[:retry_stale] - when true - 3 # default to three retries - when nil, false - 0 # no retries - else options[:retry_stale].to_i - end - begin - # Passing this in an option so Collection.create_from_results can see it. - # It should only raise on stale records if there are any retries left. - options[:raise_on_stale] = stale_retries_left > 0 - block.call - # If ThinkingSphinx::Collection.create_from_results found records in Sphinx but not - # in the DB and the :raise_on_stale option is set, this exception is raised. We retry - # a limited number of times, excluding the stale ids from the search. - rescue StaleIdsException => e - stale_retries_left -= 1 - - stale_ids |= e.ids # For logging - options[:without_ids] = Array(options[:without_ids]) | e.ids # Actual exclusion - - tries = stale_retries_left - ::ActiveRecord::Base.logger.debug("Sphinx Stale Ids (%s %s left): %s" % [ - tries, (tries==1 ? 'try' : 'tries'), stale_ids.join(', ') - ]) - - retry - end - end - - def count(*args) - results, client = search_results(*args.clone) - results[:total_found] || 0 - end - - # Checks if a document with the given id exists within a specific index. - # Expected parameters: - # - # - ID of the document - # - Index to check within - # - Options hash (defaults to {}) - # - # Example: - # - # ThinkingSphinx::Search.search_for_id(10, "user_core", :class => User) - # - def search_for_id(*args) - options = args.extract_options! - client = client_from_options options - - query, filters = search_conditions( - options[:class], options[:conditions] || {} - ) - client.filters += filters - client.match_mode = :extended unless query.empty? - client.id_range = args.first..args.first - - begin - return client.query(query, args[1])[:matches].length > 0 - rescue Errno::ECONNREFUSED => err - raise ThinkingSphinx::ConnectionError, "Connection to Sphinx Daemon (searchd) failed." - end - end - - # Model.facets *args - # ThinkingSphinx::Search.facets *args - # ThinkingSphinx::Search.facets *args, :all_attributes => true - # ThinkingSphinx::Search.facets *args, :class_facet => false - # - def facets(*args) - options = args.extract_options! - - if options[:class] - facets_for_model options[:class], args, options - else - facets_for_all_models args, options - end - end - - private - - # This method handles the common search functionality, and returns both - # the result hash and the client. Not super elegant, but it'll do for - # the moment. - # - def search_results(*args) - options = args.extract_options! - query = args.join(' ') - client = client_from_options options - - query = star_query(query, options[:star]) if options[:star] - - extra_query, filters = search_conditions( - options[:class], options[:conditions] || {} - ) - client.filters += filters - client.match_mode = :extended unless extra_query.empty? - query = [query, extra_query].join(' ') - query.strip! # Because "" and " " are not equivalent - - set_sort_options! client, options - - client.limit = options[:per_page].to_i if options[:per_page] - page = options[:page] ? options[:page].to_i : 1 - page = 1 if page <= 0 - client.offset = (page - 1) * client.limit - - begin - ::ActiveRecord::Base.logger.debug "Sphinx: #{query}" - results = client.query query - ::ActiveRecord::Base.logger.debug "Sphinx Result: #{results[:matches].collect{|m| m[:attributes]["sphinx_internal_id"]}.inspect}" - rescue Errno::ECONNREFUSED => err - raise ThinkingSphinx::ConnectionError, "Connection to Sphinx Daemon (searchd) failed." - end - - return results, client - end - - # Set all the appropriate settings for the client, using the provided - # options hash. - # - def client_from_options(options = {}) - config = ThinkingSphinx::Configuration.instance - client = Riddle::Client.new config.address, config.port - klass = options[:class] - index_options = klass ? klass.sphinx_index_options : {} - - # The Riddle default is per-query max_matches=1000. If we set the - # per-server max to a smaller value in sphinx.yml, we need to override - # the Riddle default or else we get search errors like - # "per-query max_matches=1000 out of bounds (per-server max_matches=200)" - if per_server_max_matches = config.configuration.searchd.max_matches - options[:max_matches] ||= per_server_max_matches - end - - # Turn :index_weights => { "foo" => 2, User => 1 } - # into :index_weights => { "foo" => 2, "user_core" => 1, "user_delta" => 1 } - if iw = options[:index_weights] - options[:index_weights] = iw.inject({}) do |hash, (index,weight)| - if index.is_a?(Class) - name = ThinkingSphinx::Index.name(index) - hash["#{name}_core"] = weight - hash["#{name}_delta"] = weight - else - hash[index] = weight - end - hash - end - end - - [ - :max_matches, :match_mode, :sort_mode, :sort_by, :id_range, - :group_by, :group_function, :group_clause, :group_distinct, :cut_off, - :retry_count, :retry_delay, :index_weights, :rank_mode, - :max_query_time, :field_weights, :filters, :anchor, :limit - ].each do |key| - client.send( - key.to_s.concat("=").to_sym, - options[key] || index_options[key] || client.send(key) - ) - end - - options[:classes] = [klass] if klass - - client.anchor = anchor_conditions(klass, options) || {} if client.anchor.empty? - - client.filters << Riddle::Client::Filter.new( - "sphinx_deleted", [0] - ) - - # class filters - client.filters << Riddle::Client::Filter.new( - "class_crc", options[:classes].collect { |k| k.to_crc32s }.flatten - ) if options[:classes] - - # normal attribute filters - client.filters += options[:with].collect { |attr,val| - Riddle::Client::Filter.new attr.to_s, filter_value(val) - } if options[:with] - - # exclusive attribute filters - client.filters += options[:without].collect { |attr,val| - Riddle::Client::Filter.new attr.to_s, filter_value(val), true - } if options[:without] - - # every-match attribute filters - client.filters += options[:with_all].collect { |attr,vals| - Array(vals).collect { |val| - Riddle::Client::Filter.new attr.to_s, filter_value(val) - } - }.flatten if options[:with_all] - - # exclusive attribute filter on primary key - client.filters += Array(options[:without_ids]).collect { |id| - Riddle::Client::Filter.new 'sphinx_internal_id', filter_value(id), true - } if options[:without_ids] - - client - end - - def star_query(query, custom_token = nil) - token = custom_token.is_a?(Regexp) ? custom_token : /\w+/u - - query.gsub(/("#{token}(.*?#{token})?"|(?![!-])#{token})/u) do - pre, proper, post = $`, $&, $' - is_operator = pre.match(%r{(\W|^)[@~/]\Z}) # E.g. "@foo", "/2", "~3", but not as part of a token - is_quote = proper.starts_with?('"') && proper.ends_with?('"') # E.g. "foo bar", with quotes - has_star = pre.ends_with?("*") || post.starts_with?("*") - if is_operator || is_quote || has_star - proper - else - "*#{proper}*" - end - end - end - - def filter_value(value) - case value - when Range - value.first.is_a?(Time) ? timestamp(value.first)..timestamp(value.last) : value - when Array - value.collect { |val| val.is_a?(Time) ? timestamp(val) : val } - else - Array(value) - end - end - - # Returns the integer timestamp for a Time object. - # - # If using Rails 2.1+, need to handle timezones to translate them back to - # UTC, as that's what datetimes will be stored as by MySQL. - # - # in_time_zone is a method that was added for the timezone support in - # Rails 2.1, which is why it's used for testing. I'm sure there's better - # ways, but this does the job. - # - def timestamp(value) - value.respond_to?(:in_time_zone) ? value.utc.to_i : value.to_i - end - - # Translate field and attribute conditions to the relevant search string - # and filters. - # - def search_conditions(klass, conditions={}) - attributes = klass ? klass.sphinx_indexes.collect { |index| - index.attributes.collect { |attrib| attrib.unique_name } - }.flatten : [] - - search_string = [] - filters = [] - - conditions.each do |key,val| - if attributes.include?(key.to_sym) - filters << Riddle::Client::Filter.new( - key.to_s, filter_value(val) - ) - else - search_string << "@#{key} #{val}" - end - end - - return search_string.join(' '), filters - end - - # Return the appropriate latitude and longitude values, depending on - # whether the relevant attributes have been defined, and also whether - # there's actually any values. - # - def anchor_conditions(klass, options) - attributes = klass ? klass.sphinx_indexes.collect { |index| - index.attributes.collect { |attrib| attrib.unique_name } - }.flatten : [] - - lat_attr = klass ? klass.sphinx_indexes.collect { |index| - index.options[:latitude_attr] - }.compact.first : nil - - lon_attr = klass ? klass.sphinx_indexes.collect { |index| - index.options[:longitude_attr] - }.compact.first : nil - - lat_attr = options[:latitude_attr] if options[:latitude_attr] - lat_attr ||= :lat if attributes.include?(:lat) - lat_attr ||= :latitude if attributes.include?(:latitude) - - lon_attr = options[:longitude_attr] if options[:longitude_attr] - lon_attr ||= :lng if attributes.include?(:lng) - lon_attr ||= :lon if attributes.include?(:lon) - lon_attr ||= :long if attributes.include?(:long) - lon_attr ||= :longitude if attributes.include?(:longitude) - - lat = options[:lat] - lon = options[:lon] - - if options[:geo] - lat = options[:geo].first - lon = options[:geo].last - end - - lat && lon ? { - :latitude_attribute => lat_attr.to_s, - :latitude => lat, - :longitude_attribute => lon_attr.to_s, - :longitude => lon - } : nil - end - - # Set the sort options using the :order key as well as the appropriate - # Riddle settings. - # - def set_sort_options!(client, options) - klass = options[:class] - fields = klass ? klass.sphinx_indexes.collect { |index| - index.fields.collect { |field| field.unique_name } - }.flatten : [] - index_options = klass ? klass.sphinx_index_options : {} - - order = options[:order] || index_options[:order] - case order - when Symbol - client.sort_mode = :attr_asc if client.sort_mode == :relevance || client.sort_mode.nil? - if fields.include?(order) - client.sort_by = order.to_s.concat("_sort") - else - client.sort_by = order.to_s - end - when String - client.sort_mode = :extended - client.sort_by = sorted_fields_to_attributes(order, fields) - else - # do nothing - end - - client.sort_mode = :attr_asc if client.sort_mode == :asc - client.sort_mode = :attr_desc if client.sort_mode == :desc - end - - # Search through a collection of fields and translate any appearances - # of them in a string to their attribute equivalent for sorting. - # - def sorted_fields_to_attributes(string, fields) - fields.each { |field| - string.gsub!(/(^|\s)#{field}(,?\s|$)/) { |match| - match.gsub field.to_s, field.to_s.concat("_sort") - } - } - - string - end - - def facets_for_model(klass, args, options) - hash = ThinkingSphinx::FacetCollection.new args + [options] - options = options.clone.merge! :group_function => :attr - - klass.sphinx_facets.inject(hash) do |hash, facet| - unless facet.name == :class && !options[:class_facet] - options[:group_by] = facet.attribute_name - hash.add_from_results facet, search(*(args + [options])) - end - - hash - end - end - - def facets_for_all_models(args, options) - options = GlobalFacetOptions.merge(options) - hash = ThinkingSphinx::FacetCollection.new args + [options] - options = options.merge! :group_function => :attr - - facet_names(options).inject(hash) do |hash, name| - options[:group_by] = name - hash.add_from_results name, search(*(args + [options])) - hash - end - end - - def facet_classes(options) - options[:classes] || ThinkingSphinx.indexed_models.collect { |model| - model.constantize - } - end - - def facet_names(options) - classes = facet_classes(options) - names = options[:all_attributes] ? - facet_names_for_all_classes(classes) : - facet_names_common_to_all_classes(classes) - - names.delete "class_crc" unless options[:class_facet] - names - end - - def facet_names_for_all_classes(classes) - classes.collect { |klass| - klass.sphinx_facets.collect { |facet| facet.attribute_name } - }.flatten.uniq - end - - def facet_names_common_to_all_classes(classes) - facet_names_for_all_classes(classes).select { |name| - classes.all? { |klass| - klass.sphinx_facets.detect { |facet| - facet.attribute_name == name - } - } - } - end - end - end -end diff --git a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/tasks.rb b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/tasks.rb deleted file mode 100644 index ab8990e..0000000 --- a/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/tasks.rb +++ /dev/null @@ -1,128 +0,0 @@ -require 'fileutils' - -namespace :thinking_sphinx do - task :app_env do - Rake::Task[:environment].invoke if defined?(RAILS_ROOT) - Rake::Task[:merb_env].invoke if defined?(Merb) - end - - desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings" - task :running_start => :app_env do - Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running? - Rake::Task["thinking_sphinx:start"].invoke - end - - desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings" - task :start => :app_env do - config = ThinkingSphinx::Configuration.instance - - FileUtils.mkdir_p config.searchd_file_path - raise RuntimeError, "searchd is already running." if sphinx_running? - - Dir["#{config.searchd_file_path}/*.spl"].each { |file| File.delete(file) } - - cmd = "#{config.bin_path}searchd --pidfile --config #{config.config_file}" - puts cmd - system cmd - - sleep(2) - - if sphinx_running? - puts "Started successfully (pid #{sphinx_pid})." - else - puts "Failed to start searchd daemon. Check #{config.searchd_log_file}." - end - end - - desc "Stop Sphinx using Thinking Sphinx's settings" - task :stop => :app_env do - raise RuntimeError, "searchd is not running." unless sphinx_running? - config = ThinkingSphinx::Configuration.instance - pid = sphinx_pid - system "#{config.bin_path}searchd --stop --config #{config.config_file}" - puts "Stopped search daemon (pid #{pid})." - end - - desc "Restart Sphinx" - task :restart => [:app_env, :stop, :start] - - desc "Generate the Sphinx configuration file using Thinking Sphinx's settings" - task :configure => :app_env do - config = ThinkingSphinx::Configuration.instance - puts "Generating Configuration to #{config.config_file}" - config.build - end - - desc "Index data for Sphinx using Thinking Sphinx's settings" - task :index => :app_env do - ThinkingSphinx::Deltas::Job.cancel_thinking_sphinx_jobs - - config = ThinkingSphinx::Configuration.instance - unless ENV["INDEX_ONLY"] == "true" - puts "Generating Configuration to #{config.config_file}" - config.build - end - - FileUtils.mkdir_p config.searchd_file_path - cmd = "#{config.bin_path}indexer --config #{config.config_file} --all" - cmd << " --rotate" if sphinx_running? - puts cmd - system cmd - end - - namespace :index do - task :delta => :app_env do - ThinkingSphinx.indexed_models.select { |model| - model.constantize.sphinx_indexes.any? { |index| index.delta? } - }.each do |model| - model.constantize.sphinx_indexes.select { |index| - index.delta? && index.delta_object.respond_to?(:delayed_index) - }.each { |index| - index.delta_object.delayed_index(index.model) - } - end - end - end - - desc "Process stored delta index requests" - task :delayed_delta => :app_env do - require 'delayed/worker' - - Delayed::Worker.new( - :min_priority => ENV['MIN_PRIORITY'], - :max_priority => ENV['MAX_PRIORITY'] - ).start - end -end - -namespace :ts do - desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings" - task :run => "thinking_sphinx:running_start" - desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings" - task :start => "thinking_sphinx:start" - desc "Stop Sphinx using Thinking Sphinx's settings" - task :stop => "thinking_sphinx:stop" - desc "Index data for Sphinx using Thinking Sphinx's settings" - task :in => "thinking_sphinx:index" - namespace :in do - desc "Index Thinking Sphinx datetime delta indexes" - task :delta => "thinking_sphinx:index:delta" - end - task :index => "thinking_sphinx:index" - desc "Restart Sphinx" - task :restart => "thinking_sphinx:restart" - desc "Generate the Sphinx configuration file using Thinking Sphinx's settings" - task :conf => "thinking_sphinx:configure" - desc "Generate the Sphinx configuration file using Thinking Sphinx's settings" - task :config => "thinking_sphinx:configure" - desc "Process stored delta index requests" - task :dd => "thinking_sphinx:delayed_delta" -end - -def sphinx_pid - ThinkingSphinx.sphinx_pid -end - -def sphinx_running? - ThinkingSphinx.sphinx_running? -end -- cgit v1.3