ebeigarts-thinking-sphinx 1.1.22 → 1.2.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/README.textile +14 -0
  2. data/VERSION.yml +4 -0
  3. data/lib/thinking_sphinx.rb +60 -64
  4. data/lib/thinking_sphinx/active_record.rb +35 -7
  5. data/lib/thinking_sphinx/active_record/scopes.rb +39 -0
  6. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +3 -2
  7. data/lib/thinking_sphinx/attribute.rb +62 -22
  8. data/lib/thinking_sphinx/configuration.rb +21 -1
  9. data/lib/thinking_sphinx/core/array.rb +7 -0
  10. data/lib/thinking_sphinx/deltas/delayed_delta.rb +3 -0
  11. data/lib/thinking_sphinx/deploy/capistrano.rb +26 -8
  12. data/lib/thinking_sphinx/excerpter.rb +22 -0
  13. data/lib/thinking_sphinx/facet.rb +8 -2
  14. data/lib/thinking_sphinx/facet_search.rb +134 -0
  15. data/lib/thinking_sphinx/index.rb +2 -2
  16. data/lib/thinking_sphinx/index/builder.rb +0 -1
  17. data/lib/thinking_sphinx/property.rb +2 -0
  18. data/lib/thinking_sphinx/rails_additions.rb +14 -0
  19. data/lib/thinking_sphinx/search.rb +633 -671
  20. data/lib/thinking_sphinx/search_methods.rb +421 -0
  21. data/lib/thinking_sphinx/source.rb +5 -5
  22. data/lib/thinking_sphinx/source/internal_properties.rb +1 -1
  23. data/lib/thinking_sphinx/source/sql.rb +10 -8
  24. data/lib/thinking_sphinx/tasks.rb +14 -9
  25. data/spec/{unit → lib}/thinking_sphinx/active_record/delta_spec.rb +1 -1
  26. data/spec/{unit → lib}/thinking_sphinx/active_record/has_many_association_spec.rb +0 -0
  27. data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +96 -0
  28. data/spec/{unit → lib}/thinking_sphinx/active_record_spec.rb +44 -5
  29. data/spec/{unit → lib}/thinking_sphinx/association_spec.rb +0 -0
  30. data/spec/{unit → lib}/thinking_sphinx/attribute_spec.rb +110 -3
  31. data/spec/{unit → lib}/thinking_sphinx/configuration_spec.rb +87 -41
  32. data/spec/lib/thinking_sphinx/core/array_spec.rb +9 -0
  33. data/spec/{unit → lib}/thinking_sphinx/core/string_spec.rb +0 -0
  34. data/spec/lib/thinking_sphinx/excerpter_spec.rb +49 -0
  35. data/spec/lib/thinking_sphinx/facet_search_spec.rb +176 -0
  36. data/spec/{unit → lib}/thinking_sphinx/facet_spec.rb +34 -15
  37. data/spec/{unit → lib}/thinking_sphinx/field_spec.rb +0 -0
  38. data/spec/{unit → lib}/thinking_sphinx/index/builder_spec.rb +100 -0
  39. data/spec/{unit → lib}/thinking_sphinx/index/faux_column_spec.rb +0 -0
  40. data/spec/{unit → lib}/thinking_sphinx/index_spec.rb +0 -0
  41. data/spec/{unit → lib}/thinking_sphinx/rails_additions_spec.rb +12 -0
  42. data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
  43. data/spec/lib/thinking_sphinx/search_spec.rb +1066 -0
  44. data/spec/{unit → lib}/thinking_sphinx/source_spec.rb +10 -0
  45. data/spec/{unit → lib}/thinking_sphinx_spec.rb +10 -0
  46. data/tasks/distribution.rb +20 -38
  47. data/tasks/testing.rb +3 -1
  48. data/vendor/riddle/lib/riddle.rb +1 -1
  49. data/vendor/riddle/lib/riddle/client.rb +3 -0
  50. data/vendor/riddle/lib/riddle/client/message.rb +4 -3
  51. data/vendor/riddle/lib/riddle/configuration/section.rb +1 -1
  52. data/vendor/riddle/lib/riddle/controller.rb +17 -7
  53. metadata +63 -83
  54. data/lib/thinking_sphinx/active_record/search.rb +0 -57
  55. data/lib/thinking_sphinx/collection.rb +0 -148
  56. data/lib/thinking_sphinx/facet_collection.rb +0 -59
  57. data/lib/thinking_sphinx/search/facets.rb +0 -104
  58. data/spec/unit/thinking_sphinx/active_record/search_spec.rb +0 -107
  59. data/spec/unit/thinking_sphinx/collection_spec.rb +0 -15
  60. data/spec/unit/thinking_sphinx/facet_collection_spec.rb +0 -64
  61. data/spec/unit/thinking_sphinx/search_spec.rb +0 -228
