thinking-sphinx 1.3.11 → 1.3.12

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/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.11
1
+ 1.3.12
@@ -179,7 +179,11 @@ module ThinkingSphinx
179
179
  def live_value(instance)
180
180
  object = instance
181
181
  column = @columns.first
182
- column.__stack.each { |method| object = object.send(method) }
182
+ column.__stack.each { |method|
183
+ object = object.send(method)
184
+ return nil if object.nil?
185
+ }
186
+
183
187
  sphinx_value object.send(column.__name)
184
188
  end
185
189
 
@@ -32,15 +32,11 @@ module ThinkingSphinx
32
32
 
33
33
  def initialize(index, &block)
34
34
  @index = index
35
- @source = ThinkingSphinx::Source.new(@index)
36
- @index.sources << @source
37
35
  @explicit_source = false
38
36
 
39
37
  self.instance_eval &block
40
38
 
41
- if @index.sources.any? { |source|
42
- source.fields.length == 0
43
- }
39
+ if no_fields?
44
40
  raise "At least one field is necessary for an index"
45
41
  end
46
42
  end
@@ -105,7 +101,7 @@ module ThinkingSphinx
105
101
  def indexes(*args)
106
102
  options = args.extract_options!
107
103
  args.each do |columns|
108
- field = Field.new(@source, FauxColumn.coerce(columns), options)
104
+ field = Field.new(source, FauxColumn.coerce(columns), options)
109
105
 
110
106
  add_sort_attribute field, options if field.sortable
111
107
  add_facet_attribute field, options if field.faceted
@@ -151,7 +147,7 @@ module ThinkingSphinx
151
147
  def has(*args)
152
148
  options = args.extract_options!
153
149
  args.each do |columns|
154
- attribute = Attribute.new(@source, FauxColumn.coerce(columns), options)
150
+ attribute = Attribute.new(source, FauxColumn.coerce(columns), options)
155
151
 
156
152
  add_facet_attribute attribute, options if attribute.faceted
157
153
  end
@@ -162,7 +158,7 @@ module ThinkingSphinx
162
158
  options[:facet] = true
163
159
 
164
160
  args.each do |columns|
165
- attribute = Attribute.new(@source, FauxColumn.coerce(columns), options)
161
+ attribute = Attribute.new(source, FauxColumn.coerce(columns), options)
166
162
 
167
163
  add_facet_attribute attribute, options
168
164
  end
@@ -176,7 +172,7 @@ module ThinkingSphinx
176
172
  # where "parent_type = 'Article'", "created_at < NOW()"
177
173
  #
178
174
  def where(*args)
179
- @source.conditions += args
175
+ source.conditions += args
180
176
  end
181
177
 
182
178
  # Use this method to add some manual SQL strings to the GROUP BY
@@ -186,7 +182,7 @@ module ThinkingSphinx
186
182
  # group_by "lat", "lng"
187
183
  #
188
184
  def group_by(*args)
189
- @source.groupings += args
185
+ source.groupings += args
190
186
  end
191
187
 
192
188
  # This is what to use to set properties on the index. Chief amongst
@@ -251,10 +247,18 @@ module ThinkingSphinx
251
247
 
252
248
  private
253
249
 
250
+ def source
251
+ @source ||= begin
252
+ source = ThinkingSphinx::Source.new(@index)
253
+ @index.sources << source
254
+ source
255
+ end
256
+ end
257
+
254
258
  def set_single_property(key, value)
255
259
  source_options = ThinkingSphinx::Configuration::SourceOptions
256
260
  if source_options.include?(key.to_s)
257
- @source.options.merge! key => value
261
+ source.options.merge! key => value
258
262
  else
259
263
  @index.local_options.merge! key => value
260
264
  end
@@ -272,7 +276,7 @@ module ThinkingSphinx
272
276
  def add_internal_attribute(property, options, suffix, crc = false)
273
277
  return unless ThinkingSphinx::Facet.translate?(property)
274
278
 
