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 +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
|