hariton-thinking-sphinx 1.2.7.1 → 1.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.textile CHANGED
@@ -154,3 +154,4 @@ Since I first released this library, there's been quite a few people who have su
154
154
  * Zach Inglis
155
155
  * Ward Bekker
156
156
  * Brian Terlson
157
+ * Christian Aust
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
+ :patch: 11
2
3
  :major: 1
3
4
  :minor: 2
4
- :patch: 7
@@ -7,6 +7,7 @@ require 'riddle'
7
7
  require 'after_commit'
8
8
  require 'yaml'
9
9
 
10
+ require 'thinking_sphinx/core/array'
10
11
  require 'thinking_sphinx/core/string'
11
12
  require 'thinking_sphinx/property'
12
13
  require 'thinking_sphinx/active_record'
@@ -155,6 +155,14 @@ module ThinkingSphinx
155
155
  self.name.underscore.tr(':/\\', '_')
156
156
  end
157
157
 
158
+ def sphinx_index_names
159
+ klass = source_of_sphinx_index
160
+ names = ["#{klass.sphinx_name}_core"]
161
+ names << "#{klass.sphinx_name}_delta" if sphinx_delta?
162
+
163
+ names
164
+ end
165
+
158
166
  private
159
167
 
160
168
  def sphinx_delta?
@@ -281,7 +289,7 @@ module ThinkingSphinx
281
289
  # @return [Integer] Unique record id for the purposes of Sphinx.
282
290
  #
283
291
  def primary_key_for_sphinx
284
- read_attribute(self.class.primary_key_for_sphinx)
292
+ @primary_key_for_sphinx ||= read_attribute(self.class.primary_key_for_sphinx)
285
293
  end
286
294
 
287
295
  def sphinx_document_id
@@ -9,8 +9,7 @@ module ThinkingSphinx
9
9
  (@reflection.klass.sphinx_indexes || []).each do |index|
10
10
  attribute = index.attributes.detect { |attrib|
11
11
  attrib.columns.length == 1 &&
12
- attrib.columns.first.__name == foreign_key.to_sym &&
13
- attrib.columns.first.__stack == stack
12
+ attrib.columns.first.__name == foreign_key.to_sym
14
13
  }
15
14
  break if attribute
16
15
  end
@@ -29,7 +29,7 @@ module ThinkingSphinx
29
29
  end
30
30
 
31
31
  def cast_to_datetime(clause)
32
- "cast(extract(epoch from #{clause}) as bigint)"
32
+ "cast(extract(epoch from #{clause}) as int)"
33
33
  end
34
34
 
35
35
  def cast_to_unsigned(clause)
@@ -75,7 +75,9 @@ module ThinkingSphinx
75
75
  @crc = options[:crc]
76
76
 
77
77
  @type ||= :multi unless @query_source.nil?
78
- @type = :integer if @type == :string && @crc
78
+ if @type == :string && @crc
79
+ @type = is_many? ? :multi : :integer
80
+ end
79
81
 
80
82
  source.attributes << self
81
83
  end
@@ -160,11 +162,12 @@ module ThinkingSphinx
160
162
  end
161
163
 
162
164
  if base_type == :string && @crc
163
- :integer
165
+ base_type = :integer
164
166
  else
165
- @crc = false
166
- base_type
167
+ @crc = false unless base_type == :multi && is_many_strings? && @crc
167
168
  end
169
+
170
+ base_type
168
171
  end
169
172
  end
170
173
 
@@ -187,6 +190,10 @@ module ThinkingSphinx
187
190
  all_of_type?(:datetime, :date, :timestamp)
188
191
  end
189
192
 
193
+ def all_strings?
194
+ all_of_type?(:string, :text)
195
+ end
196
+
190
197
  private
191
198
 
192
199
  def source_value(offset, delta)
@@ -284,6 +291,10 @@ WHERE #{@source.index.delta_object.clause(model, true)})
284
291
  def is_many_datetimes?
285
292
  is_many? && all_datetimes?
286
293
  end
294
+
295
+ def is_many_strings?
296
+ is_many? && all_strings?
297
+ end
287
298
 
288
299
  def type_from_database
289
300
  klass = @associations.values.flatten.first ?