275
- Attribute.new(@source,
279
+ Attribute.new(source,
276
280
  property.columns.collect { |col| col.clone },
277
281
  options.merge(
278
282
  :type => property.is_a?(Field) ? :string : options[:type],
@@ -281,6 +285,12 @@ module ThinkingSphinx
281
285
  ).except(:facet)
282
286
  )
283
287
  end
288
+
289
+ def no_fields?
290
+ @index.sources.empty? || @index.sources.any? { |source|
291
+ source.fields.length == 0
292
+ }
293
+ end
284
294
  end
285
295
  end
286
296
  end
@@ -57,6 +57,16 @@ module ThinkingSphinx
57
57
  ThinkingSphinx.facets *args
58
58
  end
59
59
 
60
+ def self.matching_fields(fields, bitmask)
61
+ matches = []
62
+ bitstring = bitmask.to_s(2).rjust(32, '0').reverse
63
+
64
+ fields.each_with_index do |field, index|
65
+ matches << field if bitstring[index, 1] == '1'
66
+ end
67
+ matches
68
+ end
69
+
60
70
  def initialize(*args)
61
71
  ThinkingSphinx.context.define_indexes
62
72
 
@@ -262,6 +272,7 @@ module ThinkingSphinx
262
272
  replace instances_from_matches
263
273
  add_excerpter
264
274
  add_sphinx_attributes
275
+ add_matching_fields if client.rank_mode == :fieldmask
265
276
  end
266
277
  end
267
278
  end
@@ -283,11 +294,7 @@ module ThinkingSphinx
283
294
  each do |object|
284
295
  next if object.nil? || object.respond_to?(:sphinx_attributes)
285
296
 
286
- match = @results[:matches].detect { |match|
287
- match[:attributes]['sphinx_internal_id'] == object.
288
- primary_key_for_sphinx &&
289
- match[:attributes]['class_crc'] == object.class.to_crc32
290
- }
297
+ match = match_hash object
291
298
  next if match.nil?
292
299
 
293
300
  object.metaclass.instance_eval do
@@ -296,6 +303,30 @@ module ThinkingSphinx
296
303
  end
297
304
  end
298
305
 
306
+ def add_matching_fields
307
+ each do |object|
308
+ next if object.nil? || object.respond_to?(:matching_fields)
309
+
310
+ match = match_hash object
311
+ next if match.nil?
312
+ fields = ThinkingSphinx::Search.matching_fields(
313
+ @results[:fields], match[:weight]
314
+ )
315
+
316
+ object.metaclass.instance_eval do
317
+ define_method(:matching_fields) { fields }
318
+ end
319
+ end
320
+ end
321
+
322
+ def match_hash(object)
323
+ @results[:matches].detect { |match|
324
+ match[:attributes]['sphinx_internal_id'] == object.
325
+ primary_key_for_sphinx &&
326
+ match[:attributes]['class_crc'] == object.class.to_crc32
327
+ }
328
+ end
329
+
299
330
  def self.log(message, method = :debug, identifier = 'Sphinx')
300
331
  return if ::ActiveRecord::Base.logger.nil?
301
332
  identifier_color, message_color = "4;32;1", "0" # 0;1 = Bold
@@ -319,9 +350,7 @@ module ThinkingSphinx
319
350
  :group_distinct, :id_range, :cut_off, :retry_count, :retry_delay,
320
351
  :rank_mode, :max_query_time, :field_weights
321
352
  ].each do |key|
322
- # puts "key: #{key}"
323
353
  value = options[key] || index_options[key]
324
- # puts "value: #{value.inspect}"
325
354
  client.send("#{key}=", value) if value
326
355
  end
327
356
 
@@ -8,7 +8,7 @@ module ThinkingSphinx
8
8
 
9
9
  attr_accessor :model, :fields, :attributes, :conditions, :groupings,
10
10
  :options
11
- attr_reader :base, :index
11
+ attr_reader :base, :index, :database_configuration
12
12
 
13
13
  def initialize(index, options = {})
14
14
  @index = index
@@ -19,6 +19,8 @@ module ThinkingSphinx
19
19
  @groupings = []
20
20
  @options = options
21
21
  @associations = {}
22
+ @database_configuration = @model.connection.
23
+ instance_variable_get(:@config).clone
22
24
 
