freelancing-god-thinking-sphinx 1.1.12 → 1.1.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,124 @@
1
+ module ThinkingSphinx
2
+ class Source
3
+ module SQL
4
+ # Generates the big SQL statement to get the data back for all the fields
5
+ # and attributes, using all the relevant association joins. If you want
6
+ # the version filtered for delta values, send through :delta => true in the
7
+ # options. Won't do much though if the index isn't set up to support a
8
+ # delta sibling.
9
+ #
10
+ # Examples:
11
+ #
12
+ # source.to_sql
13
+ # source.to_sql(:delta => true)
14
+ #
15
+ def to_sql(options={})
16
+ sql = <<-SQL
17
+ SELECT #{ sql_select_clause options[:offset] }
18
+ FROM #{ @model.quoted_table_name }
19
+ #{ all_associations.collect { |assoc| assoc.to_sql }.join(' ') }
20
+ WHERE #{ sql_where_clause(options) }
21
+ GROUP BY #{ sql_group_clause }
22
+ SQL
23
+
24
+ sql += " ORDER BY NULL" if adapter.sphinx_identifier == "mysql"
25
+ sql
26
+ end
27
+
28
+ # Simple helper method for the query range SQL - which is a statement that
29
+ # returns minimum and maximum id values. These can be filtered by delta -
30
+ # so pass in :delta => true to get the delta version of the SQL.
31
+ #
32
+ def to_sql_query_range(options={})
33
+ min_statement = adapter.convert_nulls(
34
+ "MIN(#{quote_column(@model.primary_key)})", 1
35
+ )
36
+ max_statement = adapter.convert_nulls(
37
+ "MAX(#{quote_column(@model.primary_key)})", 1
38
+ )
39
+
40
+ sql = "SELECT #{min_statement}, #{max_statement} " +
41
+ "FROM #{@model.quoted_table_name} "
42
+ if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
43
+ sql << "WHERE #{@index.delta_object.clause(@model, options[:delta])}"
44
+ end
45
+
46
+ sql
47
+ end
48
+
49
+ # Simple helper method for the query info SQL - which is a statement that
50
+ # returns the single row for a corresponding id.
51
+ #
52
+ def to_sql_query_info(offset)
53
+ "SELECT * FROM #{@model.quoted_table_name} WHERE " +
54
+ "#{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
55
+ end
56
+
57
+ def sql_select_clause(offset)
58
+ unique_id_expr = ThinkingSphinx.unique_id_expression(offset)
59
+
60
+ (
61
+ ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] +
62
+ @fields.collect { |field| field.to_select_sql } +
63
+ @attributes.collect { |attribute| attribute.to_select_sql }
64
+ ).compact.join(", ")
65
+ end
66
+
67
+ def sql_where_clause(options)
68
+ logic = [
69
+ "#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} >= $start",
70
+ "#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} <= $end"
71
+ ]
72
+
73
+ if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
74
+ logic << "#{@index.delta_object.clause(@model, options[:delta])}"
75
+ end
76
+
77
+ logic += (@conditions || [])
78
+
79
+ logic.join(" AND ")
80
+ end
81
+
82
+ def sql_group_clause
83
+ internal_groupings = []
84
+ if @model.column_names.include?(@model.inheritance_column)
85
+ internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}"
86
+ end
87
+
88
+ (
89
+ ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] +
90
+ @fields.collect { |field| field.to_group_sql }.compact +
91
+ @attributes.collect { |attribute| attribute.to_group_sql }.compact +
92
+ @groupings + internal_groupings
93
+ ).join(", ")
94
+ end
95
+
96
+ def sql_query_pre_for_core
97
+ if self.delta? && !@index.delta_object.reset_query(@model).blank?
98
+ [@index.delta_object.reset_query(@model)]
99
+ else
100
+ []
101
+ end
102
+ end
103
+
104
+ def sql_query_pre_for_delta
105
+ [""]
106
+ end
107
+
108
+ def quote_column(column)
109
+ @model.connection.quote_column_name(column)
110
+ end
111
+
112
+ def crc_column
113
+ if @model.column_names.include?(@model.inheritance_column)
114
+ adapter.cast_to_unsigned(adapter.convert_nulls(
115
+ adapter.crc(adapter.quote_with_table(@model.inheritance_column), true),
116
+ @model.to_crc32
117
+ ))
118
+ else
119
+ @model.to_crc32.to_s
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -30,7 +30,7 @@ namespace :thinking_sphinx do
30
30
  if sphinx_running?