@@ -0,0 +1,7 @@
1
+ module SearchAsArray
2
+ def ===(object)
3
+ object.is_a?(ThinkingSphinx::Search) || super
4
+ end
5
+ end
6
+
7
+ Array.extend SearchAsArray
@@ -72,7 +72,7 @@ module ThinkingSphinx
72
72
  end
73
73
 
74
74
  def value(object, attribute_value)
75
- return translate(object, attribute_value) if translate?
75
+ return translate(object, attribute_value) if translate? || float?
76
76
 
77
77
  case @property.type
78
78
  when :datetime
@@ -91,18 +91,35 @@ module ThinkingSphinx
91
91
  private
92
92
 
93
93
  def translate(object, attribute_value)
94
- column.__stack.each { |method|
95
- return nil unless object = object.send(method)
96
- }
97
- if object.is_a?(Array)
98
- object.collect { |item| item.send(column.__name) }
94
+ objects = source_objects(object)
95
+ return nil if objects.nil? || objects.empty?
96
+
97
+ if objects.length > 1
98
+ objects.collect { |item| item.send(column.__name) }.detect { |item|
99
+ item.to_crc32 == attribute_value
100
+ }
99
101
  else
100
- object.send(column.__name)
102
+ objects.first.send(column.__name)
101
103
  end
102
104
  end
103
105
 
106
+ def source_objects(object)
107
+ column.__stack.each { |method|
108
+ object = Array(object).collect { |item|
109
+ item.send(method)
110
+ }.flatten.compact
111
+
112
+ return nil if object.empty?
113
+ }
114
+ Array(object)
115
+ end
116
+
104
117
  def column
105
118
  @property.columns.first
106
119
  end
120
+
121
+ def float?
122
+ @property.type == :float
123
+ end
107
124
  end
108
125
  end
@@ -41,10 +41,10 @@ module ThinkingSphinx
41
41
  end
42
42
 
43
43
  def name
44
- self.class.name(@model)
44
+ self.class.name_for @model
45
45
  end
46
46
 
47
- def self.name(model)
47
+ def self.name_for(model)
48
48
  model.name.underscore.tr(':/\\', '_')
49
49
  end
50
50
 
@@ -198,7 +198,6 @@ module ThinkingSphinx
198
198
  # set_property :delta => true
199
199
  # set_property :field_weights => {"name" => 100}
200
200
  # set_property :order => "name ASC"
201
- # set_property :include => :picture
202
201
  # set_property :select => 'name'
203
202
  #
204
203
  # Also, the following two properties are particularly relevant for
@@ -14,6 +14,8 @@ module ThinkingSphinx
14
14
  @faceted = options[:facet]
15
15
  @admin = options[:admin]
16
16
 
