freelancing-god-thinking-sphinx 0.9.6 → 0.9.7
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 +23 -1
- data/lib/thinking_sphinx.rb +4 -1
- data/lib/thinking_sphinx/active_record.rb +19 -0
- data/lib/thinking_sphinx/active_record/delta.rb +4 -0
- data/lib/thinking_sphinx/association.rb +5 -5
- data/lib/thinking_sphinx/attribute.rb +5 -2
- data/lib/thinking_sphinx/configuration.rb +86 -104
- data/lib/thinking_sphinx/field.rb +17 -5
- data/lib/thinking_sphinx/index.rb +71 -2
- data/lib/thinking_sphinx/index/builder.rb +5 -5
- data/lib/thinking_sphinx/search.rb +7 -1
- data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +4 -0
- data/spec/unit/thinking_sphinx/active_record/search_spec.rb +63 -0
- data/spec/unit/thinking_sphinx/active_record_spec.rb +74 -2
- data/spec/unit/thinking_sphinx/association_spec.rb +247 -0
- data/spec/unit/thinking_sphinx/attribute_spec.rb +285 -2
- data/spec/unit/thinking_sphinx/configuration_spec.rb +431 -0
- data/spec/unit/thinking_sphinx/field_spec.rb +169 -5
- data/spec/unit/thinking_sphinx/index_spec.rb +186 -1
- data/spec/unit/thinking_sphinx_spec.rb +25 -0
- metadata +2 -2
data/README
CHANGED
@@ -4,6 +4,26 @@
|
|
4
4
|
|
5
5
|
First, if you haven't done so already, check out the main usage[http://ts.freelancing-gods.com/usage.html] page. Once you've done that, the next place to look for information is the specific method docs - ThinkingSphinx::Search and ThinkingSphinx::Index::Builder in particular.
|
6
6
|
|
7
|
+
== Contributing
|
8
|
+
|
9
|
+
Fork on GitHub and after you've committed tested patches, send a pull request.
|
10
|
+
|
11
|
+
To get the spec suite running, you will need to install the not-a-mock gem if you don't already have it:
|
12
|
+
|
13
|
+
git clone git://github.com/freelancing-god/not-a-mock.git
|
14
|
+
cd not-a-mock
|
15
|
+
rake gem
|
16
|
+
gem install pkg/not_a_mock-1.1.0.gem
|
17
|
+
|
18
|
+
Then set up your database
|
19
|
+
|
20
|
+
cp spec/fixtures/database.yml.default spec/fixtures/database.yml
|
21
|
+
mysqladmin -u root create thinking_sphinx
|
22
|
+
|
23
|
+
You should now have a passing test suite from which to build your patch on.
|
24
|
+
|
25
|
+
rake spec
|
26
|
+
|
7
27
|
== Contributors
|
8
28
|
|
9
29
|
Since I first released this library, there's been quite a few people who have submitted patches, to my immense gratitude. Others have suggested syntax changes and general improvements. So my thanks to the following people:
|
@@ -22,4 +42,6 @@ Since I first released this library, there's been quite a few people who have su
|
|
22
42
|
- Patrick Lenz
|
23
43
|
- Björn Andreasson
|
24
44
|
- James Healy
|
25
|
-
- Jae-Jun Hwang
|
45
|
+
- Jae-Jun Hwang
|
46
|
+
- Xavier Shay
|
47
|
+
- Jason Rust
|
data/lib/thinking_sphinx.rb
CHANGED
@@ -20,7 +20,7 @@ module ThinkingSphinx
|
|
20
20
|
module Version #:nodoc:
|
21
21
|
Major = 0
|
22
22
|
Minor = 9
|
23
|
-
Tiny =
|
23
|
+
Tiny = 7
|
24
24
|
|
25
25
|
String = [Major, Minor, Tiny].join('.')
|
26
26
|
end
|
@@ -67,6 +67,9 @@ module ThinkingSphinx
|
|
67
67
|
@@deltas_enabled = value
|
68
68
|
end
|
69
69
|
|
70
|
+
# Checks to see if MySQL will allow simplistic GROUP BY statements. If not,
|
71
|
+
# or if not using MySQL, this will return false.
|
72
|
+
#
|
70
73
|
def self.use_group_by_shortcut?
|
71
74
|
::ActiveRecord::ConnectionAdapters.constants.include?("MysqlAdapter") &&
|
72
75
|
::ActiveRecord::Base.connection.is_a?(
|
@@ -78,6 +78,8 @@ module ThinkingSphinx
|
|
78
78
|
after_commit :index_delta
|
79
79
|
end
|
80
80
|
|
81
|
+
after_destroy :toggle_deleted
|
82
|
+
|
81
83
|
index
|
82
84
|
end
|
83
85
|
alias_method :sphinx_index, :define_index
|
@@ -111,5 +113,22 @@ module ThinkingSphinx
|
|
111
113
|
:include, ThinkingSphinx::ActiveRecord::HasManyAssociation
|
112
114
|
)
|
113
115
|
end
|
116
|
+
|
117
|
+
def toggle_deleted
|
118
|
+
config = ThinkingSphinx::Configuration.new
|
119
|
+
client = Riddle::Client.new config.address, config.port
|
120
|
+
|
121
|
+
client.update(
|
122
|
+
"#{self.class.name.downcase}_core",
|
123
|
+
['sphinx_deleted'],
|
124
|
+
{self.id => 1}
|
125
|
+
)
|
126
|
+
|
127
|
+
client.update(
|
128
|
+
"#{self.class.name.downcase}_delta",
|
129
|
+
['sphinx_deleted'],
|
130
|
+
{self.id => 1}
|
131
|
+
) if self.class.indexes.any? { |index| index.delta? }
|
132
|
+
end
|
114
133
|
end
|
115
134
|
end
|
@@ -122,16 +122,16 @@ module ThinkingSphinx
|
|
122
122
|
options[:class_name] = klass.name
|
123
123
|
options[:foreign_key] ||= "#{ref.name}_id"
|
124
124
|
|
125
|
-
|
125
|
+
quoted_foreign_type = klass.connection.quote_column_name ref.options[:foreign_type]
|
126
126
|
case options[:conditions]
|
127
127
|
when nil
|
128
|
-
options[:conditions] = "::ts_join_alias::.#{
|
128
|
+
options[:conditions] = "::ts_join_alias::.#{quoted_foreign_type} = '#{klass.name}'"
|
129
129
|
when Array
|
130
|
-
options[:conditions] << "::ts_join_alias::.#{
|
130
|
+
options[:conditions] << "::ts_join_alias::.#{quoted_foreign_type} = '#{klass.name}'"
|
131
131
|
when Hash
|
132
|
-
options[:conditions].merge!(foreign_type => klass.name)
|
132
|
+
options[:conditions].merge!(ref.options[:foreign_type] => klass.name)
|
133
133
|
else
|
134
|
-
options[:conditions] << " AND ::ts_join_alias::.#{
|
134
|
+
options[:conditions] << " AND ::ts_join_alias::.#{quoted_foreign_type} = '#{klass.name}'"
|
135
135
|
end
|
136
136
|
|
137
137
|
options
|
@@ -57,6 +57,8 @@ module ThinkingSphinx
|
|
57
57
|
@columns = Array(columns)
|
58
58
|
@associations = {}
|
59
59
|
|
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
|
+
|
60
62
|
@alias = options[:as]
|
61
63
|
@type = options[:type]
|
62
64
|
end
|
@@ -139,7 +141,7 @@ module ThinkingSphinx
|
|
139
141
|
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
140
142
|
"CONCAT_WS('#{separator}', #{clause})"
|
141
143
|
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
142
|
-
clause.split(', ').join(" || #{separator} || ")
|
144
|
+
clause.split(', ').join(" || '#{separator}' || ")
|
143
145
|
else
|
144
146
|
clause
|
145
147
|
end
|
@@ -271,7 +273,8 @@ module ThinkingSphinx
|
|
271
273
|
klasses = @associations[col].empty? ? [@model] :
|
272
274
|
@associations[col].collect { |assoc| assoc.reflection.klass }
|
273
275
|
klasses.all? { |klass|
|
274
|
-
klass.columns.detect { |column| column.name == col.__name.to_s }
|
276
|
+
column = klass.columns.detect { |column| column.name == col.__name.to_s }
|
277
|
+
!column.nil? && column.type == :integer
|
275
278
|
}
|
276
279
|
}
|
277
280
|
end
|
@@ -49,11 +49,12 @@ module ThinkingSphinx
|
|
49
49
|
self.app_root = Merb.root if defined?(Merb)
|
50
50
|
self.app_root ||= app_root
|
51
51
|
|
52
|
-
self.config_file = "#{app_root}/config/#{environment}.sphinx.conf"
|
53
|
-
self.searchd_log_file = "#{app_root}/log/searchd.log"
|
54
|
-
self.query_log_file = "#{app_root}/log/searchd.query.log"
|
55
|
-
self.pid_file = "#{app_root}/log/searchd.#{environment}.pid"
|
56
|
-
self.searchd_file_path = "#{app_root}/db/sphinx/#{environment}"
|
52
|
+
self.config_file = "#{self.app_root}/config/#{environment}.sphinx.conf"
|
53
|
+
self.searchd_log_file = "#{self.app_root}/log/searchd.log"
|
54
|
+
self.query_log_file = "#{self.app_root}/log/searchd.query.log"
|
55
|
+
self.pid_file = "#{self.app_root}/log/searchd.#{environment}.pid"
|
56
|
+
self.searchd_file_path = "#{self.app_root}/db/sphinx/#{environment}"
|
57
|
+
self.address = "0.0.0.0"
|
57
58
|
self.port = 3312
|
58
59
|
self.allow_star = false
|
59
60
|
self.mem_limit = "64M"
|
@@ -97,6 +98,7 @@ indexer
|
|
97
98
|
|
98
99
|
searchd
|
99
100
|
{
|
101
|
+
address = #{self.address}
|
100
102
|
port = #{self.port}
|
101
103
|
log = #{self.searchd_log_file}
|
102
104
|
query_log = #{self.query_log_file}
|
@@ -110,107 +112,27 @@ searchd
|
|
110
112
|
ThinkingSphinx.indexed_models.each do |model|
|
111
113
|
model = model.constantize
|
112
114
|
sources = []
|
115
|
+
delta_sources = []
|
113
116
|
prefixed_fields = []
|
114
117
|
infixed_fields = []
|
115
118
|
|
116
119
|
model.indexes.each_with_index do |index, i|
|
117
|
-
|
118
|
-
index.link!
|
120
|
+
file.write index.to_config(i, database_conf, charset_type)
|
119
121
|
|
120
|
-
|
121
|
-
attrib.to_sphinx_clause
|
122
|
-
}.join("\n ")
|
123
|
-
|
124
|
-
adapter = case index.adapter
|
125
|
-
when :postgres
|
126
|
-
create_array_accum
|
127
|
-
"pgsql"
|
128
|
-
when :mysql
|
129
|
-
"mysql"
|
130
|
-
else
|
131
|
-
raise "Unsupported Database Adapter: Sphinx only supports MySQL and PosgreSQL"
|
132
|
-
end
|
133
|
-
|
134
|
-
file.write <<-SOURCE
|
135
|
-
|
136
|
-
source #{model.name.downcase}_#{i}_core
|
137
|
-
{
|
138
|
-
type = #{adapter}
|
139
|
-
sql_host = #{database_conf[:host] || "localhost"}
|
140
|
-
sql_user = #{database_conf[:username]}
|
141
|
-
sql_pass = #{database_conf[:password]}
|
142
|
-
sql_db = #{database_conf[:database]}
|
143
|
-
|
144
|
-
sql_query_pre = #{charset_type == "utf-8" && adapter == "mysql" ? "SET NAMES utf8" : ""}
|
145
|
-
sql_query_pre = #{index.to_sql_query_pre}
|
146
|
-
sql_query = #{index.to_sql.gsub(/\n/, ' ')}
|
147
|
-
sql_query_range = #{index.to_sql_query_range}
|
148
|
-
sql_query_info = #{index.to_sql_query_info}
|
149
|
-
#{attr_sources}
|
150
|
-
}
|
151
|
-
SOURCE
|
152
|
-
|
153
|
-
if index.delta?
|
154
|
-
file.write <<-SOURCE
|
155
|
-
|
156
|
-
source #{model.name.downcase}_#{i}_delta : #{model.name.downcase}_#{i}_core
|
157
|
-
{
|
158
|
-
sql_query_pre = #{charset_type == "utf-8" && adapter == "mysql" ? "SET NAMES utf8" : ""}
|
159
|
-
sql_query = #{index.to_sql(:delta => true).gsub(/\n/, ' ')}
|
160
|
-
sql_query_range = #{index.to_sql_query_range :delta => true}
|
161
|
-
}
|
162
|
-
SOURCE
|
163
|
-
end
|
122
|
+
create_array_accum if index.adapter == :postgres
|
164
123
|
sources << "#{model.name.downcase}_#{i}_core"
|
124
|
+
delta_sources << "#{model.name.downcase}_#{i}_delta" if index.delta?
|
165
125
|
end
|
166
126
|
|
167
127
|
source_list = sources.collect { |s| "source = #{s}" }.join("\n")
|
168
|
-
delta_list =
|
169
|
-
file.write <<-INDEX
|
170
|
-
|
171
|
-
index #{model.name.downcase}_core
|
172
|
-
{
|
173
|
-
#{source_list}
|
174
|
-
path = #{self.searchd_file_path}/#{model.name.downcase}_core
|
175
|
-
charset_type = #{self.charset_type}
|
176
|
-
INDEX
|
177
|
-
file.puts " morphology = #{self.morphology}" unless self.morphology.blank?
|
178
|
-
file.puts " charset_table = #{self.charset_table}" unless self.charset_table.nil?
|
179
|
-
file.puts " ignore_chars = #{self.ignore_chars}" unless self.ignore_chars.nil?
|
128
|
+
delta_list = delta_sources.collect { |s| "source = #{s}" }.join("\n")
|
180
129
|
|
181
|
-
|
182
|
-
|
183
|
-
file.
|
130
|
+
file.write core_index_for_model(model, source_list)
|
131
|
+
unless delta_list.blank?
|
132
|
+
file.write delta_index_for_model(model, delta_list)
|
184
133
|
end
|
185
134
|
|
186
|
-
file.write(
|
187
|
-
|
188
|
-
if model.indexes.any? { |index| index.delta? }
|
189
|
-
file.write <<-INDEX
|
190
|
-
|
191
|
-
index #{model.name.downcase}_delta : #{model.name.downcase}_core
|
192
|
-
{
|
193
|
-
#{delta_list}
|
194
|
-
path = #{self.searchd_file_path}/#{model.name.downcase}_delta
|
195
|
-
}
|
196
|
-
|
197
|
-
index #{model.name.downcase}
|
198
|
-
{
|
199
|
-
type = distributed
|
200
|
-
local = #{model.name.downcase}_core
|
201
|
-
local = #{model.name.downcase}_delta
|
202
|
-
charset_type = #{self.charset_type}
|
203
|
-
}
|
204
|
-
INDEX
|
205
|
-
else
|
206
|
-
file.write <<-INDEX
|
207
|
-
index #{model.name.downcase}
|
208
|
-
{
|
209
|
-
type = distributed
|
210
|
-
local = #{model.name.downcase}_core
|
211
|
-
}
|
212
|
-
INDEX
|
213
|
-
end
|
135
|
+
file.write distributed_index_for_model(model)
|
214
136
|
end
|
215
137
|
end
|
216
138
|
end
|
@@ -220,8 +142,9 @@ index #{model.name.downcase}
|
|
220
142
|
# messy dependencies issues).
|
221
143
|
#
|
222
144
|
def load_models
|
223
|
-
|
224
|
-
|
145
|
+
base = "#{app_root}/app/models/"
|
146
|
+
Dir["#{base}**/*.rb"].each do |file|
|
147
|
+
model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1')
|
225
148
|
|
226
149
|
next if model_name.nil?
|
227
150
|
next if ::ActiveRecord::Base.send(:subclasses).detect { |model|
|
@@ -229,8 +152,10 @@ index #{model.name.downcase}
|
|
229
152
|
}
|
230
153
|
|
231
154
|
begin
|
232
|
-
model_name.
|
233
|
-
rescue
|
155
|
+
model_name.classify.constantize
|
156
|
+
rescue LoadError
|
157
|
+
model_name.gsub(/.*[\/\\]/, '').classify.constantize
|
158
|
+
rescue NameError
|
234
159
|
next
|
235
160
|
end
|
236
161
|
end
|
@@ -252,11 +177,68 @@ index #{model.name.downcase}
|
|
252
177
|
end unless conf.nil?
|
253
178
|
end
|
254
179
|
|
180
|
+
def core_index_for_model(model, sources)
|
181
|
+
output = <<-INDEX
|
182
|
+
|
183
|
+
index #{model.name.downcase}_core
|
184
|
+
{
|
185
|
+
#{sources}
|
186
|
+
path = #{self.searchd_file_path}/#{model.name.downcase}_core
|
187
|
+
charset_type = #{self.charset_type}
|
188
|
+
INDEX
|
189
|
+
|
190
|
+
output += " morphology = #{self.morphology}\n" unless self.morphology.blank?
|
191
|
+
output += " charset_table = #{self.charset_table}\n" unless self.charset_table.nil?
|
192
|
+
output += " ignore_chars = #{self.ignore_chars}\n" unless self.ignore_chars.nil?
|
193
|
+
|
194
|
+
if self.allow_star
|
195
|
+
output += " enable_star = 1\n"
|
196
|
+
output += " min_prefix_len = 1\n"
|
197
|
+
output += " min_infix_len = 1\n"
|
198
|
+
end
|
199
|
+
|
200
|
+
unless model.indexes.collect(&:prefix_fields).flatten.empty?
|
201
|
+
output += " prefix_fields = #{model.indexes.collect(&:prefix_fields).flatten.join(', ')}\n"
|
202
|
+
end
|
203
|
+
|
204
|
+
unless model.indexes.collect(&:infix_fields).flatten.empty?
|
205
|
+
output += " infix_fields = #{model.indexes.collect(&:infix_fields).flatten.join(', ')}\n"
|
206
|
+
end
|
207
|
+
|
208
|
+
output + "}\n"
|
209
|
+
end
|
210
|
+
|
211
|
+
def delta_index_for_model(model, sources)
|
212
|
+
<<-INDEX
|
213
|
+
index #{model.name.downcase}_delta : #{model.name.downcase}_core
|
214
|
+
{
|
215
|
+
#{sources}
|
216
|
+
path = #{self.searchd_file_path}/#{model.name.downcase}_delta
|
217
|
+
}
|
218
|
+
INDEX
|
219
|
+
end
|
220
|
+
|
221
|
+
def distributed_index_for_model(model)
|
222
|
+
sources = ["local = #{model.name.downcase}_core"]
|
223
|
+
if model.indexes.any? { |index| index.delta? }
|
224
|
+
sources << "local = #{model.name.downcase}_delta"
|
225
|
+
end
|
226
|
+
|
227
|
+
<<-INDEX
|
228
|
+
index #{model.name.downcase}
|
229
|
+
{
|
230
|
+
type = distributed
|
231
|
+
#{ sources.join("\n ") }
|
232
|
+
charset_type = #{self.charset_type}
|
233
|
+
}
|
234
|
+
INDEX
|
235
|
+
end
|
236
|
+
|
255
237
|
def create_array_accum
|
256
|
-
execute "begin"
|
257
|
-
execute "savepoint ts"
|
238
|
+
::ActiveRecord::Base.connection.execute "begin"
|
239
|
+
::ActiveRecord::Base.connection.execute "savepoint ts"
|
258
240
|
begin
|
259
|
-
execute <<-SQL
|
241
|
+
::ActiveRecord::Base.connection.execute <<-SQL
|
260
242
|
CREATE AGGREGATE array_accum (anyelement)
|
261
243
|
(
|
262
244
|
sfunc = array_append,
|
@@ -266,10 +248,10 @@ index #{model.name.downcase}
|
|
266
248
|
SQL
|
267
249
|
rescue
|
268
250
|
raise unless $!.to_s =~ /already exists with same argument types/
|
269
|
-
execute "rollback to savepoint ts"
|
251
|
+
::ActiveRecord::Base.connection.execute "rollback to savepoint ts"
|
270
252
|
end
|
271
|
-
execute "release savepoint
|
272
|
-
execute "commit"
|
253
|
+
::ActiveRecord::Base.connection.execute "release savepoint ts"
|
254
|
+
::ActiveRecord::Base.connection.execute "commit"
|
273
255
|
end
|
274
256
|
end
|
275
257
|
end
|
@@ -8,7 +8,7 @@ module ThinkingSphinx
|
|
8
8
|
# associations. Which can get messy. Use Index.link!, it really helps.
|
9
9
|
#
|
10
10
|
class Field
|
11
|
-
attr_accessor :alias, :columns, :sortable, :associations, :model
|
11
|
+
attr_accessor :alias, :columns, :sortable, :associations, :model, :infixes, :prefixes
|
12
12
|
|
13
13
|
# To create a new field, you'll need to pass in either a single Column
|
14
14
|
# or an array of them, and some (optional) options. The columns are
|
@@ -17,6 +17,8 @@ module ThinkingSphinx
|
|
17
17
|
# Valid options are:
|
18
18
|
# - :as => :alias_name
|
19
19
|
# - :sortable => true
|
20
|
+
# - :infixes => true
|
21
|
+
# - :prefixes => true
|
20
22
|
#
|
21
23
|
# Alias is only required in three circumstances: when there's
|
22
24
|
# another attribute or field with the same name, when the column name is
|
@@ -27,6 +29,12 @@ module ThinkingSphinx
|
|
27
29
|
# to an integer value), which can be sorted by. Thinking Sphinx is smart
|
28
30
|
# enough to realise that when you specify fields in sort statements, you
|
29
31
|
# mean their respective attributes.
|
32
|
+
#
|
33
|
+
# If you have partial matching enabled (ie: enable_star), then you can
|
34
|
+
# specify certain fields to have their prefixes and infixes indexed. Keep
|
35
|
+
# in mind, though, that Sphinx's default is _all_ fields - so once you
|
36
|
+
# highlight a particular field, no other fields in the index will have
|
37
|
+
# these partial indexes.
|
30
38
|
#
|
31
39
|
# Here's some examples:
|
32
40
|
#
|
@@ -41,15 +49,19 @@ module ThinkingSphinx
|
|
41
49
|
#
|
42
50
|
# Field.new(
|
43
51
|
# [Column.new(:posts, :subject), Column.new(:posts, :content)],
|
44
|
-
# :as => :posts
|
52
|
+
# :as => :posts, :prefixes => true
|
45
53
|
# )
|
46
54
|
#
|
47
55
|
def initialize(columns, options = {})
|
48
56
|
@columns = Array(columns)
|
49
57
|
@associations = {}
|
58
|
+
|
59
|
+
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) }
|
50
60
|
|
51
61
|
@alias = options[:as]
|
52
62
|
@sortable = options[:sortable] || false
|
63
|
+
@infixes = options[:infixes] || false
|
64
|
+
@prefixes = options[:prefixes] || false
|
53
65
|
end
|
54
66
|
|
55
67
|
# Get the part of the SELECT clause related to this field. Don't forget
|
@@ -105,7 +117,7 @@ module ThinkingSphinx
|
|
105
117
|
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
106
118
|
"CONCAT_WS(' ', #{clause})"
|
107
119
|
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
108
|
-
clause.split(', ').join(" ||
|
120
|
+
clause.split(', ').join(" || ' ' || ")
|
109
121
|
else
|
110
122
|
clause
|
111
123
|
end
|
@@ -116,7 +128,7 @@ module ThinkingSphinx
|
|
116
128
|
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
117
129
|
"GROUP_CONCAT(#{clause} SEPARATOR ' ')"
|
118
130
|
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
119
|
-
"array_to_string(array_accum(#{clause}), '
|
131
|
+
"array_to_string(array_accum(#{clause}), ' ')"
|
120
132
|
else
|
121
133
|
clause
|
122
134
|
end
|
@@ -183,4 +195,4 @@ module ThinkingSphinx
|
|
183
195
|
associations.values.flatten.any? { |assoc| assoc.is_many? }
|
184
196
|
end
|
185
197
|
end
|
186
|
-
end
|
198
|
+
end
|