@@ -2,7 +2,7 @@ module ThinkingSphinx
2
2
  class Source
3
3
  module InternalProperties
4
4
  def add_internal_attributes_and_facets
5
- add_internal_attribute :sphinx_internal_id, :integer, @model.primary_key.to_sym
5
+ add_internal_attribute :sphinx_internal_id, :integer, @model.primary_key_for_sphinx.to_sym
6
6
  add_internal_attribute :class_crc, :integer, crc_column, true
7
7
  add_internal_attribute :subclass_crcs, :multi, subclasses_to_s
8
8
  add_internal_attribute :sphinx_deleted, :integer, "0"
@@ -33,10 +33,10 @@ GROUP BY #{ sql_group_clause }
33
33
  return nil if @index.options[:disable_range]
34
34
 
35
35
  min_statement = adapter.convert_nulls(
36
- "MIN(#{quote_column(@model.primary_key)})", 1
36
+ "MIN(#{quote_column(@model.primary_key_for_sphinx)})", 1
37
37
  )
38
38
  max_statement = adapter.convert_nulls(
39
- "MAX(#{quote_column(@model.primary_key)})", 1
39
+ "MAX(#{quote_column(@model.primary_key_for_sphinx)})", 1
40
40
  )
41
41
 
42
42
  sql = "SELECT #{min_statement}, #{max_statement} " +
@@ -53,14 +53,14 @@ GROUP BY #{ sql_group_clause }
53
53
  #
54
54
  def to_sql_query_info(offset)
55
55
  "SELECT * FROM #{@model.quoted_table_name} WHERE " +
56
- "#{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
56
+ "#{quote_column(@model.primary_key_for_sphinx)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
57
57
  end
58
58
 
59
59
  def sql_select_clause(offset)
60
60
  unique_id_expr = ThinkingSphinx.unique_id_expression(offset)
61
61
 
62
62
  (
63
- ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] +
63
+ ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} #{unique_id_expr} AS #{quote_column(@model.primary_key_for_sphinx)} "] +
64
64
  @fields.collect { |field| field.to_select_sql } +
65
65
  @attributes.collect { |attribute| attribute.to_select_sql }
66
66
  ).compact.join(", ")
@@ -69,8 +69,8 @@ GROUP BY #{ sql_group_clause }
69
69
  def sql_where_clause(options)
70
70
  logic = []
71
71
  logic += [
72
- "#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} >= $start",
73
- "#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} <= $end"
72
+ "#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} >= $start",
73
+ "#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} <= $end"
74
74
  ] unless @index.options[:disable_range]
75
75
 
76
76
  if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
@@ -88,7 +88,7 @@ GROUP BY #{ sql_group_clause }
88
88
  end
89
89
 
90
90
  (
91
- ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] +
91
+ ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)}"] +
92
92
  @fields.collect { |field| field.to_group_sql }.compact +
93
93
  @attributes.collect { |attribute| attribute.to_group_sql }.compact +
94
94
  @groupings + internal_groupings
@@ -112,7 +112,9 @@ GROUP BY #{ sql_group_clause }
112
112
  end
113
113
 
114
114
  def crc_column