31
31
  puts "Started successfully (pid #{sphinx_pid})."
32
32
  else
33
- puts "Failed to start searchd daemon. Check #{config.searchd_log_file}."
33
+ puts "Failed to start searchd daemon. Check #{config.searchd_log_file}"
34
34
  end
35
35
  end
36
36
 
@@ -14,7 +14,7 @@ describe "ThinkingSphinx::ActiveRecord" do
14
14
  )
15
15
 
16
16
  @index = ThinkingSphinx::Index.stub_instance(:delta? => false)
17
- ThinkingSphinx::Index.stub_method(:new => @index)
17
+ ThinkingSphinx::Index::Builder.stub_method(:generate => @index)
18
18
  end
19
19
 
20
20
  after :each do
@@ -24,17 +24,17 @@ describe "ThinkingSphinx::ActiveRecord" do
24
24
  ThinkingSphinx.indexed_models.delete "TestModule::TestModel"
25
25
  end
26
26
 
27
- it "should return nil and do nothing if indexes are disabled" do
27
+ it "should do nothing if indexes are disabled" do
28
28
  ThinkingSphinx.stub_method(:define_indexes? => false)
29
29
 
30
- TestModule::TestModel.define_index {}.should be_nil
30
+ TestModule::TestModel.define_index {}
31
31
  ThinkingSphinx::Index.should_not have_received(:new)
32
32
 
33
33
  ThinkingSphinx.unstub_method(:define_indexes?)
34
34
  end
35
35
 
36
36
  it "should add a new index to the model" do
37
- TestModule::TestModel.define_index do; end
37
+ TestModule::TestModel.define_index {}
38
38
 
39
39
  TestModule::TestModel.sphinx_indexes.length.should == 1
40
40
  end
@@ -144,7 +144,7 @@ describe "ThinkingSphinx::ActiveRecord" do
144
144
  end
145
145
 
146
146
  it "should return the parent if model inherits an index" do
147
- Parent.source_of_sphinx_index.should == Person
147
+ Admin::Person.source_of_sphinx_index.should == Person
148
148
  end
149
149
  end
150
150
 
@@ -289,10 +289,12 @@ describe "ThinkingSphinx::ActiveRecord" do
289
289
  end
290
290
 
291
291
  it "should allow associations to other STI models" do
292
- Child.sphinx_indexes.last.link!
293
- sql = Child.sphinx_indexes.last.to_riddle_for_core(0, 0).sql_query
292
+ source = Child.sphinx_indexes.last.sources.first
293
+ sql = source.to_riddle_for_core(0, 0).sql_query
294
294
  sql.gsub!('$start', '0').gsub!('$end', '100')
295
- lambda { Child.connection.execute(sql) }.should_not raise_error(ActiveRecord::StatementInvalid)
295
+ lambda {
296
+ Child.connection.execute(sql)
297
+ }.should_not raise_error(ActiveRecord::StatementInvalid)
296
298
  end
297
299
  end
298
300
 
@@ -1,23 +1,28 @@
1
1
  require 'spec/spec_helper'
2
2
 
3
3
  describe ThinkingSphinx::Attribute do
4
+ before :each do
5
+ @index = ThinkingSphinx::Index.new(Person)
6
+ @source = ThinkingSphinx::Source.new(@index)
7
+ end
8
+
4
9
  describe '#initialize' do
5
10
  it 'raises if no columns are provided so that configuration errors are easier to track down' do
6
11
  lambda {
7
- ThinkingSphinx::Attribute.new([])
12
+ ThinkingSphinx::Attribute.new(@source, [])
8
13
  }.should raise_error(RuntimeError)
9
14
  end
10
15
 
11
16
  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
12
17
  lambda {
13
- ThinkingSphinx::Attribute.new([1234])
18
+ ThinkingSphinx::Attribute.new(@source, [1234])
14
19
  }.should raise_error(RuntimeError)
15
20
  end
16
21
  end
17
22
 
18
23
  describe "unique_name method" do
19
24
  before :each do