17
+ @alias = @alias.to_sym unless @alias.blank?
18
+
17
19
  @columns.each { |col|
18
20
  @associations[col] = association_stack(col.__stack.clone).each { |assoc|
19
21
  assoc.join_to(source.base)
@@ -177,6 +177,13 @@ module ThinkingSphinx
177
177
  (current_page - 1) * per_page
178
178
  end
179
179
 
180
+ def indexes
181
+ return options[:index] if options[:index]
182
+ return '*' if classes.empty?
183
+
184
+ classes.collect { |klass| klass.sphinx_index_names }.flatten.join(',')
185
+ end
186
+
180
187
  def each_with_groupby_and_count(&block)
181
188
  populate
182
189
  results[:matches].each_with_index do |match, index|
@@ -194,15 +201,15 @@ module ThinkingSphinx
194
201
  end
195
202
 
196
203
  def excerpt_for(string, model = nil)
197
- if model.nil? && options[:classes].length == 1
198
- model ||= options[:classes].first
204
+ if model.nil? && one_class
205
+ model ||= one_class
199
206
  end
200
207
 
201
208
  populate
202
209
  client.excerpts(
203
210
  :docs => [string],
204
211
  :words => results[:words].keys.join(' '),
205
- :index => "#{model.name == model.class_name ? model.sphinx_name : model.class_name.underscore.tr(':/\\', '_')}_core"
212
+ :index => "#{model.source_of_sphinx_index.sphinx_name}_core"
206
213
  ).first
207
214
  end
208
215
 
@@ -224,7 +231,7 @@ module ThinkingSphinx
224
231
  retry_on_stale_index do
225
232
  begin
226
233
  log "Querying Sphinx: #{query}"
227
- @results = client.query query, index, comment
234
+ @results = client.query query, indexes, comment
228
235
  rescue Errno::ECONNREFUSED => err
229
236
  raise ThinkingSphinx::ConnectionError,
230
237
  'Connection to Sphinx Daemon (searchd) failed.'
@@ -266,8 +273,8 @@ module ThinkingSphinx
266
273
  def client
267
274
  client = config.client
268
275
 
269
- index_options = (options[:classes] || []).length != 1 ?
270
- {} : options[:classes].first.sphinx_indexes.first.local_options
276
+ index_options = one_class ?
277
+ one_class.sphinx_indexes.first.local_options : {}
271
278
 
272
279
  [
273
280
  :max_matches, :group_by, :group_function, :group_clause,
@@ -321,6 +328,14 @@ module ThinkingSphinx
321
328
  end
322
329
  end
323
330
 
331
+ def classes
332
+ @classes ||= options[:classes] || []
333
+ end
334
+
335
+ def one_class
336
+ @one_class ||= classes.length != 1 ? nil : classes.first
337
+ end
338
+
324
339
  def query
325
340
  @query ||= begin
326
341
  q = @args.join(' ') << conditions_as_query
@@ -333,7 +348,7 @@ module ThinkingSphinx
333
348
 
334
349
  # Soon to be deprecated.
335
350
  keys = @options[:conditions].keys.reject { |key|
336
- attributes.include?(key)
351
+ attributes.include?(key.to_sym)
337
352
  }
338
353
 
339
354
  ' ' + keys.collect { |key|
@@ -359,10 +374,6 @@ module ThinkingSphinx
359
374
  end
360
375
  end
361
376
 
362
- def index
363
- options[:index] || '*'
364
- end
365
-
366
377
  def comment
367
378
  options[:comment] || ''
368
379
  end
@@ -404,9 +415,9 @@ module ThinkingSphinx
404
415
  end
405
416
 
406
417
  def field_names
407
- return [] if (options[:classes] || []).length != 1
418
+ return [] unless one_class
408
419
 
409
- options[:classes].first.sphinx_indexes.collect { |index|
420
+ one_class.sphinx_indexes.collect { |index|
410
421
  index.fields.collect { |field| field.unique_name }
411
422
  }.flatten
412
423
  end
@@ -428,7 +439,7 @@ module ThinkingSphinx
428
439
  weights = options[:index_weights] || {}
429
440
  weights.keys.inject({}) do |hash, key|
430
441
  if key.is_a?(Class)
431
- name = ThinkingSphinx::Index.name(key)
442
+ name = ThinkingSphinx::Index.name_for(key)
432
443
  hash["#{name}_core"] = weights[key]
433
444
  hash["#{name}_delta"] = weights[key]
434
445
  else
@@ -450,7 +461,7 @@ module ThinkingSphinx
450
461
  def internal_filters
451
462
  filters = [Riddle::Client::Filter.new('sphinx_deleted', [0])]
452
463
 
453
- class_crcs = (options[:classes] || []).collect { |klass|
464
+ class_crcs = classes.collect { |klass|
454
465
  klass.to_crc32s
455
466
  }.flatten
456
467
 
@@ -467,7 +478,7 @@ module ThinkingSphinx
467
478
 
468
479
  def condition_filters
469
480
  (options[:conditions] || {}).collect { |attrib, value|
470
- if attributes.include?(attrib)
481
+ if attributes.include?(attrib.to_sym)
471
482
  puts <<-MSG
472
483
  Deprecation Warning: filters on attributes should be done using the :with
473
484
  option, not :conditions. For example:
@@ -513,6 +524,8 @@ MSG
513
524
  value.collect { |v| filter_value(v) }.flatten
514
525
  when Time
515
526
  value.respond_to?(:in_time_zone) ? [value.utc.to_i] : [value.to_i]
527
+ when NilClass
528
+ 0
516
529
  else
517
530
  Array(value)
518
531
  end
@@ -542,15 +555,15 @@ MSG
542
555
  end
543
556
 
544
557
  def index_option(key)
545
- return nil if options[:classes].length != 1
558
+ return nil unless one_class
546
559
 
547
- options[:classes].first.sphinx_indexes.collect { |index|
560
+ one_class.sphinx_indexes.collect { |index|
548
561
  index.local_options[key]
549
562
  }.compact.first
550
563
  end
551
564
 
552
565
  def attribute(*keys)
553
- return nil if options[:classes].length != 1
566
+ return nil unless one_class
554
567
 
555
568
  keys.detect { |key|
556
569
  attributes.include?(key)
@@ -558,9 +571,9 @@ MSG
558
571
  end
559
572
 
560
573
  def attributes
561
- return [] if (options[:classes] || []).length != 1
574
+ return [] unless one_class
562
575
 
563
- attributes = options[:classes].first.sphinx_indexes.collect { |index|
576
+ attributes = one_class.sphinx_indexes.collect { |index|
564
577
  index.attributes.collect { |attrib| attrib.unique_name }
565
578
  }.flatten
566
579
  end
@@ -599,7 +612,7 @@ MSG
599
612
 
600
613
  # if the user has specified an SQL order, return the collection
601
614
  # without rearranging it into the Sphinx order
602
- return instances if options[:sql_order]
615
+ return instances if (options[:sql_order] || index_options[:sql_order])
603
616
 
604
617
  ids.collect { |obj_id|
605
618
  instances.detect do |obj|
@@ -612,6 +625,8 @@ MSG
612
625
  # the number of #find's in multi-model searches.
613
626
  #
614
627
  def instances_from_matches
628
+ return single_class_results if one_class
629
+
615
630
  groups = results[:matches].group_by { |match|
616
631
  match[:attributes]["class_crc"]
617
632
  }
@@ -630,6 +645,10 @@ MSG
630
645
  end
631
646
  end
632
647
 
648
+ def single_class_results
649
+ instances_from_class one_class, results[:matches]
650
+ end
651
+
633
652
  def class_from_crc(crc)
634
653
  config.models_by_crc[crc].constantize
635
654
  end
@@ -643,12 +662,11 @@ MSG
643
662
  end
644
663
 
645
664
  def is_scope?(method)
646
- options[:classes] && options[:classes].length == 1 &&
647
- options[:classes].first.sphinx_scopes.include?(method)
665
+ one_class && one_class.sphinx_scopes.include?(method)
648
666
  end
649
667
 
650
668
  def add_scope(method, *args, &block)
651
- merge_search options[:classes].first.send(method, *args, &block)
669
+ merge_search one_class.send(method, *args, &block)
652
670
  end
653
671
 
654
672
  def merge_search(search)
@@ -82,7 +82,7 @@ module ThinkingSphinx
82
82
  config = @model.connection.instance_variable_get(:@config)
83
83
 
84
84
  source.sql_host = config[:host] || "localhost"
85
- source.sql_user = config[:username] || config[:user] || ""
85
+ source.sql_user = config[:username] || config[:user] || 'root'
86
86
  source.sql_pass = (config[:password].to_s || "").gsub('#', '\#')
87
87
  source.sql_db = config[:database]
88
88
  source.sql_port = config[:port]
@@ -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
@@ -351,4 +351,18 @@ describe "ThinkingSphinx::ActiveRecord" do
351
351
  @person.primary_key_for_sphinx.should == id
352
352
  end
353
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
354
368
  end
@@ -265,6 +265,41 @@ describe ThinkingSphinx::Attribute do
265
265
  end
266
266
  end
267
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
+
268
303
  describe "MVA with source query" do
269
304
  before :each do
270
305
  @attribute = ThinkingSphinx::Attribute.new(@source,
@@ -0,0 +1,9 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe Array do
4
+ describe '.===' do
5
+ it "should return true if an instance of ThinkingSphinx::Search" do
6
+ Array.should === ThinkingSphinx::Search.new
7
+ end
8
+ end
9
+ end
@@ -277,26 +277,57 @@ describe ThinkingSphinx::Facet do
277
277
  end
278
278
 
279
279
  describe "#value" do
280
- before :each do
281
- @index = ThinkingSphinx::Index.new(Friendship)
282
- @source = ThinkingSphinx::Source.new(@index)
283
- @field = ThinkingSphinx::Field.new(
284
- @source, ThinkingSphinx::Index::FauxColumn.new(:person, :first_name)
285
- )
286
- @facet = ThinkingSphinx::Facet.new(@field)
287
- end
280
+ describe 'for fields from associations' do
281
+ before :each do
282
+ @index = ThinkingSphinx::Index.new(Friendship)
283
+ @source = ThinkingSphinx::Source.new(@index)
284
+ @field = ThinkingSphinx::Field.new(
285
+ @source, ThinkingSphinx::Index::FauxColumn.new(:person, :first_name)
286
+ )
287
+ @facet = ThinkingSphinx::Facet.new(@field)
288
+ end
288
289
 
289
- it "should return association values" do
290
- person = Person.find(:first)
291
- friendship = Friendship.new(:person => person)
290
+ it "should return association values" do
291
+ person = Person.find(:first)
292
+ friendship = Friendship.new(:person => person)
292
293
 
293
- @facet.value(friendship, 1).should == person.first_name
294
+ @facet.value(friendship, 1).should == person.first_name
295
+ end
296
+
297
+ it "should return nil if the association is nil" do
298
+ friendship = Friendship.new(:person => nil)
299
+
300
+ @facet.value(friendship, 1).should be_nil
301
+ end
302
+
303
+ it "should return multi-level association values" do
304
+ person = Person.find(:first)
305
+ tag = person.tags.build(:name => 'buried')
306
+ friendship = Friendship.new(:person => person)
307
+
308
+ field = ThinkingSphinx::Field.new(
309
+ @source, ThinkingSphinx::Index::FauxColumn.new(:person, :tags, :name)
310
+ )
311
+ ThinkingSphinx::Facet.new(field).value(friendship, 'buried'.to_crc32).
312
+ should == 'buried'
313
+ end
294
314
  end
295
315
 
296
- it "should return nil if the association is nil" do
297
- friendship = Friendship.new(:person => nil)
316
+ describe 'for float attributes' do
317
+ before :each do
318
+ @index = ThinkingSphinx::Index.new(Alpha)
319
+ @source = ThinkingSphinx::Source.new(@index)
320
+ @attribute = ThinkingSphinx::Attribute.new(
321
+ @source, ThinkingSphinx::Index::FauxColumn.new(:cost)
322
+ )
323
+ @facet = ThinkingSphinx::Facet.new(@attribute)
324
+ end
325
+
326
+ it "should translate using the given model" do
327
+ alpha = Alpha.new(:cost => 10.5)
298
328
 
299
- @facet.value(friendship, 1).should be_nil
329
+ @facet.value(alpha, 1093140480).should == 10.5
330
+ end
300
331
  end
301
332
  end
302
333
  end
@@ -53,6 +53,35 @@ describe ThinkingSphinx::Index::Builder do
53
53
  end
54
54
  end
55
55
 
56
+ describe 'aliased field' do
57
+ before :each do
58
+ @index = ThinkingSphinx::Index::Builder.generate(Person) do
59
+ indexes first_name, :as => 'name'
60
+ end
61
+
62
+ @source = @index.sources.first
63
+ end
64
+
65
+ it "should store the alias as a symbol for consistency" do
66
+ @source.fields.last.unique_name.should == :name
67
+ end
68
+ end
69
+
70
+ describe 'aliased attribute' do
71
+ before :each do
72
+ @index = ThinkingSphinx::Index::Builder.generate(Person) do
73
+ indexes first_name
74
+ has :id, :as => 'real_id'
75
+ end
76
+
77
+ @source = @index.sources.first
78
+ end
79
+
80
+ it "should store the alias as a symbol for consistency" do
81
+ @source.attributes.last.unique_name.should == :real_id
82
+ end
83
+ end
84
+
56
85
  describe "sortable field" do
57
86
  before :each do
58
87
  @index = ThinkingSphinx::Index::Builder.generate(Person) do
@@ -223,6 +252,77 @@ describe ThinkingSphinx::Index::Builder do
223
252
  end
224
253
  end
225
254
 
255
+ describe 'faceted manual MVA' do
256
+ before :each do
257
+ @index = ThinkingSphinx::Index::Builder.generate(Person) do
258
+ indexes first_name
259
+ has 'SQL STATEMENT', :type => :multi, :as => :sql, :facet => true
260
+ end
261
+
262
+ @source = @index.sources.first
263
+ end
264
+
265
+ after :each do
266
+ Person.sphinx_facets.delete_at(-1)
267
+ end
268
+
269
+ it "should have two attributes alongside the four internal ones" do
270
+ @source.attributes.length.should == 6
271
+ end
272
+
273
+ it "should set the facet attribute name to have the _facet suffix" do
274
+ @source.attributes.last.unique_name.should == :sql_facet
275
+ end
276
+
277
+ it "should keep the original attribute's name set as requested" do
278
+ @source.attributes[-2].unique_name.should == :sql
279
+ end
280
+
281
+ it "should set the attribute type to multi" do
282
+ @source.attributes.last.type.should == :multi
283
+ end
284
+
285
+ it "should set the attribute column to be the same as the field" do
286
+ @source.attributes.last.columns.length.should == 1
287
+ @source.attributes.last.columns.first.__name.should == 'SQL STATEMENT'
288
+ end
289
+ end
290
+
291
+ describe 'faceted MVA field' do
292
+ before :each do
293
+ @index = ThinkingSphinx::Index::Builder.generate(Person) do
294
+ indexes tags(:name), :as => :tags, :facet => true
295
+ end
296
+
297
+ @source = @index.sources.first
298
+ end
299
+
300
+ after :each do
301
+ Person.sphinx_facets.delete_at(-1)
302
+ end
303
+
304
+ it "should have one field" do
305
+ @source.fields.length.should == 1
306
+ end
307
+
308
+ it "should have one attribute alongside the four internal ones" do
309
+ @source.attributes.length.should == 5
310
+ end
311
+
312
+ it "should set the attribute name to have the _facet suffix" do
313
+ @source.attributes.last.unique_name.should == :tags_facet
314
+ end
315
+
316
+ it "should set the attribute type to multi" do
317
+ @source.attributes.last.type.should == :multi
318
+ end
319
+
320
+ it "should set the attribute column to be the same as the field" do
321
+ @source.attributes.last.columns.length.should == 1
322
+ @source.attributes.last.columns.first.__name.should == :name
323
+ end
324
+ end
325
+
226
326
  describe "no fields" do
227
327
  it "should raise an exception" do
228
328
  lambda {
@@ -193,6 +193,14 @@ describe ThinkingSphinx::Search do
193
193
  search[3].id.should == 1
194
194
  end
195
195
 
196
+ it "should use the requested classes to generate the index argument" do
197
+ @client.should_receive(:query) do |query, index, comment|
198
+ index.should == 'alpha_core,beta_core,beta_delta'
199
+ end
200
+
201
+ ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).first
202
+ end
203
+
196
204
  describe 'query' do
197
205
  it "should concatenate arguments with spaces" do
198
206
  @client.should_receive(:query) do |query, index, comment|
@@ -379,6 +387,13 @@ describe ThinkingSphinx::Search do
379
387
  filter.exclude?.should be_false
380
388
  end
381
389
 
390
+ it "should treat nils in arrays as 0" do
391
+ ThinkingSphinx::Search.new(:with => {:ints => [nil, 1, 2, 3]}).first
392
+
393
+ filter = @client.filters.last
394
+ filter.values.should == [0, 1, 2, 3]
395
+ end
396
+
382
397
  it "should append inclusive filters of time ranges" do
383
398
  first, last = 1.week.ago, Time.now
384
399
  ThinkingSphinx::Search.new(:with => {
@@ -688,6 +703,35 @@ describe ThinkingSphinx::Search do
688
703
  end
689
704
  end
690
705
 
706
+ describe 'sql ordering' do
707
+ before :each do
708
+ @client.stub! :query => {
709
+ :matches => minimal_result_hashes(@alpha_b, @alpha_a)
710
+ }
711
+ Alpha.stub! :find => [@alpha_a, @alpha_b]
712
+ end
713
+
714
+ it "shouldn't re-sort SQL results based on Sphinx information" do
715
+ search = ThinkingSphinx::Search.new(
716
+ :classes => [Alpha],
717
+ :sql_order => 'id'
718
+ )
719
+ search.first.should == @alpha_a
720
+ search.last.should == @alpha_b
721
+ end
722
+
723
+ it "should use the option for the ActiveRecord::Base#find calls" do
724
+ Alpha.should_receive(:find) do |mode, options|
725
+ options[:order].should == 'id'
726
+ end
727
+
728
+ ThinkingSphinx::Search.new(
729
+ :classes => [Alpha],
730
+ :sql_order => 'id'
731
+ ).first
732
+ end
733
+ end
734
+
691
735
  describe 'excerpts' do
692
736
  before :each do
693
737
  @search = ThinkingSphinx::Search.new
@@ -802,6 +846,27 @@ describe ThinkingSphinx::Search do
802
846
  end
803
847
  end
804
848
 
849
+ describe '#indexes' do
850
+ it "should default to '*'" do
851
+ ThinkingSphinx::Search.new.indexes.should == '*'
852
+ end
853
+
854
+ it "should use given class to determine index name" do
855
+ ThinkingSphinx::Search.new(:classes => [Alpha]).indexes.
856
+ should == 'alpha_core'
857
+ end
858
+
859
+ it "should add both core and delta indexes for given classes" do
860
+ ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).indexes.
861
+ should == 'alpha_core,beta_core,beta_delta'
862
+ end
863
+
864
+ it "should respect the :index option" do
865
+ ThinkingSphinx::Search.new(:classes => [Alpha], :index => '*').indexes.
866
+ should == '*'
867
+ end
868
+ end
869
+
805
870
  describe '.each_with_groupby_and_count' do
806
871
  before :each do
807
872
  @alpha = Alpha.new
@@ -942,6 +1007,14 @@ describe ThinkingSphinx::Search do
942
1007
 
943
1008
  @search.excerpt_for('string', Beta)
944
1009
  end
1010
+
1011
+ it "should use the correct index in STI situations" do
1012
+ @client.should_receive(:excerpts) do |options|
1013
+ options[:index].should == 'person_core'
1014
+ end
1015
+
1016
+ @search.excerpt_for('string', Parent)
1017
+ end
945
1018
  end
946
1019
 
947
1020
  describe '#search' do
@@ -72,6 +72,16 @@ describe ThinkingSphinx::Source do
72
72
  @riddle.sql_sock.should == config[:socket]
73
73
  end
74
74
 
75
+ it "should use a default username of root if nothing else is provided" do
76
+ Person.connection.stub!(:instance_variable_get => {
77
+ :user => nil,
78
+ :username => nil
79
+ })
80
+
81
+ riddle = @source.to_riddle_for_core(1, 0)
82
+ riddle.sql_user.should == 'root'
83
+ end
84
+
75
85
  it "should assign attributes" do
76
86
  # 3 internal attributes plus the one requested
77
87
  @riddle.sql_attr_uint.length.should == 4
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hariton-thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.7.1
4
+ version: 1.2.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Allan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-10 00:00:00 -07:00
12
+ date: 2009-09-13 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -47,6 +47,7 @@ files:
47
47
  - lib/thinking_sphinx/attribute.rb
48
48
  - lib/thinking_sphinx/class_facet.rb
49
49
  - lib/thinking_sphinx/configuration.rb
50
+ - lib/thinking_sphinx/core/array.rb
50
51
  - lib/thinking_sphinx/core/string.rb
51
52
  - lib/thinking_sphinx/deltas.rb
52
53
  - lib/thinking_sphinx/deltas/datetime_delta.rb
@@ -103,7 +104,7 @@ files:
103
104
  - vendor/riddle/lib/riddle/configuration/sql_source.rb
104
105
  - vendor/riddle/lib/riddle/configuration/xml_source.rb
105
106
  - vendor/riddle/lib/riddle/controller.rb
106
- has_rdoc: true
107
+ has_rdoc: false
107
108
  homepage: http://ts.freelancing-gods.com
108
109
  licenses:
109
110
  post_install_message: |+
@@ -144,7 +145,7 @@ requirements: []
144
145
  rubyforge_project:
145
146
  rubygems_version: 1.3.5
146
147
  signing_key:
147
- specification_version: 2
148
+ specification_version: 3
148
149
  summary: A concise and easy-to-use Ruby library that connects ActiveRecord to the Sphinx search daemon, managing configuration, indexing and searching.
149
150
  test_files:
150
151
  - spec/lib/thinking_sphinx/active_record/delta_spec.rb
@@ -154,6 +155,7 @@ test_files:
154
155
  - spec/lib/thinking_sphinx/association_spec.rb
155
156
  - spec/lib/thinking_sphinx/attribute_spec.rb
156
157
  - spec/lib/thinking_sphinx/configuration_spec.rb
158
+ - spec/lib/thinking_sphinx/core/array_spec.rb
157
159
  - spec/lib/thinking_sphinx/core/string_spec.rb
158
160
  - spec/lib/thinking_sphinx/excerpter_spec.rb
159
161
  - spec/lib/thinking_sphinx/facet_search_spec.rb