freelancing-god-thinking-sphinx 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +4 -0
- data/lib/thinking_sphinx.rb +4 -2
- data/lib/thinking_sphinx/active_record.rb +8 -10
- data/lib/thinking_sphinx/active_record/search.rb +7 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +26 -20
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +48 -4
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +117 -71
- data/lib/thinking_sphinx/attribute.rb +27 -82
- data/lib/thinking_sphinx/collection.rb +34 -19
- data/lib/thinking_sphinx/configuration.rb +0 -25
- data/lib/thinking_sphinx/core/string.rb +22 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +1 -12
- data/lib/thinking_sphinx/deltas/default_delta.rb +9 -3
- data/lib/thinking_sphinx/field.rb +18 -54
- data/lib/thinking_sphinx/index.rb +105 -145
- data/lib/thinking_sphinx/index/builder.rb +2 -2
- data/lib/thinking_sphinx/search.rb +17 -0
- data/spec/unit/thinking_sphinx/active_record_spec.rb +2 -1
- data/spec/unit/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/unit/thinking_sphinx/index_spec.rb +2 -2
- data/tasks/distribution.rb +48 -0
- data/tasks/testing.rb +86 -0
- metadata +7 -2
- data/tasks/thinking_sphinx_tasks.rake +0 -1
- data/tasks/thinking_sphinx_tasks.rb +0 -128
data/README
CHANGED
data/lib/thinking_sphinx.rb
CHANGED
|
@@ -6,11 +6,13 @@ require 'active_record'
|
|
|
6
6
|
require 'riddle'
|
|
7
7
|
require 'after_commit'
|
|
8
8
|
|
|
9
|
+
require 'thinking_sphinx/core/string'
|
|
9
10
|
require 'thinking_sphinx/active_record'
|
|
10
11
|
require 'thinking_sphinx/association'
|
|
11
12
|
require 'thinking_sphinx/attribute'
|
|
12
13
|
require 'thinking_sphinx/collection'
|
|
13
14
|
require 'thinking_sphinx/configuration'
|
|
15
|
+
require 'thinking_sphinx/facet'
|
|
14
16
|
require 'thinking_sphinx/field'
|
|
15
17
|
require 'thinking_sphinx/index'
|
|
16
18
|
require 'thinking_sphinx/rails_additions'
|
|
@@ -24,14 +26,14 @@ require 'thinking_sphinx/adapters/postgresql_adapter'
|
|
|
24
26
|
ActiveRecord::Base.send(:include, ThinkingSphinx::ActiveRecord)
|
|
25
27
|
|
|
26
28
|
Merb::Plugins.add_rakefiles(
|
|
27
|
-
File.join(File.dirname(__FILE__), "
|
|
29
|
+
File.join(File.dirname(__FILE__), "thinking_sphinx", "tasks")
|
|
28
30
|
) if defined?(Merb)
|
|
29
31
|
|
|
30
32
|
module ThinkingSphinx
|
|
31
33
|
module Version #:nodoc:
|
|
32
34
|
Major = 1
|
|
33
35
|
Minor = 1
|
|
34
|
-
Tiny =
|
|
36
|
+
Tiny = 3
|
|
35
37
|
|
|
36
38
|
String = [Major, Minor, Tiny].join('.')
|
|
37
39
|
end
|
|
@@ -10,7 +10,7 @@ module ThinkingSphinx
|
|
|
10
10
|
module ActiveRecord
|
|
11
11
|
def self.included(base)
|
|
12
12
|
base.class_eval do
|
|
13
|
-
class_inheritable_array :sphinx_indexes
|
|
13
|
+
class_inheritable_array :sphinx_indexes, :sphinx_facets
|
|
14
14
|
class << self
|
|
15
15
|
# Allows creation of indexes for Sphinx. If you don't do this, there
|
|
16
16
|
# isn't much point trying to search (or using this plugin at all,
|
|
@@ -94,14 +94,7 @@ module ThinkingSphinx
|
|
|
94
94
|
# you in some other way, awesome.
|
|
95
95
|
#
|
|
96
96
|
def to_crc32
|
|
97
|
-
|
|
98
|
-
self.name.each_byte do |byte|
|
|
99
|
-
result ^= byte
|
|
100
|
-
8.times do
|
|
101
|
-
result = (result >> 1) ^ (0xEDB88320 * (result & 1))
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
result ^ 0xFFFFFFFF
|
|
97
|
+
self.name.to_crc32
|
|
105
98
|
end
|
|
106
99
|
|
|
107
100
|
def to_crc32s
|
|
@@ -121,13 +114,18 @@ module ThinkingSphinx
|
|
|
121
114
|
end
|
|
122
115
|
|
|
123
116
|
def to_riddle(offset)
|
|
124
|
-
|
|
117
|
+
sphinx_database_adapter.setup
|
|
125
118
|
|
|
126
119
|
indexes = [to_riddle_for_core(offset)]
|
|
127
120
|
indexes << to_riddle_for_delta(offset) if sphinx_delta?
|
|
128
121
|
indexes << to_riddle_for_distributed
|
|
129
122
|
end
|
|
130
123
|
|
|
124
|
+
def sphinx_database_adapter
|
|
125
|
+
@sphinx_database_adapter ||=
|
|
126
|
+
ThinkingSphinx::AbstractAdapter.detect(self)
|
|
127
|
+
end
|
|
128
|
+
|
|
131
129
|
private
|
|
132
130
|
|
|
133
131
|
def sphinx_name
|
|
@@ -42,6 +42,13 @@ module ThinkingSphinx
|
|
|
42
42
|
args << options
|
|
43
43
|
ThinkingSphinx::Search.search_for_id(*args)
|
|
44
44
|
end
|
|
45
|
+
|
|
46
|
+
def facets(*args)
|
|
47
|
+
options = args.extract_options!
|
|
48
|
+
options[:class] = self
|
|
49
|
+
args << options
|
|
50
|
+
ThinkingSphinx::Search.facets(*args)
|
|
51
|
+
end
|
|
45
52
|
end
|
|
46
53
|
end
|
|
47
54
|
end
|
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
module ThinkingSphinx
|
|
2
2
|
class AbstractAdapter
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
|
12
|
-
ThinkingSphinx::MysqlAdapter
|
|
13
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
|
14
|
-
ThinkingSphinx::PostgreSQLAdapter
|
|
15
|
-
else
|
|
16
|
-
raise "Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
protected
|
|
3
|
+
def initialize(model)
|
|
4
|
+
@model = model
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def setup
|
|
8
|
+
# Deliberately blank - subclasses should do something though. Well, if
|
|
9
|
+
# they need to.
|
|
10
|
+
end
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
def self.detect(model)
|
|
13
|
+
case model.connection.class.name
|
|
14
|
+
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
|
15
|
+
ThinkingSphinx::MysqlAdapter.new model
|
|
16
|
+
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
|
17
|
+
ThinkingSphinx::PostgreSQLAdapter.new model
|
|
18
|
+
else
|
|
19
|
+
raise "Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL"
|
|
24
20
|
end
|
|
25
21
|
end
|
|
22
|
+
|
|
23
|
+
def quote_with_table(column)
|
|
24
|
+
"#{@model.quoted_table_name}.#{@model.connection.quote_column_name(column)}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
def connection
|
|
30
|
+
@connection ||= @model.connection
|
|
31
|
+
end
|
|
26
32
|
end
|
|
27
33
|
end
|
|
@@ -1,9 +1,53 @@
|
|
|
1
1
|
module ThinkingSphinx
|
|
2
2
|
class MysqlAdapter < AbstractAdapter
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
def setup
|
|
4
|
+
# Does MySQL actually need to do anything?
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def sphinx_identifier
|
|
8
|
+
"mysql"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def concatenate(clause, separator = ' ')
|
|
12
|
+
"CONCAT_WS('#{separator}', #{clause})"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def group_concatenate(clause, separator = ' ')
|
|
16
|
+
"GROUP_CONCAT(#{clause} SEPARATOR '#{separator}')"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def cast_to_string(clause)
|
|
20
|
+
"CAST(#{clause} AS CHAR)"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def cast_to_datetime(clause)
|
|
24
|
+
"UNIX_TIMESTAMP(#{clause})"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def cast_to_unsigned(clause)
|
|
28
|
+
"CAST(#{clause} AS UNSIGNED)"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def convert_nulls(clause, default = '')
|
|
32
|
+
default = "'#{default}'" if default.is_a?(String)
|
|
33
|
+
|
|
34
|
+
"IFNULL(#{clause}, #{default})"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def boolean(value)
|
|
38
|
+
value ? 1 : 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def crc(clause)
|
|
42
|
+
"CRC32(#{clause})"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def utf8_query_pre
|
|
46
|
+
"SET NAMES utf8"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def time_difference(diff)
|
|
50
|
+
"DATE_SUB(NOW(), INTERVAL #{diff} SECOND)"
|
|
7
51
|
end
|
|
8
52
|
end
|
|
9
53
|
end
|
|
@@ -1,83 +1,129 @@
|
|
|
1
1
|
module ThinkingSphinx
|
|
2
2
|
class PostgreSQLAdapter < AbstractAdapter
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
def setup
|
|
4
|
+
create_array_accum_function
|
|
5
|
+
create_crc32_function
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def sphinx_identifier
|
|
9
|
+
"pgsql"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def concatenate(clause, separator = ' ')
|
|
13
|
+
clause.split(', ').collect { |field|
|
|
14
|
+
"COALESCE(#{field}, '')"
|
|
15
|
+
}.join(" || '#{separator}' || ")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def group_concatenate(clause, separator = ' ')
|
|
19
|
+
"array_to_string(array_accum(#{clause}), '#{separator}')"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def cast_to_string(clause)
|
|
23
|
+
clause
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def cast_to_datetime(clause)
|
|
27
|
+
"cast(extract(epoch from #{clause}) as int)"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def cast_to_unsigned(clause)
|
|
31
|
+
clause
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def convert_nulls(clause, default = '')
|
|
35
|
+
default = "'#{default}'" if default.is_a?(String)
|
|
10
36
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
"COALESCE(#{clause}, #{default})"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def boolean(value)
|
|
41
|
+
value ? 'TRUE' : 'FALSE'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def crc(clause)
|
|
45
|
+
"crc32(#{clause})"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def utf8_query_pre
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def time_difference(diff)
|
|
53
|
+
"current_timestamp - interval '#{diff} seconds'"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def execute(command, output_error = false)
|
|
59
|
+
connection.execute "begin"
|
|
60
|
+
connection.execute "savepoint ts"
|
|
61
|
+
begin
|
|
62
|
+
connection.execute command
|
|
63
|
+
rescue StandardError => err
|
|
64
|
+
puts err if output_error
|
|
65
|
+
connection.execute "rollback to savepoint ts"
|
|
22
66
|
end
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
connection.execute "release savepoint ts"
|
|
68
|
+
connection.execute "commit"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def create_array_accum_function
|
|
72
|
+
if connection.raw_connection.server_version > 80200
|
|
73
|
+
execute <<-SQL
|
|
74
|
+
CREATE AGGREGATE array_accum (anyelement)
|
|
75
|
+
(
|
|
76
|
+
sfunc = array_append,
|
|
77
|
+
stype = anyarray,
|
|
78
|
+
initcond = '{}'
|
|
79
|
+
);
|
|
80
|
+
SQL
|
|
81
|
+
else
|
|
82
|
+
execute <<-SQL
|
|
83
|
+
CREATE AGGREGATE array_accum
|
|
84
|
+
(
|
|
85
|
+
basetype = anyelement,
|
|
86
|
+
sfunc = array_append,
|
|
87
|
+
stype = anyarray,
|
|
88
|
+
initcond = '{}'
|
|
89
|
+
);
|
|
90
|
+
SQL
|
|
45
91
|
end
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def create_crc32_function
|
|
95
|
+
execute "CREATE LANGUAGE 'plpgsql';"
|
|
96
|
+
function = <<-SQL
|
|
97
|
+
CREATE OR REPLACE FUNCTION crc32(word text)
|
|
98
|
+
RETURNS bigint AS $$
|
|
99
|
+
DECLARE tmp bigint;
|
|
100
|
+
DECLARE i int;
|
|
101
|
+
DECLARE j int;
|
|
102
|
+
DECLARE word_array bytea;
|
|
103
|
+
BEGIN
|
|
104
|
+
i = 0;
|
|
105
|
+
tmp = 4294967295;
|
|
106
|
+
word_array = decode(replace(word, E'\\\\', E'\\\\\\\\'), 'escape');
|
|
107
|
+
LOOP
|
|
108
|
+
tmp = (tmp # get_byte(word_array, i))::bigint;
|
|
109
|
+
i = i + 1;
|
|
110
|
+
j = 0;
|
|
60
111
|
LOOP
|
|
61
|
-
tmp = (tmp #
|
|
62
|
-
|
|
63
|
-
j
|
|
64
|
-
LOOP
|
|
65
|
-
tmp = ((tmp >> 1) # (3988292384 * (tmp & 1)))::bigint;
|
|
66
|
-
j = j + 1;
|
|
67
|
-
IF j >= 8 THEN
|
|
68
|
-
EXIT;
|
|
69
|
-
END IF;
|
|
70
|
-
END LOOP;
|
|
71
|
-
IF i >= char_length(word) THEN
|
|
112
|
+
tmp = ((tmp >> 1) # (3988292384 * (tmp & 1)))::bigint;
|
|
113
|
+
j = j + 1;
|
|
114
|
+
IF j >= 8 THEN
|
|
72
115
|
EXIT;
|
|
73
116
|
END IF;
|
|
74
117
|
END LOOP;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
118
|
+
IF i >= char_length(word) THEN
|
|
119
|
+
EXIT;
|
|
120
|
+
END IF;
|
|
121
|
+
END LOOP;
|
|
122
|
+
return (tmp # 4294967295);
|
|
123
|
+
END
|
|
124
|
+
$$ IMMUTABLE STRICT LANGUAGE plpgsql;
|
|
125
|
+
SQL
|
|
126
|
+
execute function, true
|
|
81
127
|
end
|
|
82
128
|
end
|
|
83
129
|
end
|
|
@@ -9,7 +9,7 @@ module ThinkingSphinx
|
|
|
9
9
|
# associations. Which can get messy. Use Index.link!, it really helps.
|
|
10
10
|
#
|
|
11
11
|
class Attribute
|
|
12
|
-
attr_accessor :alias, :columns, :associations, :model
|
|
12
|
+
attr_accessor :alias, :columns, :associations, :model, :faceted
|
|
13
13
|
|
|
14
14
|
# To create a new attribute, you'll need to pass in either a single Column
|
|
15
15
|
# or an array of them, and some (optional) options.
|
|
@@ -59,8 +59,9 @@ module ThinkingSphinx
|
|
|
59
59
|
|
|
60
60
|
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) }
|
|
61
61
|
|
|
62
|
-
@alias
|
|
63
|
-
@type
|
|
62
|
+
@alias = options[:as]
|
|
63
|
+
@type = options[:type]
|
|
64
|
+
@faceted = options[:facet]
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
# Get the part of the SELECT clause related to this attribute. Don't forget
|
|
@@ -76,10 +77,10 @@ module ThinkingSphinx
|
|
|
76
77
|
|
|
77
78
|
separator = all_ints? ? ',' : ' '
|
|
78
79
|
|
|
79
|
-
clause = concatenate(clause, separator) if concat_ws?
|
|
80
|
-
clause = group_concatenate(clause, separator) if is_many?
|
|
81
|
-
clause = cast_to_datetime(clause) if type == :datetime
|
|
82
|
-
clause = convert_nulls(clause) if type == :string
|
|
80
|
+
clause = adapter.concatenate(clause, separator) if concat_ws?
|
|
81
|
+
clause = adapter.group_concatenate(clause, separator) if is_many?
|
|
82
|
+
clause = adapter.cast_to_datetime(clause) if type == :datetime
|
|
83
|
+
clause = adapter.convert_nulls(clause) if type == :string
|
|
83
84
|
|
|
84
85
|
"#{clause} AS #{quote_column(unique_name)}"
|
|
85
86
|
end
|
|
@@ -133,63 +134,31 @@ module ThinkingSphinx
|
|
|
133
134
|
end
|
|
134
135
|
end
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
when
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
else
|
|
147
|
-
clause
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def group_concatenate(clause, separator = ' ')
|
|
152
|
-
case @model.connection.class.name
|
|
153
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
|
154
|
-
"GROUP_CONCAT(#{clause} SEPARATOR '#{separator}')"
|
|
155
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
|
156
|
-
"array_to_string(array_accum(#{clause}), '#{separator}')"
|
|
137
|
+
# Returns the type of the column. If that's not already set, it returns
|
|
138
|
+
# :multi if there's the possibility of more than one value, :string if
|
|
139
|
+
# there's more than one association, otherwise it figures out what the
|
|
140
|
+
# actual column's datatype is and returns that.
|
|
141
|
+
def type
|
|
142
|
+
@type ||= case
|
|
143
|
+
when is_many?
|
|
144
|
+
:multi
|
|
145
|
+
when @associations.values.flatten.length > 1
|
|
146
|
+
:string
|
|
157
147
|
else
|
|
158
|
-
|
|
148
|
+
translated_type_from_database
|
|
159
149
|
end
|
|
160
150
|
end
|
|
161
151
|
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
|
167
|
-
clause
|
|
168
|
-
else
|
|
169
|
-
clause
|
|
170
|
-
end
|
|
152
|
+
def to_facet
|
|
153
|
+
return nil unless @faceted
|
|
154
|
+
|
|
155
|
+
ThinkingSphinx::Facet.new(unique_name, @columns, self)
|
|
171
156
|
end
|
|
172
157
|
|
|
173
|
-
|
|
174
|
-
case @model.connection.class.name
|
|
175
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
|
176
|
-
"UNIX_TIMESTAMP(#{clause})"
|
|
177
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
|
178
|
-
"cast(extract(epoch from #{clause}) as int)"
|
|
179
|
-
else
|
|
180
|
-
clause
|
|
181
|
-
end
|
|
182
|
-
end
|
|
158
|
+
private
|
|
183
159
|
|
|
184
|
-
def
|
|
185
|
-
|
|
186
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
|
187
|
-
"IFNULL(#{clause}, '')"
|
|
188
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
|
189
|
-
"COALESCE(#{clause}, '')"
|
|
190
|
-
else
|
|
191
|
-
clause
|
|
192
|
-
end
|
|
160
|
+
def adapter
|
|
161
|
+
@adapter ||= @model.sphinx_database_adapter
|
|
193
162
|
end
|
|
194
163
|
|
|
195
164
|
def quote_column(column)
|
|
@@ -203,16 +172,7 @@ module ThinkingSphinx
|
|
|
203
172
|
def concat_ws?
|
|
204
173
|
multiple_associations? || @columns.length > 1
|
|
205
174
|
end
|
|
206
|
-
|
|
207
|
-
# Checks the association tree for each column - if they're all the same,
|
|
208
|
-
# returns false.
|
|
209
|
-
#
|
|
210
|
-
def multiple_sources?
|
|
211
|
-
first = associations[@columns.first]
|
|
212
|
-
|
|
213
|
-
!@columns.all? { |col| associations[col] == first }
|
|
214
|
-
end
|
|
215
|
-
|
|
175
|
+
|
|
216
176
|
# Checks whether any column requires multiple associations (which only
|
|
217
177
|
# happens for polymorphic situations).
|
|
218
178
|
#
|
|
@@ -252,21 +212,6 @@ module ThinkingSphinx
|
|
|
252
212
|
columns.all? { |col| col.is_string? }
|
|
253
213
|
end
|
|
254
214
|
|
|
255
|
-
# Returns the type of the column. If that's not already set, it returns
|
|
256
|
-
# :multi if there's the possibility of more than one value, :string if
|
|
257
|
-
# there's more than one association, otherwise it figures out what the
|
|
258
|
-
# actual column's datatype is and returns that.
|
|
259
|
-
def type
|
|
260
|
-
@type ||= case
|
|
261
|
-
when is_many?
|
|
262
|
-
:multi
|
|
263
|
-
when @associations.values.flatten.length > 1
|
|
264
|
-
:string
|
|
265
|
-
else
|
|
266
|
-
translated_type_from_database
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
|
|
270
215
|
def all_ints?
|
|
271
216
|
@columns.all? { |col|
|
|
272
217
|
klasses = @associations[col].empty? ? [@model] :
|