warp-thinking-sphinx 1.3.11 → 1.3.13

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.11
1
+ 1.3.13
@@ -164,6 +164,8 @@ module ThinkingSphinx
164
164
  end
165
165
 
166
166
  def define_indexes
167
+ superclass.define_indexes unless superclass == ::ActiveRecord::Base
168
+
167
169
  return if sphinx_index_blocks.nil? ||
168
170
  defined_indexes? ||
169
171
  !ThinkingSphinx.define_indexes?
@@ -178,7 +178,11 @@ module ThinkingSphinx
178
178
  def live_value(instance)
179
179
  object = instance
180
180
  column = @columns.first
181
- column.__stack.each { |method| object = object.send(method) }
181
+ column.__stack.each { |method|
182
+ object = object.send(method)
183
+ return sphinx_value(nil) if object.nil?
184
+ }
185
+
182
186
  sphinx_value object.send(column.__name)
183
187
  end
184
188
 
@@ -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 == 0
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: warp-thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.11
4
+ version: 1.3.13
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 +08:00
12
+ date: 2009-12-17 00:00:00 +08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency