moneypools-thinking-sphinx 1.2.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/LICENCE +20 -0
  2. data/README.textile +157 -0
  3. data/VERSION.yml +4 -0
  4. data/lib/thinking_sphinx.rb +211 -0
  5. data/lib/thinking_sphinx/active_record.rb +307 -0
  6. data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
  7. data/lib/thinking_sphinx/active_record/delta.rb +87 -0
  8. data/lib/thinking_sphinx/active_record/has_many_association.rb +28 -0
  9. data/lib/thinking_sphinx/active_record/scopes.rb +39 -0
  10. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +42 -0
  11. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +54 -0
  12. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +136 -0
  13. data/lib/thinking_sphinx/association.rb +164 -0
  14. data/lib/thinking_sphinx/attribute.rb +340 -0
  15. data/lib/thinking_sphinx/class_facet.rb +15 -0
  16. data/lib/thinking_sphinx/configuration.rb +282 -0
  17. data/lib/thinking_sphinx/core/array.rb +7 -0
  18. data/lib/thinking_sphinx/core/string.rb +15 -0
  19. data/lib/thinking_sphinx/deltas.rb +30 -0
  20. data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
  21. data/lib/thinking_sphinx/deltas/default_delta.rb +68 -0
  22. data/lib/thinking_sphinx/deltas/delayed_delta.rb +34 -0
  23. data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
  24. data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
  25. data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
  26. data/lib/thinking_sphinx/deploy/capistrano.rb +100 -0
  27. data/lib/thinking_sphinx/excerpter.rb +22 -0
  28. data/lib/thinking_sphinx/facet.rb +125 -0
  29. data/lib/thinking_sphinx/facet_search.rb +134 -0
  30. data/lib/thinking_sphinx/field.rb +82 -0
  31. data/lib/thinking_sphinx/index.rb +99 -0
  32. data/lib/thinking_sphinx/index/builder.rb +286 -0
  33. data/lib/thinking_sphinx/index/faux_column.rb +110 -0
  34. data/lib/thinking_sphinx/property.rb +162 -0
  35. data/lib/thinking_sphinx/rails_additions.rb +150 -0
  36. data/lib/thinking_sphinx/search.rb +689 -0
  37. data/lib/thinking_sphinx/search_methods.rb +421 -0
  38. data/lib/thinking_sphinx/source.rb +150 -0
  39. data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
  40. data/lib/thinking_sphinx/source/sql.rb +128 -0
  41. data/lib/thinking_sphinx/tasks.rb +165 -0
  42. data/rails/init.rb +14 -0
  43. data/spec/lib/thinking_sphinx/active_record/delta_spec.rb +130 -0
  44. data/spec/lib/thinking_sphinx/active_record/has_many_association_spec.rb +49 -0
  45. data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +96 -0
  46. data/spec/lib/thinking_sphinx/active_record_spec.rb +364 -0
  47. data/spec/lib/thinking_sphinx/association_spec.rb +239 -0
  48. data/spec/lib/thinking_sphinx/attribute_spec.rb +500 -0
  49. data/spec/lib/thinking_sphinx/configuration_spec.rb +335 -0
  50. data/spec/lib/thinking_sphinx/core/array_spec.rb +9 -0
  51. data/spec/lib/thinking_sphinx/core/string_spec.rb +9 -0
  52. data/spec/lib/thinking_sphinx/excerpter_spec.rb +49 -0
  53. data/spec/lib/thinking_sphinx/facet_search_spec.rb +176 -0
  54. data/spec/lib/thinking_sphinx/facet_spec.rb +333 -0
  55. data/spec/lib/thinking_sphinx/field_spec.rb +154 -0
  56. data/spec/lib/thinking_sphinx/index/builder_spec.rb +455 -0
  57. data/spec/lib/thinking_sphinx/index/faux_column_spec.rb +30 -0
  58. data/spec/lib/thinking_sphinx/index_spec.rb +45 -0
  59. data/spec/lib/thinking_sphinx/rails_additions_spec.rb +203 -0
  60. data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
  61. data/spec/lib/thinking_sphinx/search_spec.rb +1066 -0
  62. data/spec/lib/thinking_sphinx/source_spec.rb +227 -0
  63. data/spec/lib/thinking_sphinx_spec.rb +162 -0
  64. data/tasks/distribution.rb +49 -0
  65. data/tasks/rails.rake +1 -0
  66. data/tasks/testing.rb +83 -0
  67. data/vendor/after_commit/LICENSE +20 -0
  68. data/vendor/after_commit/README +16 -0
  69. data/vendor/after_commit/Rakefile +22 -0
  70. data/vendor/after_commit/init.rb +8 -0
  71. data/vendor/after_commit/lib/after_commit.rb +45 -0
  72. data/vendor/after_commit/lib/after_commit/active_record.rb +114 -0
  73. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
  74. data/vendor/after_commit/test/after_commit_test.rb +53 -0
  75. data/vendor/riddle/lib/riddle.rb +30 -0
  76. data/vendor/riddle/lib/riddle/client.rb +622 -0
  77. data/vendor/riddle/lib/riddle/client/filter.rb +53 -0
  78. data/vendor/riddle/lib/riddle/client/message.rb +66 -0
  79. data/vendor/riddle/lib/riddle/client/response.rb +84 -0
  80. data/vendor/riddle/lib/riddle/configuration.rb +33 -0
  81. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +48 -0
  82. data/vendor/riddle/lib/riddle/configuration/index.rb +142 -0
  83. data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
  84. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
  85. data/vendor/riddle/lib/riddle/configuration/searchd.rb +25 -0
  86. data/vendor/riddle/lib/riddle/configuration/section.rb +43 -0
  87. data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
  88. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +34 -0
  89. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
  90. data/vendor/riddle/lib/riddle/controller.rb +54 -0
  91. metadata +168 -0