20
- @attribute = ThinkingSphinx::Attribute.new [
25
+ @attribute = ThinkingSphinx::Attribute.new @source, [
21
26
  Object.stub_instance(:__stack => [], :__name => "col_name")
22
27
  ]
23
28
  end
@@ -42,7 +47,7 @@ describe ThinkingSphinx::Attribute do
42
47
 
43
48
  describe "column_with_prefix method" do
44
49
  before :each do
45
- @attribute = ThinkingSphinx::Attribute.new [
50
+ @attribute = ThinkingSphinx::Attribute.new @source, [
46
51
  ThinkingSphinx::Index::FauxColumn.new(:col_name)
47
52
  ]
48
53
  @attribute.columns.each { |col| @attribute.associations[col] = [] }
@@ -88,7 +93,7 @@ describe ThinkingSphinx::Attribute do
88
93
  @assoc_c = Object.stub_instance(:is_many? => true)
89
94
 
90
95
  @attribute = ThinkingSphinx::Attribute.new(
91
- [ThinkingSphinx::Index::FauxColumn.new(:col_name)]
96
+ @source, [ThinkingSphinx::Index::FauxColumn.new(:col_name)]
92
97
  )
93
98
  @attribute.associations = {
94
99
  :a => @assoc_a, :b => @assoc_b, :c => @assoc_c
@@ -122,7 +127,7 @@ describe ThinkingSphinx::Attribute do
122
127
  @col_c = ThinkingSphinx::Index::FauxColumn.new("c")
123
128
 
124
129
  @attribute = ThinkingSphinx::Attribute.new(
125
- [@col_a, @col_b, @col_c]
130
+ @source, [@col_a, @col_b, @col_c]
126
131
  )
127
132
  end
128
133
 
@@ -146,7 +151,7 @@ describe ThinkingSphinx::Attribute do
146
151
  describe "type method" do
147
152
  before :each do
148
153
  @column = ThinkingSphinx::Index::FauxColumn.new(:col_name)
149
- @attribute = ThinkingSphinx::Attribute.new([@column])
154
+ @attribute = ThinkingSphinx::Attribute.new(@source, [@column])
150
155
  @attribute.model = Person
151
156
  @attribute.stub_method(:is_many? => false)
152
157
  end
@@ -177,7 +182,7 @@ describe ThinkingSphinx::Attribute do
177
182
 
178
183
  describe "all_ints? method" do
179
184
  it "should return true if all columns are integers" do
180
- attribute = ThinkingSphinx::Attribute.new(
185
+ attribute = ThinkingSphinx::Attribute.new(@source,
181
186
  [ ThinkingSphinx::Index::FauxColumn.new(:id),
182
187
  ThinkingSphinx::Index::FauxColumn.new(:team_id) ]
183
188
  )
@@ -188,7 +193,7 @@ describe ThinkingSphinx::Attribute do
188
193
  end
189
194
 
190
195
  it "should return false if only some columns are integers" do
191
- attribute = ThinkingSphinx::Attribute.new(
196
+ attribute = ThinkingSphinx::Attribute.new(@source,
192
197
  [ ThinkingSphinx::Index::FauxColumn.new(:id),
193
198
  ThinkingSphinx::Index::FauxColumn.new(:first_name) ]
194
199
  )
@@ -199,7 +204,7 @@ describe ThinkingSphinx::Attribute do
199
204
  end
200
205
 
201
206
  it "should return false if no columns are integers" do
202
- attribute = ThinkingSphinx::Attribute.new(
207
+ attribute = ThinkingSphinx::Attribute.new(@source,
203
208
  [ ThinkingSphinx::Index::FauxColumn.new(:first_name),
204
209
  ThinkingSphinx::Index::FauxColumn.new(:last_name) ]
205
210
  )
@@ -213,7 +218,7 @@ describe ThinkingSphinx::Attribute do
213
218
  describe "with custom queries" do
214
219
  before :each do
215
220
  index = CricketTeam.sphinx_indexes.first
216
- @statement = index.to_riddle_for_core(0, 0).sql_attr_multi.first
221
+ @statement = index.sources.first.to_riddle_for_core(0, 0).sql_attr_multi.last
217
222
  end
218
223
 
219
224
  it "should track the query type accordingly" do
@@ -43,4 +43,236 @@ describe ThinkingSphinx::Facet do
43
43
  ThinkingSphinx::Facet.attribute_name_for(ThinkingSphinx::Facet.name_for('attribute_facet')).should == 'attribute_facet'
44
44
  end
45
45
  end
46
+
47
+ describe ".attribute_name_from_value" do
48
+ it "should append _facet if the value is a string" do
49
+ ThinkingSphinx::Facet.attribute_name_from_value('attribute', 'string').
50
+ should == 'attribute_facet'
51
+ end
52
+
53
+ it "should not append _facet if the value isn't a string" do
54
+ ThinkingSphinx::Facet.attribute_name_from_value('attribute', 1).
55
+ should == 'attribute'
56
+ ThinkingSphinx::Facet.attribute_name_from_value('attribute', Time.now).
57
+ should == 'attribute'
58
+ ThinkingSphinx::Facet.attribute_name_from_value('attribute', true).
59
+ should == 'attribute'
60
+ ThinkingSphinx::Facet.attribute_name_from_value('attribute', 1.23).
61
+ should == 'attribute'
62
+ end
63
+
64
+ it "should append _facet is the value is an array of strings" do
65
+ ThinkingSphinx::Facet.attribute_name_from_value('attribute', ['a', 'b']).
66
+ should == 'attribute_facet'
67
+ end
68
+
69
+ it "should not append _facet if the value is an array of integers" do
70
+ ThinkingSphinx::Facet.attribute_name_from_value('attribute', [1, 2]).
71
+ should == 'attribute'
72
+ end
73
+ end
74
+
75
+ describe ".translate?" do
76
+ before :each do
77
+ @index = ThinkingSphinx::Index.new(Alpha)
78
+ @source = ThinkingSphinx::Source.new(@index)
79
+ @attribute = ThinkingSphinx::Attribute.new(
80
+ @source, ThinkingSphinx::Index::FauxColumn.new(:name)
81
+ )
82
+ end
83
+
84
+ it "should return true if the property is a field" do
85
+ field = ThinkingSphinx::Field.new(
86
+ @source, ThinkingSphinx::Index::FauxColumn.new(:name)
87
+ )
88
+
89
+ ThinkingSphinx::Facet.translate?(field).should be_true
90
+ end
91
+
92
+ it "should return true if the property is a string attribute" do
93
+ @attribute.stub_method(:type => :string)
94
+
95
+ ThinkingSphinx::Facet.translate?(@attribute).should be_true
96
+ end
97
+
98
+ it "should return false if the property is an integer attribute" do
99
+ @attribute.stub_method(:type => :integer)
100
+
101
+ ThinkingSphinx::Facet.translate?(@attribute).should be_false
102
+ end
103
+
104
+ it "should return false if the property is a boolean attribute" do
105
+ @attribute.stub_method(:type => :boolean)
106
+
107
+ ThinkingSphinx::Facet.translate?(@attribute).should be_false
108
+ end
109
+
110
+ it "should return false if the property is a timestamp attribute" do
111
+ @attribute.stub_method(:type => :datetime)
112
+
113
+ ThinkingSphinx::Facet.translate?(@attribute).should be_false
114
+ end
115
+
116
+ it "should return false if the property is a float attribute" do
117
+ @attribute.stub_method(:type => :float)
118
+
119
+ ThinkingSphinx::Facet.translate?(@attribute).should be_false
120
+ end
121
+
122
+ it "should return false if the property is an MVA of integer values" do
123
+ @attribute.stub_method(:type => :multi, :all_ints? => true)
124
+
125
+ ThinkingSphinx::Facet.translate?(@attribute).should be_false
126
+ end
127
+
128
+ it "should return true if the property is an MVA of string values" do
129
+ @attribute.stub_method(:type => :multi, :all_ints? => false)
130
+
131
+ ThinkingSphinx::Facet.translate?(@attribute).should be_true
132
+ end
133
+ end
134
+
135
+ describe "#translate?" do
136
+ before :each do
137
+ @index = ThinkingSphinx::Index.new(Alpha)
138
+ @source = ThinkingSphinx::Source.new(@index)
139
+ @attribute = ThinkingSphinx::Attribute.new(
140
+ @source, ThinkingSphinx::Index::FauxColumn.new(:name)
141
+ )
142
+ end
143
+
144
+ it "should return true if the property is a field" do
145
+ field = ThinkingSphinx::Field.new(
146
+ @source, ThinkingSphinx::Index::FauxColumn.new(:name)
147
+ )
148
+
149
+ ThinkingSphinx::Facet.new(field).translate?.should be_true
150
+ end
151
+
152
+ it "should return true if the property is a string attribute" do
153
+ @attribute.stub_method(:type => :string)
154
+
155
+ ThinkingSphinx::Facet.new(@attribute).translate?.should be_true
156
+ end
157
+
158
+ it "should return false if the property is an integer attribute" do
159
+ @attribute.stub_method(:type => :integer)
160
+
161
+ ThinkingSphinx::Facet.new(@attribute).translate?.should be_false
162
+ end
163
+
164
+ it "should return false if the property is a boolean attribute" do
165
+ @attribute.stub_method(:type => :boolean)
166
+
167
+ ThinkingSphinx::Facet.new(@attribute).translate?.should be_false
168
+ end
169
+
170
+ it "should return false if the property is a timestamp attribute" do
171
+ @attribute.stub_method(:type => :datetime)
172
+
173
+ ThinkingSphinx::Facet.new(@attribute).translate?.should be_false
174
+ end
175
+
176
+ it "should return false if the property is a float attribute" do
177
+ @attribute.stub_method(:type => :float)
178
+
179
+ ThinkingSphinx::Facet.new(@attribute).translate?.should be_false
180
+ end
181
+
182
+ it "should return false if the property is an MVA of integer values" do
183
+ @attribute.stub_method(:type => :multi, :all_ints? => true)
184
+
185
+ ThinkingSphinx::Facet.new(@attribute).translate?.should be_false
186
+ end
187
+
188
+ it "should return true if the property is an MVA of string values" do
189
+ @attribute.stub_method(:type => :multi, :all_ints? => false)
190
+
191
+ ThinkingSphinx::Facet.new(@attribute).translate?.should be_true
192
+ end
193
+ end
194
+
195
+ describe "#attribute_name" do
196
+ before :each do
197
+ @index = ThinkingSphinx::Index.new(Alpha)
198
+ @source = ThinkingSphinx::Source.new(@index)
199
+ @attribute = ThinkingSphinx::Attribute.new(
200
+ @source, ThinkingSphinx::Index::FauxColumn.new(:name)
201
+ )
202
+ end
203
+
204
+ it "should return the attribute name if built off an integer attribute" do
205
+ @attribute.stub_method(:type => :integer)
206
+
207
+ ThinkingSphinx::Facet.new(@attribute).attribute_name.should == "name"
208
+ end
209
+
210
+ it "should return the attribute name if built off a boolean attribute" do
211
+ @attribute.stub_method(:type => :boolean)
212
+
213
+ ThinkingSphinx::Facet.new(@attribute).attribute_name.should == "name"
214
+ end
215
+
216
+ it "should return the attribute name if built off a float attribute" do
217
+ @attribute.stub_method(:type => :float)
218
+
219
+ ThinkingSphinx::Facet.new(@attribute).attribute_name.should == "name"
220
+ end
221
+
222
+ it "should return the attribute name if built off a timestamp attribute" do
223
+ @attribute.stub_method(:type => :datetime)
224
+
225
+ ThinkingSphinx::Facet.new(@attribute).attribute_name.should == "name"
226
+ end
227
+
228
+ it "should return the attribute name with _facet suffix if built off a string attribute" do
229
+ @attribute.stub_method(:type => :string)
230
+
231
+ ThinkingSphinx::Facet.new(@attribute).attribute_name.should == "name_facet"
232
+ end
233
+
234
+ it "should return the attribute name with _facet suffix if built off a field" do
235
+ field = ThinkingSphinx::Field.new(
236
+ @source, ThinkingSphinx::Index::FauxColumn.new(:name)
237
+ )
238
+
239
+ ThinkingSphinx::Facet.new(field).attribute_name.should == "name_facet"
240
+ end
241
+
242
+ it "should return the attribute name if build off an integer MVA" do
243
+ @attribute.stub_method(:type => :multi, :all_ints? => true)
244
+
245
+ ThinkingSphinx::Facet.new(@attribute).attribute_name.should == "name"
246
+ end
247
+
248
+ it "should return the attribute name with the _facet suffix if build off an non-integer MVA" do
249
+ @attribute.stub_method(:type => :multi, :all_ints? => false)
250
+
251
+ ThinkingSphinx::Facet.new(@attribute).attribute_name.should == "name_facet"
252
+ end
253
+ end
254
+
255
+ describe "#type" do
256
+ before :each do
257
+ @index = ThinkingSphinx::Index.new(Alpha)
258
+ @source = ThinkingSphinx::Source.new(@index)
259
+ end
260
+
261
+ it "should return :string if the property is a field" do
262
+ field = ThinkingSphinx::Field.new(
263
+ @source, ThinkingSphinx::Index::FauxColumn.new(:name)
264
+ )
265
+
266
+ ThinkingSphinx::Facet.new(field).type.should == :string
267
+ end
268
+
269
+ it "should return the attribute type if the property is an attribute" do
270
+ attribute = ThinkingSphinx::Attribute.new(
271
+ @source, ThinkingSphinx::Index::FauxColumn.new(:name)
272
+ )
273
+ attribute.stub_method(:type => :anything)
274
+
275
+ ThinkingSphinx::Facet.new(attribute).type.should == :anything
276
+ end
277
+ end
46
278
  end