115
- if @model.column_names.include?(@model.inheritance_column)
115
+ if @model.table_exists? &&
116
+ @model.column_names.include?(@model.inheritance_column)
117
+
116
118
  adapter.cast_to_unsigned(adapter.convert_nulls(
117
119
  adapter.crc(adapter.quote_with_table(@model.inheritance_column), true),
118
120
  @model.to_crc32
@@ -12,7 +12,7 @@ namespace :thinking_sphinx do
12
12
 
13
13
  desc "Output the current Thinking Sphinx version"
14
14
  task :version => :app_env do
15
- puts "Thinking Sphinx v" + ThinkingSphinx::Version::String
15
+ puts "Thinking Sphinx v" + ThinkingSphinx.version
16
16
  end
17
17
 
18
18
  desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
@@ -30,7 +30,7 @@ namespace :thinking_sphinx do
30
30
 
31
31
  Dir["#{config.searchd_file_path}/*.spl"].each { |file| File.delete(file) }
32
32
 
33
- system! "#{config.bin_path}#{config.searchd_binary_name} --pidfile --config #{config.config_file}"
33
+ system! "#{config.bin_path}#{config.searchd_binary_name} --pidfile --config \"#{config.config_file}\""
34
34
 
35
35
  sleep(2)
36
36
 
@@ -43,11 +43,14 @@ namespace :thinking_sphinx do
43
43
 
44
44
  desc "Stop Sphinx using Thinking Sphinx's settings"
45
45
  task :stop => :app_env do
46
- raise RuntimeError, "searchd is not running." unless sphinx_running?
47
- config = ThinkingSphinx::Configuration.instance
48
- pid = sphinx_pid
49
- system! "#{config.bin_path}#{config.searchd_binary_name} --stop --config #{config.config_file}"
50
- puts "Stopped search daemon (pid #{pid})."
46
+ unless sphinx_running?
47
+ puts "searchd is not running"
48
+ else
49
+ config = ThinkingSphinx::Configuration.instance
50
+ pid = sphinx_pid
51
+ system! "#{config.bin_path}#{config.searchd_binary_name} --stop --config \"#{config.config_file}\""
52
+ puts "Stopped search daemon (pid #{pid})."
53
+ end
51
54
  end
52
55
 
53
56
  desc "Restart Sphinx"
@@ -71,7 +74,7 @@ namespace :thinking_sphinx do
71
74
  end
72
75
 
73
76
  FileUtils.mkdir_p config.searchd_file_path
74
- cmd = "#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} --all"
77
+ cmd = "#{config.bin_path}#{config.indexer_binary_name} --config \"#{config.config_file}\" --all"
75
78
  cmd << " --rotate" if sphinx_running?
76
79
 
77
80
  system! cmd
@@ -111,6 +114,7 @@ namespace :thinking_sphinx do
111
114
  # http://www.sphinxsearch.com/docs/current.html#xmlpipe2
112
115
  desc "Streams XML data to STDOUT"
113
116
  task :xml => :app_env do
117
+ ThinkingSphinx::Configuration.instance.load_models
114
118
  source_name = ENV["NAME"]
115
119
  # STDERR.puts "Source name: #{source_name}"
116
120
  source_name =~ /^(.+)_(delta|core)_(\d+)$/
@@ -141,7 +145,8 @@ namespace :thinking_sphinx do
141
145
  # STDERR.puts "fetching #{start_id}.. "
142
146
  # STDERR.puts query
143
147
  model_klass.sphinx_database_adapter.select_each(query) do |values|
144
- id = values.delete "id"
148
+ pk_name = model_klass.primary_key_for_sphinx.to_s
149
+ id = values.delete(pk_name)
145
150
  puts %{<sphinx:document id="#{id.to_i}">}
146
151
  values.each do |k, v|
147
152
  if source.sql_attr_bool.include?(k.to_sym)
@@ -18,7 +18,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
18
18
 
19
19
  @beta.should have_received(:toggle_delta)
20
20
  end
21
-
21
+
22
22
  describe "suspended_delta method" do
23
23
  before :each do
24
24
  ThinkingSphinx.stub_method(:deltas_enabled? => true)
@@ -0,0 +1,96 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::ActiveRecord::Scopes do
4
+ after :each do
5
+ Alpha.remove_sphinx_scopes
6
+ end
7
+
8
+ it "should be included into models with indexes" do
9
+ Alpha.included_modules.should include(ThinkingSphinx::ActiveRecord::Scopes)
10
+ end
11
+
12
+ it "should not be included into models without indexes" do
13
+ Gamma.included_modules.should_not include(
14
+ ThinkingSphinx::ActiveRecord::Scopes
15
+ )
16
+ end
17
+
18
+ describe '.sphinx_scope' do
19
+ before :each do
20
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
21
+ end
22
+
23
+ it "should define a method on the model" do
24
+ Alpha.should respond_to(:by_name)
25
+ end
26
+ end
27
+
28
+ describe '.sphinx_scopes' do
29
+ before :each do
30
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
31
+ end
32
+
33
+ it "should return an array of defined scope names as symbols" do
34
+ Alpha.sphinx_scopes.should == [:by_name]
35
+ end
36
+ end
37
+
38
+ describe '.remove_sphinx_scopes' do
39
+ before :each do
40
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
41
+ Alpha.remove_sphinx_scopes
42
+ end
43
+
44
+ it "should remove sphinx scope methods" do
45
+ Alpha.should_not respond_to(:by_name)
46
+ end
47
+
48
+ it "should empty the list of sphinx scopes" do
49
+ Alpha.sphinx_scopes.should be_empty
50
+ end
51
+ end
52
+
53
+ describe '.example_scope' do
54
+ before :each do
55
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
56
+ Alpha.sphinx_scope(:by_foo) { |foo| {:conditions => {:foo => foo}} }
57
+ Alpha.sphinx_scope(:with_betas) { {:classes => [Beta]} }
58
+ end
59
+
60
+ it "should return a ThinkingSphinx::Search object" do
61
+ Alpha.by_name('foo').should be_a(ThinkingSphinx::Search)
62
+ end
63
+
64
+ it "should set the classes option" do
65
+ Alpha.by_name('foo').options[:classes].should == [Alpha]
66
+ end
67
+
68
+ it "should be able to be called on a ThinkingSphinx::Search object" do
69
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
70
+ lambda {
71
+ search.by_name('foo')
72
+ }.should_not raise_error
73
+ end
74
+
75
+ it "should return the search object it gets called upon" do
76
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
77
+ search.by_name('foo').should == search
78
+ end
79
+
80
+ it "should apply the scope options to the underlying search object" do
81
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
82
+ search.by_name('foo').options[:conditions].should == {:name => 'foo'}
83
+ end
84
+
85
+ it "should combine hash option scopes such as :conditions" do
86
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
87
+ search.by_name('foo').by_foo('bar').options[:conditions].
88
+ should == {:name => 'foo', :foo => 'bar'}
89
+ end
90
+
91
+ it "should combine array option scopes such as :classes" do
92
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
93
+ search.with_betas.options[:classes].should == [Alpha, Beta]
94
+ end
95
+ end
96
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec/spec_helper'
2
2
 
3
- describe "ThinkingSphinx::ActiveRecord" do
4
- describe "define_index method" do
3
+ describe ThinkingSphinx::ActiveRecord do
4
+ describe '.define_index' do
5
5
  before :each do
6
6
  module ::TestModule
7
7
  class TestModel < ActiveRecord::Base; end
@@ -154,7 +154,7 @@ describe "ThinkingSphinx::ActiveRecord" do
154
154
  end
155
155
  end
156
156
 
157
- describe "source_of_sphinx_index method" do
157
+ describe '.source_of_sphinx_index' do
158
158
  it "should return self if model defines an index" do
159
159
  Person.source_of_sphinx_index.should == Person
160
160
  end
@@ -164,13 +164,13 @@ describe "ThinkingSphinx::ActiveRecord" do
164
164
  end
165
165
  end
166
166
 
167
- describe "to_crc32 method" do
167
+ describe '.to_crc32' do
168
168
  it "should return an integer" do
169
169
  Person.to_crc32.should be_a_kind_of(Integer)
170
170
  end
171
171
  end
172
172
 
173
- describe "to_crc32s method" do
173
+ describe '.to_crc32s' do
174
174
  it "should return an array" do
175
175
  Person.to_crc32s.should be_a_kind_of(Array)
176
176
  end
@@ -326,4 +326,43 @@ describe "ThinkingSphinx::ActiveRecord" do
326
326
 
327
327
  (beta.id * model_count + offset).should == beta.sphinx_document_id
328
328
  end
329
+
330
+ describe '#primary_key_for_sphinx' do
331
+ before :each do
332
+ @person = Person.find(:first)
333
+ end
334
+
335
+ after :each do
336
+ Person.set_sphinx_primary_key nil
337
+ end
338
+
339
+ it "should return the id by default" do
340
+ @person.primary_key_for_sphinx.should == @person.id
341
+ end
342
+
343
+ it "should use the sphinx primary key to determine the value" do
344
+ Person.set_sphinx_primary_key :first_name
345
+ @person.primary_key_for_sphinx.should == @person.first_name
346
+ end
347
+
348
+ it "should not use accessor methods but the attributes hash" do
349
+ id = @person.id
350
+ @person.stub!(:id => 'unique_hash')
351
+ @person.primary_key_for_sphinx.should == id
352
+ end
353
+ end
354
+
355
+ describe '.sphinx_index_names' do
356
+ it "should return the core index" do
357
+ Alpha.sphinx_index_names.should == ['alpha_core']
358
+ end
359
+
360
+ it "should return the delta index if enabled" do
361
+ Beta.sphinx_index_names.should == ['beta_core', 'beta_delta']
362
+ end
363
+
364
+ it "should return the superclass with an index definition" do
365
+ Parent.sphinx_index_names.should == ['person_core', 'person_delta']
366
+ end
367
+ end
329
368
  end
@@ -4,6 +4,8 @@ describe ThinkingSphinx::Attribute do
4
4
  before :each do
5
5
  @index = ThinkingSphinx::Index.new(Person)
6
6
  @source = ThinkingSphinx::Source.new(@index)
7
+
8
+ @index.delta_object = ThinkingSphinx::Deltas::DefaultDelta.new @index, @index.local_options
7
9
  end
8
10
 
9
11
  describe '#initialize' do
@@ -86,6 +88,19 @@ describe ThinkingSphinx::Attribute do
86
88
  end
87
89
  end
88
90
 
91
+ describe '#to_select_sql' do
92
+ it "should convert a mixture of dates and datetimes to timestamps" do
93
+ attribute = ThinkingSphinx::Attribute.new(@source,
94
+ [ ThinkingSphinx::Index::FauxColumn.new(:created_at),
95
+ ThinkingSphinx::Index::FauxColumn.new(:created_on) ],
96
+ :as => :times
97
+ )
98
+ attribute.model = Friendship
99
+
100
+ attribute.to_select_sql.should == "CONCAT_WS(',', UNIX_TIMESTAMP(`friendships`.`created_at`), UNIX_TIMESTAMP(`friendships`.`created_on`)) AS `times`"
101
+ end
102
+ end
103
+
89
104
  describe "is_many? method" do
90
105
  before :each do
91
106
  @assoc_a = Object.stub_instance(:is_many? => true)
@@ -189,7 +204,7 @@ describe ThinkingSphinx::Attribute do
189
204
  attribute.model = Person
190
205
  attribute.columns.each { |col| attribute.associations[col] = [] }
191
206
 
192
- attribute.send(:all_ints?).should be_true
207
+ attribute.should be_all_ints
193
208
  end
194
209
 
195
210
  it "should return false if only some columns are integers" do
@@ -200,7 +215,7 @@ describe ThinkingSphinx::Attribute do
200
215
  attribute.model = Person
201
216
  attribute.columns.each { |col| attribute.associations[col] = [] }
202
217
 
203
- attribute.send(:all_ints?).should be_false
218
+ attribute.should_not be_all_ints
204
219
  end
205
220
 
206
221
  it "should return false if no columns are integers" do
@@ -211,7 +226,42 @@ describe ThinkingSphinx::Attribute do
211
226
  attribute.model = Person
212
227
  attribute.columns.each { |col| attribute.associations[col] = [] }
213
228
 
214
- attribute.send(:all_ints?).should be_false
229
+ attribute.should_not be_all_ints
230
+ end
231
+ end
232
+
233
+ describe "all_datetimes? method" do
234
+ it "should return true if all columns are datetimes" do
235
+ attribute = ThinkingSphinx::Attribute.new(@source,
236
+ [ ThinkingSphinx::Index::FauxColumn.new(:created_at),
237
+ ThinkingSphinx::Index::FauxColumn.new(:updated_at) ]
238
+ )
239
+ attribute.model = Friendship
240
+ attribute.columns.each { |col| attribute.associations[col] = [] }
241
+
242
+ attribute.should be_all_datetimes
243
+ end
244
+
245
+ it "should return false if only some columns are datetimes" do
246
+ attribute = ThinkingSphinx::Attribute.new(@source,
247
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
248
+ ThinkingSphinx::Index::FauxColumn.new(:created_at) ]
249
+ )
250
+ attribute.model = Friendship
251
+ attribute.columns.each { |col| attribute.associations[col] = [] }
252
+
253
+ attribute.should_not be_all_datetimes
254
+ end
255
+
256
+ it "should return true if all columns can be " do
257
+ attribute = ThinkingSphinx::Attribute.new(@source,
258
+ [ ThinkingSphinx::Index::FauxColumn.new(:created_at),
259
+ ThinkingSphinx::Index::FauxColumn.new(:created_on) ]
260
+ )
261
+ attribute.model = Friendship
262
+ attribute.columns.each { |col| attribute.associations[col] = [] }
263
+
264
+ attribute.should be_all_datetimes
215
265
  end
216
266
  end
217
267
 
@@ -232,6 +282,23 @@ describe ThinkingSphinx::Attribute do
232
282
  end
233
283
  end
234
284
 
285
+ describe "MVA with source query for a delta source" do
286
+ before :each do
287
+ @attribute = ThinkingSphinx::Attribute.new(@source,
288
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
289
+ :as => :tag_ids, :source => :query
290
+ )
291
+ end
292
+
293
+ it "should use a query" do
294
+ @attribute.type_to_config.should == :sql_attr_multi
295
+
296
+ declaration, query = @attribute.config_value(nil, true).split('; ')
297
+ declaration.should == "uint tag_ids from query"
298
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags` WHERE `tags`.`person_id` IN (SELECT `id` FROM `people` WHERE `people`.`delta` = 1)"
299
+ end
300
+ end
301
+
235
302
  describe "MVA via a HABTM association with a source query" do
236
303
  before :each do
237
304
  @attribute = ThinkingSphinx::Attribute.new(@source,
@@ -267,6 +334,24 @@ describe ThinkingSphinx::Attribute do
267
334
  end
268
335
  end
269
336
 
337
+ describe "MVA with ranged source query for a delta source" do
338
+ before :each do
339
+ @attribute = ThinkingSphinx::Attribute.new(@source,
340
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
341
+ :as => :tag_ids, :source => :ranged_query
342
+ )
343
+ end
344
+
345
+ it "should use a ranged query" do
346
+ @attribute.type_to_config.should == :sql_attr_multi
347
+
348
+ declaration, query, range_query = @attribute.config_value(nil, true).split('; ')
349
+ declaration.should == "uint tag_ids from ranged-query"
350
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags` WHERE `tags`.`person_id` >= $start AND `tags`.`person_id` <= $end AND `tags`.`person_id` IN (SELECT `id` FROM `people` WHERE `people`.`delta` = 1)"
351
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
352
+ end
353
+ end
354
+
270
355
  describe "MVA via a has-many :through with a ranged source query" do
271
356
  before :each do
272
357
  @attribute = ThinkingSphinx::Attribute.new(@source,
@@ -341,6 +426,28 @@ describe ThinkingSphinx::Attribute do
341
426
  end
342
427
  end
343
428
 
429
+ describe "MVA via two has-many associations with a ranged source query for a delta source" do
430
+ before :each do
431
+ @index = ThinkingSphinx::Index.new(Alpha)
432
+ @source = ThinkingSphinx::Source.new(@index)
433
+ @attribute = ThinkingSphinx::Attribute.new(@source,
434
+ [ThinkingSphinx::Index::FauxColumn.new(:betas, :gammas, :value)],
435
+ :as => :gamma_values, :source => :ranged_query
436
+ )
437
+
438
+ @index.delta_object = ThinkingSphinx::Deltas::DefaultDelta.new @index, @index.local_options
439
+ end
440
+
441
+ it "should use a ranged query" do
442
+ @attribute.type_to_config.should == :sql_attr_multi
443
+
444
+ declaration, query, range_query = @attribute.config_value(nil, true).split('; ')
445
+ declaration.should == "uint gamma_values from ranged-query"
446
+ query.should == "SELECT `betas`.`alpha_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `gammas`.`value` AS `gamma_values` FROM `betas` LEFT OUTER JOIN `gammas` ON gammas.beta_id = betas.id WHERE `betas`.`alpha_id` >= $start AND `betas`.`alpha_id` <= $end AND `betas`.`alpha_id` IN (SELECT `id` FROM `alphas` WHERE `alphas`.`delta` = 1)"
447
+ range_query.should == "SELECT MIN(`betas`.`alpha_id`), MAX(`betas`.`alpha_id`) FROM `betas`"
448
+ end
449
+ end
450
+
344
451
  describe "with custom queries" do
345
452
  before :each do
346
453
  index = CricketTeam.sphinx_indexes.first