@@ -0,0 +1,239 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::Association do
4
+ describe '.children' do
5
+ before :each do
6
+ @normal_reflection = stub('reflection', :options => {
7
+ :polymorphic => false
8
+ })
9
+ @normal_association = ThinkingSphinx::Association.new(nil, nil)
10
+ @poly_reflection = stub('reflection',
11
+ :options => {:polymorphic => true},
12
+ :macro => :has_many,
13
+ :name => 'polly',
14
+ :active_record => 'AR'
15
+ )
16
+ @non_poly_reflection = stub('reflection')
17
+
18
+ Person.stub!(:reflect_on_association => @normal_reflection)
19
+ ThinkingSphinx::Association.stub!(
20
+ :new => @normal_association,
21
+ :polymorphic_classes => [Person, Person],
22
+ :casted_options => {:casted => :options}
23
+ )
24
+ ::ActiveRecord::Reflection::AssociationReflection.stub!(
25
+ :new => @non_poly_reflection
26
+ )
27
+ end
28
+
29
+ it "should return an empty array if no association exists" do
30
+ Person.stub!(:reflect_on_association => nil)
31
+
32
+ ThinkingSphinx::Association.children(Person, :assoc).should == []
33
+ end
34
+
35
+ it "should return a single association instance in an array if assocation isn't polymorphic" do
36
+ ThinkingSphinx::Association.children(Person, :assoc).should == [@normal_association]
37
+ end
38
+
39
+ it "should return multiple association instances for polymorphic associations" do
40
+ Person.stub!(:reflect_on_association => @poly_reflection)
41
+
42
+ ThinkingSphinx::Association.children(Person, :assoc).should ==
43
+ [@normal_association, @normal_association]
44
+ end
45
+
46
+ it "should generate non-polymorphic 'casted' associations for each polymorphic possibility" do
47
+ Person.stub!(:reflect_on_association => @poly_reflection)
48
+ ThinkingSphinx::Association.should_receive(:casted_options).with(
49
+ Person, @poly_reflection
50
+ ).twice
51
+ ::ActiveRecord::Reflection::AssociationReflection.should_receive(:new).
52
+ with(:has_many, :polly_Person, {:casted => :options}, "AR").twice
53
+ ThinkingSphinx::Association.should_receive(:new).with(
54
+ nil, @non_poly_reflection
55
+ ).twice
56
+
57
+ ThinkingSphinx::Association.children(Person, :assoc)
58
+ end
59
+ end
60
+
61
+ describe '#children' do
62
+ before :each do
63
+ @reflection = stub('reflection', :klass => :klass)
64
+ @association = ThinkingSphinx::Association.new(nil, @reflection)
65
+ ThinkingSphinx::Association.stub!(:children => :result)
66
+ end
67
+
68
+ it "should return the children associations for the given association" do
69
+ @association.children(:assoc).should == :result
70
+ end
71
+
72
+ it "should request children for the reflection klass" do
73
+ ThinkingSphinx::Association.should_receive(:children).
74
+ with(:klass, :assoc, @association)
75
+
76
+ @association.children(:assoc)
77
+ end
78
+ end
79
+
80
+ describe '#join_to' do
81
+ before :each do
82
+ @parent_join = stub('join assoc').as_null_object
83
+ @join = stub('join assoc').as_null_object
84
+ @parent = ThinkingSphinx::Association.new(nil, nil)
85
+ @parent.stub!(:join_to => true, :join => nil)
86
+ @base_join = stub('base join', :joins => [:a, :b, :c])
87
+ ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.stub!(:new => @join)
88
+ end
89
+
90
+ it "should call the parent's join_to if parent has no join" do
91
+ @assoc = ThinkingSphinx::Association.new(@parent, :ref)
92
+ @parent.should_receive(:join_to).with(@base_join)
93
+
94
+ @assoc.join_to(@base_join)
95
+ end
96
+
97
+ it "should not call the parent's join_to if it already has a join" do
98
+ @assoc = ThinkingSphinx::Association.new(@parent, :ref)
99
+ @parent.stub!(:join => @parent_join)
100
+ @parent.should_not_receive(:join_to)
101
+
102
+ @assoc.join_to(@base_join)
103
+ end
104
+
105
+ it "should define the join association with a JoinAssociation instance" do
106
+ @assoc = ThinkingSphinx::Association.new(@parent, :ref)
107
+
108
+ @assoc.join_to(@base_join).should == @join
109
+ @assoc.join.should == @join
110
+ end
111
+ end
112
+
113
+ describe '#to_sql' do
114
+ before :each do
115
+ @reflection = stub('reflection', :klass => Person)
116
+ @association = ThinkingSphinx::Association.new(nil, @reflection)
117
+ @parent = stub('parent', :aliased_table_name => "ALIAS TABLE NAME")
118
+ @join = stub('join assoc',
119
+ :association_join => "full association join SQL",
120
+ :parent => @parent
121
+ )
122
+ @association.join = @join
123
+ end
124
+
125
+ it "should return the join's association join value" do
126
+ @association.to_sql.should == "full association join SQL"
127
+ end
128
+
129
+ it "should replace ::ts_join_alias:: with the aliased table name" do
130
+ @join.stub!(:association_join => "text with ::ts_join_alias:: gone")
131
+
132
+ @association.to_sql.should == "text with `ALIAS TABLE NAME` gone"
133
+ end
134
+ end
135
+
136
+ describe '#is_many?' do
137
+ before :each do
138
+ @parent = stub('assoc', :is_many? => :parent_is_many)
139
+ @reflection = stub('reflection', :macro => :has_many)
140
+ end
141
+
142
+ it "should return true if association is either a has_many or a habtm" do
143
+ association = ThinkingSphinx::Association.new(@parent, @reflection)
144
+ association.is_many?.should be_true
145
+
146
+ @reflection.stub!(:macro => :has_and_belongs_to_many)
147
+ association.is_many?.should be_true
148
+ end
149
+
150
+ it "should return the parent value if not a has many or habtm and there is a parent" do
151
+ association = ThinkingSphinx::Association.new(@parent, @reflection)
152
+ @reflection.stub!(:macro => :belongs_to)
153
+ association.is_many?.should == :parent_is_many
154
+ end
155
+
156
+ it "should return false if no parent and not a has many or habtm" do
157
+ association = ThinkingSphinx::Association.new(nil, @reflection)
158
+ @reflection.stub!(:macro => :belongs_to)
159
+ association.is_many?.should be_false
160
+ end
161
+ end
162
+
163
+ describe '#ancestors' do
164
+ it "should return an array of associations - including all parents" do
165
+ parent = stub('assoc', :ancestors => [:all, :ancestors])
166
+ association = ThinkingSphinx::Association.new(parent, @reflection)
167
+ association.ancestors.should == [:all, :ancestors, association]
168
+ end
169
+ end
170
+
171
+ describe '.polymorphic_classes' do
172
+ it "should return all the polymorphic result types as classes" do
173
+ Person.connection.stub!(:select_all => [
174
+ {"person_type" => "Person"},
175
+ {"person_type" => "Friendship"}
176
+ ])
177
+ ref = stub('ref',
178
+ :active_record => Person,
179
+ :options => {:foreign_type => "person_type"}
180
+ )
181
+
182
+ ThinkingSphinx::Association.send(:polymorphic_classes, ref).should == [Person, Friendship]
183
+ end
184
+ end
185
+
186
+ describe '.casted_options' do
187
+ before :each do
188
+ @options = {
189
+ :foreign_key => "thing_id",
190
+ :foreign_type => "thing_type",
191
+ :polymorphic => true
192
+ }
193
+ @reflection = stub('assoc reflection', :options => @options)
194
+ end
195
+
196
+ it "should return a new options set for a specific class" do
197
+ ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == {
198
+ :polymorphic => nil,
199
+ :class_name => "Person",
200
+ :foreign_key => "thing_id",
201
+ :foreign_type => "thing_type",
202
+ :conditions => "::ts_join_alias::.`thing_type` = 'Person'"
203
+ }
204
+ end
205
+
206
+ it "should append to existing Array of conditions" do
207
+ @options[:conditions] = ["first condition"]
208
+ ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == {
209
+ :polymorphic => nil,
210
+ :class_name => "Person",
211
+ :foreign_key => "thing_id",
212
+ :foreign_type => "thing_type",
213
+ :conditions => ["first condition", "::ts_join_alias::.`thing_type` = 'Person'"]
214
+ }
215
+ end
216
+
217
+ it "should merge to an existing Hash of conditions" do
218
+ @options[:conditions] = {"field" => "value"}
219
+ ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == {
220
+ :polymorphic => nil,
221
+ :class_name => "Person",
222
+ :foreign_key => "thing_id",
223
+ :foreign_type => "thing_type",
224
+ :conditions => {"field" => "value", "thing_type" => "Person"}
225
+ }
226
+ end
227
+
228
+ it "should append to an existing String of conditions" do
229
+ @options[:conditions] = "first condition"
230
+ ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == {
231
+ :polymorphic => nil,
232
+ :class_name => "Person",
233
+ :foreign_key => "thing_id",
234
+ :foreign_type => "thing_type",
235
+ :conditions => "first condition AND ::ts_join_alias::.`thing_type` = 'Person'"
236
+ }
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,500 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::Attribute do
4
+ before :each do
5
+ @index = ThinkingSphinx::Index.new(Person)
6
+ @source = ThinkingSphinx::Source.new(@index)
7
+
8
+ @index.delta_object = ThinkingSphinx::Deltas::DefaultDelta.new @index, @index.local_options
9
+ end
10
+
11
+ describe '#initialize' do
12
+ it 'raises if no columns are provided so that configuration errors are easier to track down' do
13
+ lambda {
14
+ ThinkingSphinx::Attribute.new(@source, [])
15
+ }.should raise_error(RuntimeError)
16
+ end
17
+
18
+ it 'raises if an element of the columns param is an integer - as happens when you use id instead of :id - so that configuration errors are easier to track down' do
19
+ lambda {
20
+ ThinkingSphinx::Attribute.new(@source, [1234])
21
+ }.should raise_error(RuntimeError)
22
+ end
23
+ end
24
+
25
+ describe "unique_name method" do
26
+ before :each do
27
+ @attribute = ThinkingSphinx::Attribute.new @source, [
28
+ stub('column', :__stack => [], :__name => "col_name")
29
+ ]
30
+ end
31
+
32
+ it "should use the alias if there is one" do
33
+ @attribute.alias = "alias"
34
+ @attribute.unique_name.should == "alias"
35
+ end
36
+
37
+ it "should use the alias if there's multiple columns" do
38
+ @attribute.columns << stub('column', :__stack => [], :__name => "col_name")
39
+ @attribute.unique_name.should be_nil
40
+
41
+ @attribute.alias = "alias"
42
+ @attribute.unique_name.should == "alias"
43
+ end
44
+
45
+ it "should use the column name if there's no alias and just one column" do
46
+ @attribute.unique_name.should == "col_name"
47
+ end
48
+ end
49
+
50
+ describe "column_with_prefix method" do
51
+ before :each do
52
+ @attribute = ThinkingSphinx::Attribute.new @source, [
53
+ ThinkingSphinx::Index::FauxColumn.new(:col_name)
54
+ ]
55
+ @attribute.columns.each { |col| @attribute.associations[col] = [] }
56
+ @attribute.model = Person
57
+
58
+ @first_join = Object.new
59
+ @first_join.stub!(:aliased_table_name => "tabular")
60
+ @second_join = Object.new
61
+ @second_join.stub!(:aliased_table_name => "data")
62
+
63
+ @first_assoc = ThinkingSphinx::Association.new nil, nil
64
+ @first_assoc.stub!(:join => @first_join, :has_column? => true)
65
+ @second_assoc = ThinkingSphinx::Association.new nil, nil
66
+ @second_assoc.stub!(:join => @second_join, :has_column? => true)
67
+ end
68
+
69
+ it "should return the column name if the column is a string" do
70
+ @attribute.columns = [ThinkingSphinx::Index::FauxColumn.new("string")]
71
+ @attribute.send(:column_with_prefix, @attribute.columns.first).should == "string"
72
+ end
73
+
74
+ it "should return the column with model's table prefix if there's no associations for the column" do
75
+ @attribute.send(:column_with_prefix, @attribute.columns.first).should == "`people`.`col_name`"
76
+ end
77
+
78
+ it "should return the column with its join table prefix if an association exists" do
79
+ column = @attribute.columns.first
80
+ @attribute.associations[column] = [@first_assoc]
81
+ @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`"
82
+ end
83
+
84
+ it "should return multiple columns concatenated if more than one association exists" do
85
+ column = @attribute.columns.first
86
+ @attribute.associations[column] = [@first_assoc, @second_assoc]
87
+ @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`, `data`.`col_name`"
88
+ end
89
+ end
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
+
104
+ describe "is_many? method" do
105
+ before :each do
106
+ @assoc_a = stub('assoc', :is_many? => true)
107
+ @assoc_b = stub('assoc', :is_many? => true)
108
+ @assoc_c = stub('assoc', :is_many? => true)
109
+
110
+ @attribute = ThinkingSphinx::Attribute.new(
111
+ @source, [ThinkingSphinx::Index::FauxColumn.new(:col_name)]
112
+ )
113
+ @attribute.associations = {
114
+ :a => @assoc_a, :b => @assoc_b, :c => @assoc_c
115
+ }
116
+ end
117
+
118
+ it "should return true if all associations return true to is_many?" do
119
+ @attribute.send(:is_many?).should be_true
120
+ end
121
+
122
+ it "should return true if one association returns true to is_many?" do
123
+ @assoc_b.stub!(:is_many? => false)
124
+ @assoc_c.stub!(:is_many? => false)
125
+
126
+ @attribute.send(:is_many?).should be_true
127
+ end
128
+
129
+ it "should return false if all associations return false to is_many?" do
130
+ @assoc_a.stub!(:is_many? => false)
131
+ @assoc_b.stub!(:is_many? => false)
132
+ @assoc_c.stub!(:is_many? => false)
133
+
134
+ @attribute.send(:is_many?).should be_false
135
+ end
136
+ end
137
+
138
+ describe "is_string? method" do
139
+ before :each do
140
+ @col_a = ThinkingSphinx::Index::FauxColumn.new("a")
141
+ @col_b = ThinkingSphinx::Index::FauxColumn.new("b")
142
+ @col_c = ThinkingSphinx::Index::FauxColumn.new("c")
143
+
144
+ @attribute = ThinkingSphinx::Attribute.new(
145
+ @source, [@col_a, @col_b, @col_c]
146
+ )
147
+ end
148
+
149
+ it "should return true if all columns return true to is_string?" do
150
+ @attribute.send(:is_string?).should be_true
151
+ end
152
+
153
+ it "should return false if one column returns true to is_string?" do
154
+ @col_a.send(:instance_variable_set, :@name, :a)
155
+ @attribute.send(:is_string?).should be_false
156
+ end
157
+
158
+ it "should return false if all columns return false to is_string?" do
159
+ @col_a.send(:instance_variable_set, :@name, :a)
160
+ @col_b.send(:instance_variable_set, :@name, :b)
161
+ @col_c.send(:instance_variable_set, :@name, :c)
162
+ @attribute.send(:is_string?).should be_false
163
+ end
164
+ end
165
+
166
+ describe "type method" do
167
+ before :each do
168
+ @column = ThinkingSphinx::Index::FauxColumn.new(:col_name)
169
+ @attribute = ThinkingSphinx::Attribute.new(@source, [@column])
170
+ @attribute.model = Person
171
+ @attribute.stub!(:is_many? => false)
172
+ end
173
+
174
+ it "should return :multi if is_many? is true" do
175
+ @attribute.stub!(:is_many? => true)
176
+ @attribute.send(:type).should == :multi
177
+ end
178
+
179
+ it "should return :string if there's more than one association" do
180
+ @attribute.associations = {:a => [:assoc], :b => [:assoc]}
181
+ @attribute.send(:type).should == :string
182
+ end
183
+
184
+ it "should return the column type from the database if not :multi or more than one association" do
185
+ @column.send(:instance_variable_set, :@name, "birthday")
186
+ @attribute.send(:type).should == :datetime
187
+
188
+ @attribute.send(:instance_variable_set, :@type, nil)
189
+ @column.send(:instance_variable_set, :@name, "first_name")
190
+ @attribute.send(:type).should == :string
191
+
192
+ @attribute.send(:instance_variable_set, :@type, nil)
193
+ @column.send(:instance_variable_set, :@name, "id")
194
+ @attribute.send(:type).should == :integer
195
+ end
196
+ end
197
+
198
+ describe "all_ints? method" do
199
+ it "should return true if all columns are integers" do
200
+ attribute = ThinkingSphinx::Attribute.new(@source,
201
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
202
+ ThinkingSphinx::Index::FauxColumn.new(:team_id) ]
203
+ )
204
+ attribute.model = Person
205
+ attribute.columns.each { |col| attribute.associations[col] = [] }
206
+
207
+ attribute.should be_all_ints
208
+ end
209
+
210
+ it "should return false if only some columns are integers" do
211
+ attribute = ThinkingSphinx::Attribute.new(@source,
212
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
213
+ ThinkingSphinx::Index::FauxColumn.new(:first_name) ]
214
+ )
215
+ attribute.model = Person
216
+ attribute.columns.each { |col| attribute.associations[col] = [] }
217
+
218
+ attribute.should_not be_all_ints
219
+ end
220
+
221
+ it "should return false if no columns are integers" do
222
+ attribute = ThinkingSphinx::Attribute.new(@source,
223
+ [ ThinkingSphinx::Index::FauxColumn.new(:first_name),
224
+ ThinkingSphinx::Index::FauxColumn.new(:last_name) ]
225
+ )
226
+ attribute.model = Person
227
+ attribute.columns.each { |col| attribute.associations[col] = [] }
228
+
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
265
+ end
266
+ end
267
+
268
+ describe '#all_strings?' do
269
+ it "should return true if all columns are strings or text" do
270
+ attribute = ThinkingSphinx::Attribute.new(@source,
271
+ [ ThinkingSphinx::Index::FauxColumn.new(:first_name),
272
+ ThinkingSphinx::Index::FauxColumn.new(:last_name) ]
273
+ )
274
+ attribute.model = Person
275
+ attribute.columns.each { |col| attribute.associations[col] = [] }
276
+
277
+ attribute.should be_all_strings
278
+ end
279
+
280
+ it "should return false if only some columns are strings" do
281
+ attribute = ThinkingSphinx::Attribute.new(@source,
282
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
283
+ ThinkingSphinx::Index::FauxColumn.new(:first_name) ]
284
+ )
285
+ attribute.model = Person
286
+ attribute.columns.each { |col| attribute.associations[col] = [] }
287
+
288
+ attribute.should_not be_all_strings
289
+ end
290
+
291
+ it "should return true if all columns are not strings" do
292
+ attribute = ThinkingSphinx::Attribute.new(@source,
293
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
294
+ ThinkingSphinx::Index::FauxColumn.new(:parent_id) ]
295
+ )
296
+ attribute.model = Person
297
+ attribute.columns.each { |col| attribute.associations[col] = [] }
298
+
299
+ attribute.should_not be_all_strings
300
+ end
301
+ end
302
+
303
+ describe "MVA with source query" do
304
+ before :each do
305
+ @attribute = ThinkingSphinx::Attribute.new(@source,
306
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
307
+ :as => :tag_ids, :source => :query
308
+ )
309
+ end
310
+
311
+ it "should use a query" do
312
+ @attribute.type_to_config.should == :sql_attr_multi
313
+
314
+ declaration, query = @attribute.config_value.split('; ')
315
+ declaration.should == "uint tag_ids from query"
316
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags`"
317
+ end
318
+ end
319
+
320
+ describe "MVA with source query for a delta source" do
321
+ before :each do
322
+ @attribute = ThinkingSphinx::Attribute.new(@source,
323
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
324
+ :as => :tag_ids, :source => :query
325
+ )
326
+ end
327
+
328
+ it "should use a query" do
329
+ @attribute.type_to_config.should == :sql_attr_multi
330
+
331
+ declaration, query = @attribute.config_value(nil, true).split('; ')
332
+ declaration.should == "uint tag_ids from query"
333
+ 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)"
334
+ end
335
+ end
336
+
337
+ describe "MVA via a HABTM association with a source query" do
338
+ before :each do
339
+ @attribute = ThinkingSphinx::Attribute.new(@source,
340
+ [ThinkingSphinx::Index::FauxColumn.new(:links, :id)],
341
+ :as => :link_ids, :source => :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 = @attribute.config_value.split('; ')
349
+ declaration.should == "uint link_ids from query"
350
+ query.should == "SELECT `links_people`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `links_people`.`link_id` AS `link_ids` FROM `links_people`"
351
+ end
352
+ end
353
+
354
+ describe "MVA with ranged source query" do
355
+ before :each do
356
+ @attribute = ThinkingSphinx::Attribute.new(@source,
357
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
358
+ :as => :tag_ids, :source => :ranged_query
359
+ )
360
+ end
361
+
362
+ it "should use a ranged query" do
363
+ @attribute.type_to_config.should == :sql_attr_multi
364
+
365
+ declaration, query, range_query = @attribute.config_value.split('; ')
366
+ declaration.should == "uint tag_ids from ranged-query"
367
+ 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"
368
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
369
+ end
370
+ end
371
+
372
+ describe "MVA with ranged source query for a delta source" do
373
+ before :each do
374
+ @attribute = ThinkingSphinx::Attribute.new(@source,
375
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
376
+ :as => :tag_ids, :source => :ranged_query
377
+ )
378
+ end
379
+
380
+ it "should use a ranged query" do
381
+ @attribute.type_to_config.should == :sql_attr_multi
382
+
383
+ declaration, query, range_query = @attribute.config_value(nil, true).split('; ')
384
+ declaration.should == "uint tag_ids from ranged-query"
385
+ 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)"
386
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
387
+ end
388
+ end
389
+
390
+ describe "MVA via a has-many :through with a ranged source query" do
391
+ before :each do
392
+ @attribute = ThinkingSphinx::Attribute.new(@source,
393
+ [ThinkingSphinx::Index::FauxColumn.new(:football_teams, :id)],
394
+ :as => :football_team_ids, :source => :ranged_query
395
+ )
396
+ end
397
+
398
+ it "should use a ranged query" do
399
+ @attribute.type_to_config.should == :sql_attr_multi
400
+
401
+ declaration, query, range_query = @attribute.config_value.split('; ')
402
+ declaration.should == "uint football_team_ids from ranged-query"
403
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`football_team_id` AS `football_team_ids` FROM `tags` WHERE `tags`.`person_id` >= $start AND `tags`.`person_id` <= $end"
404
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
405
+ end
406
+ end
407
+
408
+ describe "MVA via a has-many :through using a foreign key with a ranged source query" do
409
+ before :each do
410
+ @attribute = ThinkingSphinx::Attribute.new(@source,
411
+ [ThinkingSphinx::Index::FauxColumn.new(:friends, :id)],
412
+ :as => :friend_ids, :source => :ranged_query
413
+ )
414
+ end
415
+
416
+ it "should use a ranged query" do
417
+ @attribute.type_to_config.should == :sql_attr_multi
418
+
419
+ declaration, query, range_query = @attribute.config_value.split('; ')
420
+ declaration.should == "uint friend_ids from ranged-query"
421
+ query.should == "SELECT `friendships`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `friendships`.`friend_id` AS `friend_ids` FROM `friendships` WHERE `friendships`.`person_id` >= $start AND `friendships`.`person_id` <= $end"
422
+ range_query.should == "SELECT MIN(`friendships`.`person_id`), MAX(`friendships`.`person_id`) FROM `friendships`"
423
+ end
424
+ end
425
+
426
+ describe "MVA via a HABTM with a ranged source query" do
427
+ before :each do
428
+ @attribute = ThinkingSphinx::Attribute.new(@source,
429
+ [ThinkingSphinx::Index::FauxColumn.new(:links, :id)],
430
+ :as => :link_ids, :source => :ranged_query
431
+ )
432
+ end
433
+
434
+ it "should use a ranged query" do
435
+ @attribute.type_to_config.should == :sql_attr_multi
436
+
437
+ declaration, query, range_query = @attribute.config_value.split('; ')
438
+ declaration.should == "uint link_ids from ranged-query"
439
+ query.should == "SELECT `links_people`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `links_people`.`link_id` AS `link_ids` FROM `links_people` WHERE `links_people`.`person_id` >= $start AND `links_people`.`person_id` <= $end"
440
+ range_query.should == "SELECT MIN(`links_people`.`person_id`), MAX(`links_people`.`person_id`) FROM `links_people`"
441
+ end
442
+ end
443
+
444
+ describe "MVA via two has-many associations with a ranged source query" do
445
+ before :each do
446
+ @index = ThinkingSphinx::Index.new(Alpha)
447
+ @source = ThinkingSphinx::Source.new(@index)
448
+ @attribute = ThinkingSphinx::Attribute.new(@source,
449
+ [ThinkingSphinx::Index::FauxColumn.new(:betas, :gammas, :value)],
450
+ :as => :gamma_values, :source => :ranged_query
451
+ )
452
+ end
453
+
454
+ it "should use a ranged query" do
455
+ @attribute.type_to_config.should == :sql_attr_multi
456
+
457
+ declaration, query, range_query = @attribute.config_value.split('; ')
458
+ declaration.should == "uint gamma_values from ranged-query"
459
+ 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"
460
+ range_query.should == "SELECT MIN(`betas`.`alpha_id`), MAX(`betas`.`alpha_id`) FROM `betas`"
461
+ end
462
+ end
463
+
464
+ describe "MVA via two has-many associations with a ranged source query for a delta source" do
465
+ before :each do
466
+ @index = ThinkingSphinx::Index.new(Alpha)
467
+ @source = ThinkingSphinx::Source.new(@index)
468
+ @attribute = ThinkingSphinx::Attribute.new(@source,
469
+ [ThinkingSphinx::Index::FauxColumn.new(:betas, :gammas, :value)],
470
+ :as => :gamma_values, :source => :ranged_query
471
+ )
472
+
473
+ @index.delta_object = ThinkingSphinx::Deltas::DefaultDelta.new @index, @index.local_options
474
+ end
475
+
476
+ it "should use a ranged query" do
477
+ @attribute.type_to_config.should == :sql_attr_multi
478
+
479
+ declaration, query, range_query = @attribute.config_value(nil, true).split('; ')
480
+ declaration.should == "uint gamma_values from ranged-query"
481
+ 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)"
482
+ range_query.should == "SELECT MIN(`betas`.`alpha_id`), MAX(`betas`.`alpha_id`) FROM `betas`"
483
+ end
484
+ end
485
+
486
+ describe "with custom queries" do
487
+ before :each do
488
+ index = CricketTeam.sphinx_indexes.first
489
+ @statement = index.sources.first.to_riddle_for_core(0, 0).sql_attr_multi.last
490
+ end
491
+
492
+ it "should track the query type accordingly" do
493
+ @statement.should match(/uint tags from query/)
494
+ end
495
+
496
+ it "should include the SQL statement" do
497
+ @statement.should match(/SELECT cricket_team_id, id FROM tags/)
498
+ end
499
+ end
500
+ end