23
25
  @base = ::ActiveRecord::Associations::ClassMethods::JoinDependency.new(
24
26
  @model, [], nil
@@ -80,7 +82,7 @@ module ThinkingSphinx
80
82
  end
81
83
 
82
84
  def set_source_database_settings(source)
83
- config = @model.connection.instance_variable_get(:@config)
85
+ config = @database_configuration
84
86
 
85
87
  source.sql_host = config[:host] || "localhost"
86
88
  source.sql_user = config[:username] || config[:user] || 'root'
@@ -542,5 +542,21 @@ describe ThinkingSphinx::Attribute do
542
542
  @instance.stub!(:col_name => 42)
543
543
  @attribute.live_value(@instance).should == 42
544
544
  end
545
+
546
+ it "should handle nils in the association chain" do
547
+ @attribute = ThinkingSphinx::Attribute.new @source, [
548
+ stub('column', :__stack => [:assoc_name], :__name => :id)
549
+ ]
550
+ @instance.stub!(:assoc_name => nil)
551
+ @attribute.live_value(@instance).should be_nil
552
+ end
553
+
554
+ it "should handle association chains" do
555
+ @attribute = ThinkingSphinx::Attribute.new @source, [
556
+ stub('column', :__stack => [:assoc_name], :__name => :id)
557
+ ]
558
+ @instance.stub!(:assoc_name => stub('object', :id => 42))
559
+ @attribute.live_value(@instance).should == 42
560
+ end
545
561
  end
546
562
  end
@@ -153,6 +153,17 @@ describe ThinkingSphinx::Search do
153
153
  end
154
154
  end
155
155
 
156
+ describe '.matching_fields' do
157
+ it "should return objects with indexes matching 1's in the bitmask" do
158
+ fields = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta']
159
+ ThinkingSphinx::Search.matching_fields(fields, 85).
160
+ should == ['alpha', 'gamma', 'epsilon', 'eta']
161
+
162
+ ThinkingSphinx::Search.matching_fields(fields, 42).
163
+ should == ['beta', 'delta', 'zeta']
164
+ end
165
+ end
166
+
156
167
  describe '#populate' do
157
168
  before :each do
158
169
  @alpha_a, @alpha_b = Alpha.new, Alpha.new
@@ -164,7 +175,8 @@ describe ThinkingSphinx::Search do
164
175
  @beta_b.stub! :id => 2, :read_attribute => 2
165
176
 
166
177
  @client.stub! :query => {
167
- :matches => minimal_result_hashes(@alpha_a, @beta_b, @alpha_b, @beta_a)
178
+ :matches => minimal_result_hashes(@alpha_a, @beta_b, @alpha_b, @beta_a),
179
+ :fields => ["one", "two", "three", "four", "five"]
168
180
  }
169
181
  Alpha.stub! :find => [@alpha_a, @alpha_b]
170
182
  Beta.stub! :find => [@beta_a, @beta_b]
@@ -798,6 +810,33 @@ describe ThinkingSphinx::Search do
798
810
  hash['class_crc'].should == @search.last.class.to_crc32
799
811
  end
800
812
  end
813
+
814
+ describe '#matching_fields' do
815
+ it "should add matching_fields method if using fieldmask ranking mode" do
816
+ search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
817
+ search.first.should respond_to(:matching_fields)
818
+ end
819
+
820
+ it "should not add matching_fields method if using a different ranking mode" do
821
+ search = ThinkingSphinx::Search.new :rank_mode => :bm25
822
+ search.first.should_not respond_to(:matching_fields)
823
+ end
824
+
825
+ it "should not add matching_fields method if object already have one" do
826
+ search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
827
+ search.last.matching_fields.should_not be_an(Array)
828
+ end
829
+
830
+ it "should return an array" do
831
+ search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
832
+ search.first.matching_fields.should be_an(Array)
833
+ end
834
+
835
+ it "should return the fields that the bitmask match" do
836
+ search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
837
+ search.first.matching_fields.should == ['one', 'three', 'five']
838
+ end
839
+ end
801
840
  end
802
841
  end
803
842
 
@@ -6,6 +6,13 @@ describe ThinkingSphinx::Source do
6
6
  @source = ThinkingSphinx::Source.new(@index, :sql_range_step => 1000)
7
7
  end
8
8
 
9
+ describe '#initialize' do
10
+ it "should store the current connection details" do
11
+ config = Person.connection.instance_variable_get(:@config)
12
+ @source.database_configuration.should == config
13
+ end
14
+ end
15
+
9
16
  it "should generate the name from the model" do
10
17
  @source.name.should == "person"
11
18
  end
@@ -77,6 +84,7 @@ describe ThinkingSphinx::Source do
77
84
  :user => nil,
78
85
  :username => nil
79
86
  })
87
+ @source = ThinkingSphinx::Source.new(@index)
80
88
 
81
89
  riddle = @source.to_riddle_for_core(1, 0)
82
90
  riddle.sql_user.should == 'root'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.11
4
+ version: 1.3.12
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-12-12 00:00:00 +11:00
12
+ date: 2009-12-14 00:00:00 +11:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency