thinking-sphinx 1.3.11 → 1.3.12
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/thinking_sphinx/attribute.rb +5 -1
- data/lib/thinking_sphinx/index/builder.rb +22 -12
- data/lib/thinking_sphinx/search.rb +36 -7
- data/lib/thinking_sphinx/source.rb +4 -2
- data/spec/thinking_sphinx/attribute_spec.rb +16 -0
- data/spec/thinking_sphinx/search_spec.rb +40 -1
- data/spec/thinking_sphinx/source_spec.rb +8 -0
- metadata +2 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.
|
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|
|
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
|
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(
|
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(
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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 =
|
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 = @
|
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.
|
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
|
+
date: 2009-12-14 00:00:00 +11:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|