From 4bd16f053847f2efe347ebda9136ef2233ee0d2c Mon Sep 17 00:00:00 2001 From: hukl Date: Tue, 28 Apr 2009 00:15:53 +0200 Subject: added thinking_sphinx plugin for fulltext search on nodes and heads --- .../thinking_sphinx/adapters/abstract_adapter.rb | 42 +++++++ .../lib/thinking_sphinx/adapters/mysql_adapter.rb | 54 +++++++++ .../thinking_sphinx/adapters/postgresql_adapter.rb | 130 +++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/abstract_adapter.rb create mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/mysql_adapter.rb create mode 100644 vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/postgresql_adapter.rb (limited to 'vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters') 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 new file mode 100644 index 0000000..b68b75e --- /dev/null +++ b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/abstract_adapter.rb @@ -0,0 +1,42 @@ +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 new file mode 100644 index 0000000..597d4b6 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/mysql_adapter.rb @@ -0,0 +1,54 @@ +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 new file mode 100644 index 0000000..625971d --- /dev/null +++ b/vendor/plugins/thinking-sphinx/lib/thinking_sphinx/adapters/postgresql_adapter.rb @@ -0,0 +1,130 @@ +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 -- cgit v1.3