thinking-sphinx 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +4 -7
- data/HISTORY +27 -0
- data/README.textile +38 -218
- data/gemfiles/rails_3_2.gemfile +2 -3
- data/gemfiles/rails_4_0.gemfile +2 -3
- data/gemfiles/rails_4_1.gemfile +2 -3
- data/lib/thinking_sphinx.rb +1 -0
- data/lib/thinking_sphinx/active_record.rb +1 -0
- data/lib/thinking_sphinx/active_record/association_proxy.rb +1 -0
- data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +5 -10
- data/lib/thinking_sphinx/active_record/association_proxy/attribute_matcher.rb +38 -0
- data/lib/thinking_sphinx/active_record/attribute/type.rb +19 -8
- data/lib/thinking_sphinx/active_record/base.rb +3 -1
- data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +1 -1
- data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +4 -1
- data/lib/thinking_sphinx/active_record/index.rb +4 -4
- data/lib/thinking_sphinx/active_record/property_query.rb +57 -27
- data/lib/thinking_sphinx/active_record/simple_many_query.rb +35 -0
- data/lib/thinking_sphinx/capistrano/v3.rb +11 -10
- data/lib/thinking_sphinx/configuration.rb +23 -6
- data/lib/thinking_sphinx/connection.rb +8 -9
- data/lib/thinking_sphinx/errors.rb +7 -2
- data/lib/thinking_sphinx/facet.rb +2 -2
- data/lib/thinking_sphinx/facet_search.rb +4 -2
- data/lib/thinking_sphinx/logger.rb +7 -0
- data/lib/thinking_sphinx/masks/group_enumerators_mask.rb +4 -4
- data/lib/thinking_sphinx/middlewares/inquirer.rb +2 -2
- data/lib/thinking_sphinx/middlewares/sphinxql.rb +6 -2
- data/lib/thinking_sphinx/middlewares/stale_id_filter.rb +1 -1
- data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +6 -1
- data/lib/thinking_sphinx/real_time/property.rb +2 -1
- data/lib/thinking_sphinx/real_time/transcriber.rb +7 -3
- data/lib/thinking_sphinx/search.rb +14 -4
- data/lib/thinking_sphinx/search/context.rb +0 -6
- data/lib/thinking_sphinx/test.rb +11 -2
- data/lib/thinking_sphinx/wildcard.rb +7 -1
- data/spec/acceptance/association_scoping_spec.rb +55 -15
- data/spec/acceptance/geosearching_spec.rb +8 -2
- data/spec/acceptance/real_time_updates_spec.rb +9 -0
- data/spec/acceptance/specifying_sql_spec.rb +31 -17
- data/spec/internal/app/indices/car_index.rb +5 -0
- data/spec/internal/app/models/car.rb +5 -0
- data/spec/internal/app/models/category.rb +2 -1
- data/spec/internal/app/models/manufacturer.rb +3 -0
- data/spec/internal/db/schema.rb +9 -0
- data/spec/thinking_sphinx/active_record/attribute/type_spec.rb +7 -0
- data/spec/thinking_sphinx/active_record/base_spec.rb +17 -0
- data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +2 -1
- data/spec/thinking_sphinx/configuration_spec.rb +40 -2
- data/spec/thinking_sphinx/errors_spec.rb +21 -0
- data/spec/thinking_sphinx/facet_search_spec.rb +6 -6
- data/spec/thinking_sphinx/middlewares/inquirer_spec.rb +0 -4
- data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +25 -0
- data/spec/thinking_sphinx/middlewares/stale_id_filter_spec.rb +2 -2
- data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +2 -1
- data/spec/thinking_sphinx/search_spec.rb +56 -0
- data/spec/thinking_sphinx/wildcard_spec.rb +5 -0
- data/thinking-sphinx.gemspec +2 -2
- metadata +40 -32
- data/sketchpad.rb +0 -58
- data/spec/internal/log/.gitignore +0 -1
@@ -175,7 +175,7 @@ describe 'separate queries for MVAs' do
|
|
175
175
|
declaration, query = attribute.split(/;\s+/)
|
176
176
|
|
177
177
|
declaration.should == 'uint tag_ids from query'
|
178
|
-
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .taggings.\..tag_id. AS .tag_ids. FROM .taggings.\s
|
178
|
+
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .taggings.\..tag_id. AS .tag_ids. FROM .taggings.\s? WHERE \(.taggings.\..article_id. IS NOT NULL\)$/)
|
179
179
|
end
|
180
180
|
|
181
181
|
it "generates a SQL query with joins when appropriate for MVAs" do
|
@@ -191,7 +191,7 @@ describe 'separate queries for MVAs' do
|
|
191
191
|
declaration, query = attribute.split(/;\s+/)
|
192
192
|
|
193
193
|
declaration.should == 'uint tag_ids from query'
|
194
|
-
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..id. AS .tag_ids. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id
|
194
|
+
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..id. AS .tag_ids. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id. WHERE \(.taggings.\..article_id. IS NOT NULL\)\s?$/)
|
195
195
|
end
|
196
196
|
|
197
197
|
it "respects has_many :through joins for MVA queries" do
|
@@ -207,7 +207,7 @@ describe 'separate queries for MVAs' do
|
|
207
207
|
declaration, query = attribute.split(/;\s+/)
|
208
208
|
|
209
209
|
declaration.should == 'uint tag_ids from query'
|
210
|
-
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..id. AS .tag_ids. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id
|
210
|
+
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..id. AS .tag_ids. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id. WHERE \(.taggings.\..article_id. IS NOT NULL\)\s?$/)
|
211
211
|
end
|
212
212
|
|
213
213
|
it "can handle multiple joins for MVA queries" do
|
@@ -225,15 +225,10 @@ describe 'separate queries for MVAs' do
|
|
225
225
|
declaration, query = attribute.split(/;\s+/)
|
226
226
|
|
227
227
|
declaration.should == 'uint tag_ids from query'
|
228
|
-
query.should match(/^SELECT .articles.\..user_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..id. AS .tag_ids. FROM .articles. INNER JOIN .taggings. ON .taggings.\..article_id. = .articles.\..id. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id
|
228
|
+
query.should match(/^SELECT .articles.\..user_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..id. AS .tag_ids. FROM .articles. INNER JOIN .taggings. ON .taggings.\..article_id. = .articles.\..id. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id. WHERE \(.articles.\..user_id. IS NOT NULL\)\s?$/)
|
229
229
|
end
|
230
230
|
|
231
|
-
it "can handle HABTM joins for MVA queries" do
|
232
|
-
pending "Efficient HABTM queries are tricky."
|
233
|
-
# We don't really have any need for other tables, but that doesn't lend
|
234
|
-
# itself nicely to Thinking Sphinx's DSL, nor ARel SQL generation. This is
|
235
|
-
# a low priority - manual SQL queries for this situation may work better.
|
236
|
-
|
231
|
+
it "can handle simple HABTM joins for MVA queries" do
|
237
232
|
index = ThinkingSphinx::ActiveRecord::Index.new(:book)
|
238
233
|
index.definition_block = Proc.new {
|
239
234
|
indexes title
|
@@ -248,7 +243,7 @@ describe 'separate queries for MVAs' do
|
|
248
243
|
declaration, query = attribute.split(/;\s+/)
|
249
244
|
|
250
245
|
declaration.should == 'uint genre_ids from query'
|
251
|
-
query.should match(/^SELECT .books_genres.\..book_id. \* #{count} \+ #{source.offset} AS .id., .
|
246
|
+
query.should match(/^SELECT .books_genres.\..book_id. \* #{count} \+ #{source.offset} AS .id., .books_genres.\..genre_id. AS .genre_ids. FROM .books_genres.\s?$/)
|
252
247
|
end
|
253
248
|
|
254
249
|
it "generates an appropriate range SQL queries for an MVA" do
|
@@ -264,7 +259,7 @@ describe 'separate queries for MVAs' do
|
|
264
259
|
declaration, query, range = attribute.split(/;\s+/)
|
265
260
|
|
266
261
|
declaration.should == 'uint tag_ids from ranged-query'
|
267
|
-
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .taggings.\..tag_id. AS .tag_ids. FROM .taggings. \s?WHERE \(.taggings.\..article_id. BETWEEN \$start AND \$end\)$/)
|
262
|
+
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .taggings.\..tag_id. AS .tag_ids. FROM .taggings. \s?WHERE \(.taggings.\..article_id. BETWEEN \$start AND \$end\) AND \(.taggings.\..article_id. IS NOT NULL\)$/)
|
268
263
|
range.should match(/^SELECT MIN\(.taggings.\..article_id.\), MAX\(.taggings.\..article_id.\) FROM .taggings.\s?$/)
|
269
264
|
end
|
270
265
|
|
@@ -281,10 +276,29 @@ describe 'separate queries for MVAs' do
|
|
281
276
|
declaration, query, range = attribute.split(/;\s+/)
|
282
277
|
|
283
278
|
declaration.should == 'uint tag_ids from ranged-query'
|
284
|
-
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..id. AS .tag_ids. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id. \s?WHERE \(.taggings.\..article_id. BETWEEN \$start AND \$end\)$/)
|
279
|
+
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..id. AS .tag_ids. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id. \s?WHERE \(.taggings.\..article_id. BETWEEN \$start AND \$end\) AND \(.taggings.\..article_id. IS NOT NULL\)$/)
|
285
280
|
range.should match(/^SELECT MIN\(.taggings.\..article_id.\), MAX\(.taggings.\..article_id.\) FROM .taggings.\s?$/)
|
286
281
|
end
|
287
282
|
|
283
|
+
it "can handle ranged queries for simple HABTM joins for MVA queries" do
|
284
|
+
index = ThinkingSphinx::ActiveRecord::Index.new(:book)
|
285
|
+
index.definition_block = Proc.new {
|
286
|
+
indexes title
|
287
|
+
has genres.id, :as => :genre_ids, :source => :ranged_query
|
288
|
+
}
|
289
|
+
index.render
|
290
|
+
source = index.sources.first
|
291
|
+
|
292
|
+
attribute = source.sql_attr_multi.detect { |attribute|
|
293
|
+
attribute[/genre_ids/]
|
294
|
+
}
|
295
|
+
declaration, query, range = attribute.split(/;\s+/)
|
296
|
+
|
297
|
+
declaration.should == 'uint genre_ids from ranged-query'
|
298
|
+
query.should match(/^SELECT .books_genres.\..book_id. \* #{count} \+ #{source.offset} AS .id., .books_genres.\..genre_id. AS .genre_ids. FROM .books_genres. WHERE \(.books_genres.\..book_id. BETWEEN \$start AND \$end\)$/)
|
299
|
+
range.should match(/^SELECT MIN\(.books_genres.\..book_id.\), MAX\(.books_genres.\..book_id.\) FROM .books_genres.$/)
|
300
|
+
end
|
301
|
+
|
288
302
|
it "respects custom SQL snippets as the query value" do
|
289
303
|
index.definition_block = Proc.new {
|
290
304
|
indexes title
|
@@ -355,7 +369,7 @@ describe 'separate queries for field' do
|
|
355
369
|
declaration, query = field.split(/;\s+/)
|
356
370
|
|
357
371
|
declaration.should == 'tags from query'
|
358
|
-
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..name. AS .tags. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id.\s? ORDER BY .taggings.\..article_id. ASC\s?$/)
|
372
|
+
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..name. AS .tags. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id.\s? WHERE \(.taggings.\..article_id. IS NOT NULL\)\s? ORDER BY .taggings.\..article_id. ASC\s?$/)
|
359
373
|
end
|
360
374
|
|
361
375
|
it "respects has_many :through joins for MVF queries" do
|
@@ -368,7 +382,7 @@ describe 'separate queries for field' do
|
|
368
382
|
declaration, query = field.split(/;\s+/)
|
369
383
|
|
370
384
|
declaration.should == 'tags from query'
|
371
|
-
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..name. AS .tags. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id.\s? ORDER BY .taggings.\..article_id. ASC\s?$/)
|
385
|
+
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..name. AS .tags. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id.\s? WHERE \(.taggings.\..article_id. IS NOT NULL\)\s? ORDER BY .taggings.\..article_id. ASC\s?$/)
|
372
386
|
end
|
373
387
|
|
374
388
|
it "can handle multiple joins for MVF queries" do
|
@@ -383,7 +397,7 @@ describe 'separate queries for field' do
|
|
383
397
|
declaration, query = field.split(/;\s+/)
|
384
398
|
|
385
399
|
declaration.should == 'tags from query'
|
386
|
-
query.should match(/^SELECT .articles.\..user_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..name. AS .tags. FROM .articles. INNER JOIN .taggings. ON .taggings.\..article_id. = .articles.\..id. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id.\s? ORDER BY .articles.\..user_id. ASC\s?$/)
|
400
|
+
query.should match(/^SELECT .articles.\..user_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..name. AS .tags. FROM .articles. INNER JOIN .taggings. ON .taggings.\..article_id. = .articles.\..id. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id.\s? WHERE \(.articles.\..user_id. IS NOT NULL\)\s? ORDER BY .articles.\..user_id. ASC\s?$/)
|
387
401
|
end
|
388
402
|
|
389
403
|
it "generates a SQL query with joins when appropriate for MVFs" do
|
@@ -396,7 +410,7 @@ describe 'separate queries for field' do
|
|
396
410
|
declaration, query, range = field.split(/;\s+/)
|
397
411
|
|
398
412
|
declaration.should == 'tags from ranged-query'
|
399
|
-
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..name. AS .tags. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id. \s?WHERE \(.taggings.\..article_id. BETWEEN \$start AND \$end\)\s? ORDER BY .taggings.\..article_id. ASC$/)
|
413
|
+
query.should match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .tags.\..name. AS .tags. FROM .taggings. INNER JOIN .tags. ON .tags.\..id. = .taggings.\..tag_id. \s?WHERE \(.taggings.\..article_id. BETWEEN \$start AND \$end\) AND \(.taggings.\..article_id. IS NOT NULL\)\s? ORDER BY .taggings.\..article_id. ASC$/)
|
400
414
|
range.should match(/^SELECT MIN\(.taggings.\..article_id.\), MAX\(.taggings.\..article_id.\) FROM .taggings.\s?$/)
|
401
415
|
end
|
402
416
|
|
data/spec/internal/db/schema.rb
CHANGED
@@ -17,6 +17,15 @@ ActiveRecord::Schema.define do
|
|
17
17
|
t.timestamps
|
18
18
|
end
|
19
19
|
|
20
|
+
create_table(:manufacturers, :force => true) do |t|
|
21
|
+
t.string :name
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table(:cars, :force => true) do |t|
|
25
|
+
t.integer :manufacturer_id
|
26
|
+
t.string :name
|
27
|
+
end
|
28
|
+
|
20
29
|
create_table(:books, :force => true) do |t|
|
21
30
|
t.string :title
|
22
31
|
t.string :author
|
@@ -4,6 +4,7 @@ module ThinkingSphinx
|
|
4
4
|
end
|
5
5
|
end
|
6
6
|
|
7
|
+
require 'thinking_sphinx/errors'
|
7
8
|
require 'thinking_sphinx/active_record/attribute/type'
|
8
9
|
|
9
10
|
describe ThinkingSphinx::ActiveRecord::Attribute::Type do
|
@@ -143,5 +144,11 @@ describe ThinkingSphinx::ActiveRecord::Attribute::Type do
|
|
143
144
|
|
144
145
|
type.type.should == :timestamp
|
145
146
|
end
|
147
|
+
|
148
|
+
it 'raises an error if the database column does not exist' do
|
149
|
+
model.columns.clear
|
150
|
+
|
151
|
+
expect { type.type }.to raise_error(ThinkingSphinx::MissingColumnError)
|
152
|
+
end
|
146
153
|
end
|
147
154
|
end
|
@@ -48,6 +48,12 @@ describe ThinkingSphinx::ActiveRecord::Base do
|
|
48
48
|
end
|
49
49
|
|
50
50
|
describe '.search' do
|
51
|
+
let(:stack) { double('stack', :call => true) }
|
52
|
+
|
53
|
+
before :each do
|
54
|
+
stub_const 'ThinkingSphinx::Middlewares::DEFAULT', stack
|
55
|
+
end
|
56
|
+
|
51
57
|
it "returns a new search object" do
|
52
58
|
model.search.should be_a(ThinkingSphinx::Search)
|
53
59
|
end
|
@@ -60,6 +66,17 @@ describe ThinkingSphinx::ActiveRecord::Base do
|
|
60
66
|
model.search('pancakes').options[:classes].should == [model]
|
61
67
|
end
|
62
68
|
|
69
|
+
it "passes through options to the search object" do
|
70
|
+
model.search('pancakes', populate: true).
|
71
|
+
options[:populate].should be_true
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should automatically populate when :populate is set to true" do
|
75
|
+
stack.should_receive(:call).and_return(true)
|
76
|
+
|
77
|
+
model.search('pancakes', populate: true)
|
78
|
+
end
|
79
|
+
|
63
80
|
it "merges the :classes option with the model" do
|
64
81
|
model.search('pancakes', :classes => [sub_model]).
|
65
82
|
options[:classes].should == [sub_model, model]
|
@@ -20,7 +20,8 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks do
|
|
20
20
|
:indices_for_references => [index]) }
|
21
21
|
let(:connection) { double('connection', :execute => '') }
|
22
22
|
let(:index) { double 'index', :name => 'article_core',
|
23
|
-
:sources => [source], :document_id_for_key => 3, :distributed? => false
|
23
|
+
:sources => [source], :document_id_for_key => 3, :distributed? => false,
|
24
|
+
:type => 'plain'}
|
24
25
|
let(:source) { double('source', :attributes => []) }
|
25
26
|
|
26
27
|
before :each do
|
@@ -128,6 +128,10 @@ describe ThinkingSphinx::Configuration do
|
|
128
128
|
end
|
129
129
|
|
130
130
|
describe '#initialize' do
|
131
|
+
before :each do
|
132
|
+
FileUtils.rm_rf Rails.root.join('log')
|
133
|
+
end
|
134
|
+
|
131
135
|
it "sets the daemon pid file within log for the Rails app" do
|
132
136
|
config.searchd.pid_file.
|
133
137
|
should == File.join(Rails.root, 'log', 'test.sphinx.pid')
|
@@ -154,6 +158,19 @@ describe ThinkingSphinx::Configuration do
|
|
154
158
|
|
155
159
|
config.searchd.workers.should == 'none'
|
156
160
|
end
|
161
|
+
|
162
|
+
it 'adds settings to indexer without common section' do
|
163
|
+
write_configuration 'lemmatizer_base' => 'foo'
|
164
|
+
|
165
|
+
expect(config.indexer.lemmatizer_base).to eq('foo')
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'adds settings to common section if requested' do
|
169
|
+
write_configuration 'lemmatizer_base' => 'foo',
|
170
|
+
'common_sphinx_configuration' => true
|
171
|
+
|
172
|
+
expect(config.common.lemmatizer_base).to eq('foo')
|
173
|
+
end
|
157
174
|
end
|
158
175
|
|
159
176
|
describe '#next_offset' do
|
@@ -176,6 +193,13 @@ describe ThinkingSphinx::Configuration do
|
|
176
193
|
end
|
177
194
|
|
178
195
|
describe '#preload_indices' do
|
196
|
+
let(:distributor) { double :reconcile => true }
|
197
|
+
|
198
|
+
before :each do
|
199
|
+
stub_const 'ThinkingSphinx::Configuration::DistributedIndices',
|
200
|
+
double(:new => distributor)
|
201
|
+
end
|
202
|
+
|
179
203
|
it "searches each index path for ruby files" do
|
180
204
|
config.index_paths.replace ['/path/to/indices', '/path/to/other/indices']
|
181
205
|
|
@@ -217,6 +241,20 @@ describe ThinkingSphinx::Configuration do
|
|
217
241
|
config.preload_indices
|
218
242
|
config.preload_indices
|
219
243
|
end
|
244
|
+
|
245
|
+
it 'adds distributed indices' do
|
246
|
+
distributor.should_receive(:reconcile)
|
247
|
+
|
248
|
+
config.preload_indices
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'does not add distributed indices if disabled' do
|
252
|
+
write_configuration('distributed_indices' => false)
|
253
|
+
|
254
|
+
distributor.should_not_receive(:reconcile)
|
255
|
+
|
256
|
+
config.preload_indices
|
257
|
+
end
|
220
258
|
end
|
221
259
|
|
222
260
|
describe '#render' do
|
@@ -373,8 +411,8 @@ describe ThinkingSphinx::Configuration do
|
|
373
411
|
end
|
374
412
|
|
375
413
|
describe '#version' do
|
376
|
-
it "defaults to 2.
|
377
|
-
config.version.should == '2.
|
414
|
+
it "defaults to 2.1.4" do
|
415
|
+
config.version.should == '2.1.4'
|
378
416
|
end
|
379
417
|
|
380
418
|
it "respects supplied YAML versions" do
|
@@ -33,6 +33,27 @@ describe ThinkingSphinx::SphinxError do
|
|
33
33
|
should be_a(ThinkingSphinx::ConnectionError)
|
34
34
|
end
|
35
35
|
|
36
|
+
it 'prefixes the connection error message' do
|
37
|
+
error.stub :message => "Can't connect to MySQL server on '127.0.0.1' (61)"
|
38
|
+
|
39
|
+
ThinkingSphinx::SphinxError.new_from_mysql(error).message.
|
40
|
+
should == "Error connecting to Sphinx via the MySQL protocol. Can't connect to MySQL server on '127.0.0.1' (61)"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "translates jdbc connection errors" do
|
44
|
+
error.stub :message => "Communications link failure"
|
45
|
+
|
46
|
+
ThinkingSphinx::SphinxError.new_from_mysql(error).
|
47
|
+
should be_a(ThinkingSphinx::ConnectionError)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'prefixes the jdbc connection error message' do
|
51
|
+
error.stub :message => "Communications link failure"
|
52
|
+
|
53
|
+
ThinkingSphinx::SphinxError.new_from_mysql(error).message.
|
54
|
+
should == "Error connecting to Sphinx via the MySQL protocol. Communications link failure"
|
55
|
+
end
|
56
|
+
|
36
57
|
it "defaults to sphinx errors" do
|
37
58
|
error.stub :message => 'index foo: unknown error: something is wrong'
|
38
59
|
|
@@ -26,12 +26,12 @@ describe ThinkingSphinx::FacetSearch do
|
|
26
26
|
DumbSearch = ::Struct.new(:query, :options) do
|
27
27
|
def raw
|
28
28
|
[{
|
29
|
-
'sphinx_internal_class'
|
30
|
-
'price_bracket'
|
31
|
-
'tag_ids'
|
32
|
-
'category_id'
|
33
|
-
|
34
|
-
|
29
|
+
'sphinx_internal_class' => 'Foo',
|
30
|
+
'price_bracket' => 3,
|
31
|
+
'tag_ids' => '1,2',
|
32
|
+
'category_id' => 11,
|
33
|
+
'sphinx_internal_count' => 5,
|
34
|
+
'sphinx_internal_group' => 2
|
35
35
|
}]
|
36
36
|
end
|
37
37
|
end
|
@@ -15,10 +15,6 @@ describe ThinkingSphinx::Middlewares::Inquirer do
|
|
15
15
|
:results => [[:raw], [{'Variable_name' => 'meta', 'Value' => 'value'}]]) }
|
16
16
|
|
17
17
|
before :each do
|
18
|
-
context.stub(:log) do |notification, message, &block|
|
19
|
-
block.call unless block.nil?
|
20
|
-
end
|
21
|
-
|
22
18
|
batch_class = double
|
23
19
|
batch_class.stub(:new).and_return(batch_inquirer)
|
24
20
|
|
@@ -6,6 +6,9 @@ module ActiveRecord
|
|
6
6
|
class Base; end
|
7
7
|
end
|
8
8
|
|
9
|
+
class SphinxQLSubclass
|
10
|
+
end
|
11
|
+
|
9
12
|
require 'active_support/core_ext/module/attribute_accessors'
|
10
13
|
require 'active_support/core_ext/module/delegation'
|
11
14
|
require 'active_support/core_ext/object/blank'
|
@@ -185,6 +188,28 @@ describe ThinkingSphinx::Middlewares::SphinxQL do
|
|
185
188
|
middleware.call [context]
|
186
189
|
end
|
187
190
|
|
191
|
+
it "ignores blank subclasses" do
|
192
|
+
db_connection = double('db connection', :select_values => [''],
|
193
|
+
:schema_cache => double('cache', :table_exists? => false))
|
194
|
+
supermodel = Class.new(ActiveRecord::Base) do
|
195
|
+
def self.name; 'Cat'; end
|
196
|
+
def self.inheritance_column; 'type'; end
|
197
|
+
end
|
198
|
+
supermodel.stub :connection => db_connection, :column_names => ['type']
|
199
|
+
submodel = Class.new(supermodel) do
|
200
|
+
def self.name; 'Lion'; end
|
201
|
+
def self.inheritance_column; 'type'; end
|
202
|
+
def self.table_name; 'cats'; end
|
203
|
+
end
|
204
|
+
submodel.stub :connection => db_connection, :column_names => ['type'],
|
205
|
+
:descendants => []
|
206
|
+
index_set.first.stub :reference => :cat
|
207
|
+
|
208
|
+
search.options[:classes] = [submodel]
|
209
|
+
|
210
|
+
expect { middleware.call [context] }.to_not raise_error
|
211
|
+
end
|
212
|
+
|
188
213
|
it "filters out deleted values by default" do
|
189
214
|
sphinx_sql.should_receive(:where).with(:sphinx_deleted => false).
|
190
215
|
and_return(sphinx_sql)
|
@@ -15,7 +15,7 @@ describe ThinkingSphinx::Middlewares::StaleIdFilter do
|
|
15
15
|
|
16
16
|
describe '#call' do
|
17
17
|
before :each do
|
18
|
-
context.stub :search => search
|
18
|
+
context.stub :search => search
|
19
19
|
end
|
20
20
|
|
21
21
|
context 'one stale ids exception' do
|
@@ -88,4 +88,4 @@ describe ThinkingSphinx::Middlewares::StaleIdFilter do
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
91
|
-
end
|
91
|
+
end
|
@@ -5,7 +5,8 @@ describe ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks do
|
|
5
5
|
ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks.new :article
|
6
6
|
}
|
7
7
|
let(:instance) { double('instance', :id => 12) }
|
8
|
-
let(:config) { double('config', :indices_for_references => [index]
|
8
|
+
let(:config) { double('config', :indices_for_references => [index],
|
9
|
+
:settings => {}) }
|
9
10
|
let(:index) { double('index', :name => 'my_index', :is_a? => true,
|
10
11
|
:document_id_for_key => 123, :fields => [], :attributes => [],
|
11
12
|
:conditions => []) }
|
@@ -5,6 +5,34 @@ describe ThinkingSphinx::Search do
|
|
5
5
|
let(:context) { {:results => []} }
|
6
6
|
let(:stack) { double('stack', :call => true) }
|
7
7
|
|
8
|
+
let(:pagination_mask_methods) do
|
9
|
+
[:first_page?,
|
10
|
+
:last_page?,
|
11
|
+
:next_page,
|
12
|
+
:next_page?,
|
13
|
+
:page,
|
14
|
+
:per,
|
15
|
+
:previous_page,
|
16
|
+
:total_entries,
|
17
|
+
:total_count,
|
18
|
+
:count,
|
19
|
+
:total_pages,
|
20
|
+
:page_count,
|
21
|
+
:num_pages]
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:scopes_mask_methods) do
|
25
|
+
[:facets,
|
26
|
+
:search,
|
27
|
+
:search_for_ids]
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:group_enumerator_mask_methods) do
|
31
|
+
[:each_with_count,
|
32
|
+
:each_with_group,
|
33
|
+
:each_with_group_and_count]
|
34
|
+
end
|
35
|
+
|
8
36
|
before :each do
|
9
37
|
ThinkingSphinx::Search::Context.stub :new => context
|
10
38
|
|
@@ -141,6 +169,24 @@ describe ThinkingSphinx::Search do
|
|
141
169
|
it "should respond to Search methods" do
|
142
170
|
search.respond_to?(:per_page).should be_true
|
143
171
|
end
|
172
|
+
|
173
|
+
it "should return true for methods delegated to pagination mask by method_missing" do
|
174
|
+
pagination_mask_methods.each do |method|
|
175
|
+
expect(search).to respond_to method
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should return true for methods delegated to scopes mask by method_missing" do
|
180
|
+
scopes_mask_methods.each do |method|
|
181
|
+
expect(search).to respond_to method
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should return true for methods delegated to group enumerators mask by method_missing" do
|
186
|
+
group_enumerator_mask_methods.each do |method|
|
187
|
+
expect(search).to respond_to method
|
188
|
+
end
|
189
|
+
end
|
144
190
|
end
|
145
191
|
|
146
192
|
describe '#to_a' do
|
@@ -153,4 +199,14 @@ describe ThinkingSphinx::Search do
|
|
153
199
|
search.to_a.first.__id__.should == unglazed.__id__
|
154
200
|
end
|
155
201
|
end
|
202
|
+
|
203
|
+
it "correctly handles access to methods delegated to masks through 'method' call" do
|
204
|
+
[
|
205
|
+
pagination_mask_methods,
|
206
|
+
scopes_mask_methods,
|
207
|
+
group_enumerator_mask_methods
|
208
|
+
].flatten.each do |method|
|
209
|
+
expect { search.method method }.to_not raise_exception
|
210
|
+
end
|
211
|
+
end
|
156
212
|
end
|