thinking-sphinx 2.0.0.rc1 → 2.0.0.rc2
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 +6 -0
- data/VERSION +1 -1
- data/features/facets.feature +9 -1
- data/features/sphinx_scopes.feature +8 -0
- data/features/step_definitions/scope_steps.rb +4 -0
- data/lib/cucumber/thinking_sphinx/external_world.rb +8 -4
- data/lib/thinking_sphinx.rb +1 -1
- data/lib/thinking_sphinx/active_record.rb +6 -2
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +3 -1
- data/lib/thinking_sphinx/class_facet.rb +3 -2
- data/lib/thinking_sphinx/configuration.rb +1 -1
- data/lib/thinking_sphinx/context.rb +2 -2
- data/lib/thinking_sphinx/deltas/default_delta.rb +1 -1
- data/lib/thinking_sphinx/facet.rb +6 -5
- data/lib/thinking_sphinx/facet_search.rb +51 -23
- data/lib/thinking_sphinx/search.rb +70 -27
- data/lib/thinking_sphinx/source/internal_properties.rb +1 -1
- data/spec/thinking_sphinx/active_record/delta_spec.rb +1 -1
- data/spec/thinking_sphinx/facet_search_spec.rb +75 -81
- data/spec/thinking_sphinx/facet_spec.rb +4 -4
- data/spec/thinking_sphinx/search_methods_spec.rb +4 -0
- data/spec/thinking_sphinx/search_spec.rb +13 -8
- data/tasks/distribution.rb +2 -9
- metadata +10 -102
data/README.textile
CHANGED
@@ -177,3 +177,9 @@ Since I first released this library, there's been quite a few people who have su
|
|
177
177
|
* James Brooks
|
178
178
|
* Andrew Coldham
|
179
179
|
* Mark Dodwell
|
180
|
+
* Frédéric Malamitsas
|
181
|
+
* Jon Gubman
|
182
|
+
* Michael Schuerig
|
183
|
+
* Ben Hutton
|
184
|
+
* Alfonso Jiménez
|
185
|
+
* Szymon Nowak
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.0.
|
1
|
+
2.0.0.rc2
|
data/features/facets.feature
CHANGED
@@ -27,7 +27,7 @@ Feature: Search and browse models by their defined facets
|
|
27
27
|
And I should have the facet State
|
28
28
|
And I should have the facet Age
|
29
29
|
|
30
|
-
Scenario:
|
30
|
+
Scenario: Requesting float facets
|
31
31
|
Given Sphinx is running
|
32
32
|
And I am searching on alphas
|
33
33
|
When I am requesting facet results
|
@@ -80,3 +80,11 @@ Feature: Search and browse models by their defined facets
|
|
80
80
|
And I am searching on posts
|
81
81
|
When I am requesting facet results
|
82
82
|
Then the Comment Ids facet should have 9 keys
|
83
|
+
|
84
|
+
Scenario: Requesting facets from a subclass
|
85
|
+
Given Sphinx is running
|
86
|
+
And I am searching on animals
|
87
|
+
When I am requesting facet results
|
88
|
+
And I want classes included
|
89
|
+
Then I should have the facet Class
|
90
|
+
|
@@ -40,3 +40,11 @@ Feature: Sphinx Scopes
|
|
40
40
|
When I use the with_first_name scope set to "Andrew"
|
41
41
|
And I am retrieving the scoped result count
|
42
42
|
Then I should get a value of 7
|
43
|
+
|
44
|
+
Scenario: Counts with scopes and additional query terms
|
45
|
+
Given Sphinx is running
|
46
|
+
And I am searching on people
|
47
|
+
When I use the with_first_name scope set to "Andrew"
|
48
|
+
And I am retrieving the scoped result count for "Byrne"
|
49
|
+
Then I should get a value of 1
|
50
|
+
|
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'thinking_sphinx/test'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module Cucumber
|
4
|
+
module ThinkingSphinx
|
5
|
+
class ExternalWorld
|
6
|
+
def initialize(suppress_delta_output = true)
|
7
|
+
::ThinkingSphinx::Test.init
|
8
|
+
::ThinkingSphinx::Test.start_with_autostop
|
9
|
+
end
|
10
|
+
end
|
7
11
|
end
|
8
12
|
end
|
data/lib/thinking_sphinx.rb
CHANGED
@@ -27,7 +27,7 @@ require 'thinking_sphinx/adapters/abstract_adapter'
|
|
27
27
|
require 'thinking_sphinx/adapters/mysql_adapter'
|
28
28
|
require 'thinking_sphinx/adapters/postgresql_adapter'
|
29
29
|
|
30
|
-
require 'thinking_sphinx/railtie'
|
30
|
+
require 'thinking_sphinx/railtie' if defined?(Rails)
|
31
31
|
|
32
32
|
module ThinkingSphinx
|
33
33
|
# A ConnectionError will get thrown when a connection to Sphinx can't be
|
@@ -41,7 +41,7 @@ module ThinkingSphinx
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def to_crc32s
|
44
|
-
(
|
44
|
+
(descendants << self).collect { |klass| klass.to_crc32 }
|
45
45
|
end
|
46
46
|
|
47
47
|
def sphinx_database_adapter
|
@@ -183,7 +183,7 @@ module ThinkingSphinx
|
|
183
183
|
|
184
184
|
def insert_sphinx_index(index)
|
185
185
|
self.sphinx_indexes << index
|
186
|
-
|
186
|
+
descendants.each { |klass| klass.insert_sphinx_index(index) }
|
187
187
|
end
|
188
188
|
|
189
189
|
def has_sphinx_indexes?
|
@@ -308,6 +308,10 @@ module ThinkingSphinx
|
|
308
308
|
end
|
309
309
|
end
|
310
310
|
|
311
|
+
attr_accessor :excerpts
|
312
|
+
attr_accessor :sphinx_attributes
|
313
|
+
attr_accessor :matching_fields
|
314
|
+
|
311
315
|
def in_index?(suffix)
|
312
316
|
self.class.search_for_id self.sphinx_document_id, sphinx_index_name(suffix)
|
313
317
|
end
|
@@ -12,7 +12,9 @@ module ThinkingSphinx
|
|
12
12
|
def self.detect(model)
|
13
13
|
case model.connection.class.name
|
14
14
|
when "ActiveRecord::ConnectionAdapters::MysqlAdapter",
|
15
|
-
"ActiveRecord::ConnectionAdapters::MysqlplusAdapter"
|
15
|
+
"ActiveRecord::ConnectionAdapters::MysqlplusAdapter",
|
16
|
+
"ActiveRecord::ConnectionAdapters::Mysql2Adapter",
|
17
|
+
"ActiveRecord::ConnectionAdapters::NullDBAdapter"
|
16
18
|
ThinkingSphinx::MysqlAdapter.new model
|
17
19
|
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
18
20
|
ThinkingSphinx::PostgreSQLAdapter.new model
|
@@ -8,8 +8,9 @@ module ThinkingSphinx
|
|
8
8
|
"class_crc"
|
9
9
|
end
|
10
10
|
|
11
|
-
def value(object,
|
12
|
-
|
11
|
+
def value(object, attribute_hash)
|
12
|
+
crc = attribute_hash['class_crc']
|
13
|
+
ThinkingSphinx::Configuration.instance.models_by_crc[crc]
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
@@ -243,7 +243,7 @@ module ThinkingSphinx
|
|
243
243
|
@models_by_crc ||= begin
|
244
244
|
ThinkingSphinx.context.indexed_models.inject({}) do |hash, model|
|
245
245
|
hash[model.constantize.to_crc32] = model
|
246
|
-
model.constantize.
|
246
|
+
model.constantize.descendants.each { |subclass|
|
247
247
|
hash[subclass.to_crc32] = subclass.name
|
248
248
|
}
|
249
249
|
hash
|
@@ -40,7 +40,7 @@ class ThinkingSphinx::Context
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def add_indexed_models
|
43
|
-
ActiveRecord::Base.
|
43
|
+
ActiveRecord::Base.descendants.each do |klass|
|
44
44
|
add_indexed_model klass if klass.has_sphinx_indexes?
|
45
45
|
end
|
46
46
|
end
|
@@ -55,7 +55,7 @@ class ThinkingSphinx::Context
|
|
55
55
|
model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1')
|
56
56
|
|
57
57
|
next if model_name.nil?
|
58
|
-
next if ::ActiveRecord::Base.send(:
|
58
|
+
next if ::ActiveRecord::Base.send(:descendants).detect { |model|
|
59
59
|
model.name == model_name.camelize
|
60
60
|
}
|
61
61
|
|
@@ -44,7 +44,7 @@ module ThinkingSphinx
|
|
44
44
|
config = ThinkingSphinx::Configuration.instance
|
45
45
|
rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
|
46
46
|
|
47
|
-
output = `#{config.bin_path}#{config.indexer_binary_name} --config
|
47
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config "#{config.config_file}" #{rotate} #{model.delta_index_names.join(' ')}`
|
48
48
|
puts(output) unless ThinkingSphinx.suppress_delta_output?
|
49
49
|
end
|
50
50
|
|
@@ -71,7 +71,12 @@ module ThinkingSphinx
|
|
71
71
|
@property.is_a?(Field) ? :string : @property.type
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
74
|
+
def float?
|
75
|
+
@property.type == :float
|
76
|
+
end
|
77
|
+
|
78
|
+
def value(object, attribute_hash)
|
79
|
+
attribute_value = attribute_hash['@groupby']
|
75
80
|
return translate(object, attribute_value) if translate? || float?
|
76
81
|
|
77
82
|
case @property.type
|
@@ -117,9 +122,5 @@ module ThinkingSphinx
|
|
117
122
|
def column
|
118
123
|
@property.columns.first
|
119
124
|
end
|
120
|
-
|
121
|
-
def float?
|
122
|
-
@property.type == :float
|
123
|
-
end
|
124
125
|
end
|
125
126
|
end
|
@@ -44,23 +44,21 @@ module ThinkingSphinx
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def populate
|
47
|
-
facet_names
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
47
|
+
ThinkingSphinx::Search.bundle_searches(facet_names) { |sphinx, name|
|
48
|
+
sphinx.search *(args + [facet_search_options(name)])
|
49
|
+
}.each_with_index { |search, index|
|
50
|
+
add_from_results facet_names[index], search
|
51
|
+
}
|
53
52
|
end
|
54
53
|
|
55
|
-
def facet_search_options
|
56
|
-
config = ThinkingSphinx::Configuration.instance
|
57
|
-
max = config.configuration.searchd.max_matches || 1000
|
58
|
-
|
54
|
+
def facet_search_options(facet_name)
|
59
55
|
options.merge(
|
60
56
|
:group_function => :attr,
|
61
|
-
:limit =>
|
62
|
-
:max_matches =>
|
63
|
-
:page => 1
|
57
|
+
:limit => max_matches,
|
58
|
+
:max_matches => max_matches,
|
59
|
+
:page => 1,
|
60
|
+
:group_by => facet_name,
|
61
|
+
:ids_only => !translate?(facet_name)
|
64
62
|
)
|
65
63
|
end
|
66
64
|
|
@@ -101,21 +99,34 @@ module ThinkingSphinx
|
|
101
99
|
}
|
102
100
|
end
|
103
101
|
|
104
|
-
def
|
105
|
-
|
102
|
+
def translate?(name)
|
103
|
+
facet = facet_from_name(name)
|
104
|
+
facet.translate? || facet.float?
|
105
|
+
end
|
106
|
+
|
107
|
+
def config
|
108
|
+
ThinkingSphinx::Configuration.instance
|
109
|
+
end
|
110
|
+
|
111
|
+
def max_matches
|
112
|
+
@max_matches ||= config.configuration.searchd.max_matches || 1000
|
113
|
+
end
|
114
|
+
|
115
|
+
# example: facet = country_facet; name = :country
|
116
|
+
def add_from_results(facet, search)
|
117
|
+
name = ThinkingSphinx::Facet.name_for(facet)
|
118
|
+
facet = facet_from_name(facet)
|
106
119
|
|
107
120
|
self[name] ||= {}
|
108
121
|
|
109
|
-
return if
|
110
|
-
|
111
|
-
facet = facet_from_object(results.first, facet) if facet.is_a?(String)
|
122
|
+
return if search.empty?
|
112
123
|
|
113
|
-
|
114
|
-
facet_value = facet.value(result,
|
124
|
+
search.each_with_match do |result, match|
|
125
|
+
facet_value = facet.value(result, match[:attributes])
|
115
126
|
|
116
127
|
self[name][facet_value] ||= 0
|
117
|
-
self[name][facet_value] += count
|
118
|
-
|
128
|
+
self[name][facet_value] += match[:attributes]["@count"]
|
129
|
+
end
|
119
130
|
end
|
120
131
|
|
121
132
|
def underlying_value(key, value)
|
@@ -130,7 +141,24 @@ module ThinkingSphinx
|
|
130
141
|
end
|
131
142
|
|
132
143
|
def facet_from_object(object, name)
|
133
|
-
|
144
|
+
facet = nil
|
145
|
+
klass = object.class
|
146
|
+
|
147
|
+
while klass != ::ActiveRecord::Base && facet.nil?
|
148
|
+
facet = klass.sphinx_facets.detect { |facet|
|
149
|
+
facet.attribute_name == name
|
150
|
+
}
|
151
|
+
klass = klass.superclass
|
152
|
+
end
|
153
|
+
|
154
|
+
facet
|
155
|
+
end
|
156
|
+
|
157
|
+
def facet_from_name(name)
|
158
|
+
name = ThinkingSphinx::Facet.name_for(name)
|
159
|
+
all_facets.detect { |facet|
|
160
|
+
facet.name == name
|
161
|
+
}
|
134
162
|
end
|
135
163
|
end
|
136
164
|
end
|
@@ -57,6 +57,22 @@ module ThinkingSphinx
|
|
57
57
|
ThinkingSphinx.facets *args
|
58
58
|
end
|
59
59
|
|
60
|
+
def self.bundle_searches(enum)
|
61
|
+
client = ThinkingSphinx::Configuration.instance.client
|
62
|
+
|
63
|
+
searches = enum.collect { |item|
|
64
|
+
search = yield ThinkingSphinx, item
|
65
|
+
search.append_to client
|
66
|
+
search
|
67
|
+
}
|
68
|
+
|
69
|
+
client.run.each_with_index.collect { |results, index|
|
70
|
+
searches[index].populate_from_queue results
|
71
|
+
}
|
72
|
+
|
73
|
+
searches
|
74
|
+
end
|
75
|
+
|
60
76
|
def self.matching_fields(fields, bitmask)
|
61
77
|
matches = []
|
62
78
|
bitstring = bitmask.to_s(2).rjust(32, '0').reverse
|
@@ -111,6 +127,7 @@ module ThinkingSphinx
|
|
111
127
|
add_scope(method, *args, &block)
|
112
128
|
return self
|
113
129
|
elsif method == :search_count
|
130
|
+
merge_search one_class.search(*args)
|
114
131
|
return scoped_count
|
115
132
|
elsif method.to_s[/^each_with_.*/].nil? && !@array.respond_to?(method)
|
116
133
|
super
|
@@ -186,6 +203,17 @@ module ThinkingSphinx
|
|
186
203
|
# Compatibility with older versions of will_paginate
|
187
204
|
alias_method :page_count, :total_pages
|
188
205
|
|
206
|
+
# Query time taken
|
207
|
+
#
|
208
|
+
# @return [Integer]
|
209
|
+
#
|
210
|
+
def query_time
|
211
|
+
populate
|
212
|
+
return 0 if @results[:time].nil?
|
213
|
+
|
214
|
+
@query_time ||= @results[:time]
|
215
|
+
end
|
216
|
+
|
189
217
|
# The total number of search results available.
|
190
218
|
#
|
191
219
|
# @return [Integer]
|
@@ -232,6 +260,13 @@ module ThinkingSphinx
|
|
232
260
|
end
|
233
261
|
end
|
234
262
|
|
263
|
+
def each_with_match(&block)
|
264
|
+
populate
|
265
|
+
results[:matches].each_with_index do |match, index|
|
266
|
+
yield self[index], match
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
235
270
|
def excerpt_for(string, model = nil)
|
236
271
|
if model.nil? && one_class
|
237
272
|
model ||= one_class
|
@@ -241,7 +276,7 @@ module ThinkingSphinx
|
|
241
276
|
client.excerpts(
|
242
277
|
:docs => [string],
|
243
278
|
:words => results[:words].keys.join(' '),
|
244
|
-
:index => "#{model.source_of_sphinx_index.sphinx_name}_core"
|
279
|
+
:index => options[:index] || "#{model.source_of_sphinx_index.sphinx_name}_core"
|
245
280
|
).first
|
246
281
|
end
|
247
282
|
|
@@ -251,6 +286,29 @@ module ThinkingSphinx
|
|
251
286
|
self
|
252
287
|
end
|
253
288
|
|
289
|
+
def append_to(client)
|
290
|
+
prepare client
|
291
|
+
client.append_query query, indexes, comment
|
292
|
+
client.reset
|
293
|
+
end
|
294
|
+
|
295
|
+
def populate_from_queue(results)
|
296
|
+
return if @populated
|
297
|
+
@populated = true
|
298
|
+
@results = results
|
299
|
+
|
300
|
+
if options[:ids_only]
|
301
|
+
replace @results[:matches].collect { |match|
|
302
|
+
match[:attributes]["sphinx_internal_id"]
|
303
|
+
}
|
304
|
+
else
|
305
|
+
replace instances_from_matches
|
306
|
+
add_excerpter
|
307
|
+
add_sphinx_attributes
|
308
|
+
add_matching_fields if client.rank_mode == :fieldmask
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
254
312
|
private
|
255
313
|
|
256
314
|
def config
|
@@ -289,43 +347,32 @@ module ThinkingSphinx
|
|
289
347
|
|
290
348
|
def add_excerpter
|
291
349
|
each do |object|
|
292
|
-
next if object.
|
293
|
-
|
294
|
-
excerpter = ThinkingSphinx::Excerpter.new self, object
|
295
|
-
block = lambda { excerpter }
|
350
|
+
next if object.nil?
|
296
351
|
|
297
|
-
object.
|
298
|
-
define_method(:excerpts, &block)
|
299
|
-
end
|
352
|
+
object.excerpts = ThinkingSphinx::Excerpter.new self, object
|
300
353
|
end
|
301
354
|
end
|
302
355
|
|
303
356
|
def add_sphinx_attributes
|
304
357
|
each do |object|
|
305
|
-
next if object.nil?
|
358
|
+
next if object.nil?
|
306
359
|
|
307
360
|
match = match_hash object
|
308
361
|
next if match.nil?
|
309
362
|
|
310
|
-
object.
|
311
|
-
define_method(:sphinx_attributes) { match[:attributes] }
|
312
|
-
end
|
363
|
+
object.sphinx_attributes = match[:attributes]
|
313
364
|
end
|
314
365
|
end
|
315
366
|
|
316
367
|
def add_matching_fields
|
317
368
|
each do |object|
|
318
|
-
next if object.nil?
|
369
|
+
next if object.nil?
|
319
370
|
|
320
371
|
match = match_hash object
|
321
372
|
next if match.nil?
|
322
|
-
|
373
|
+
object.matching_fields = ThinkingSphinx::Search.matching_fields(
|
323
374
|
@results[:fields], match[:weight]
|
324
375
|
)
|
325
|
-
|
326
|
-
object.singleton_class.instance_eval do
|
327
|
-
define_method(:matching_fields) { fields }
|
328
|
-
end
|
329
376
|
end
|
330
377
|
end
|
331
378
|
|
@@ -352,6 +399,10 @@ module ThinkingSphinx
|
|
352
399
|
def client
|
353
400
|
client = config.client
|
354
401
|
|
402
|
+
prepare client
|
403
|
+
end
|
404
|
+
|
405
|
+
def prepare(client)
|
355
406
|
index_options = one_class ?
|
356
407
|
one_class.sphinx_indexes.first.local_options : {}
|
357
408
|
|
@@ -588,14 +639,6 @@ MSG
|
|
588
639
|
end
|
589
640
|
|
590
641
|
# When passed a Time instance, returns the integer timestamp.
|
591
|
-
#
|
592
|
-
# If using Rails 2.1+, need to handle timezones to translate them back to
|
593
|
-
# UTC, as that's what datetimes will be stored as by MySQL.
|
594
|
-
#
|
595
|
-
# in_time_zone is a method that was added for the timezone support in
|
596
|
-
# Rails 2.1, which is why it's used for testing. I'm sure there's better
|
597
|
-
# ways, but this does the job.
|
598
|
-
#
|
599
642
|
def filter_value(value)
|
600
643
|
case value
|
601
644
|
when Range
|
@@ -603,7 +646,7 @@ MSG
|
|
603
646
|
when Array
|
604
647
|
value.collect { |v| filter_value(v) }.flatten
|
605
648
|
when Time
|
606
|
-
|
649
|
+
[value.to_i]
|
607
650
|
when NilClass
|
608
651
|
0
|
609
652
|
else
|
@@ -106,7 +106,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
|
|
106
106
|
|
107
107
|
it "should call indexer for the delta index" do
|
108
108
|
Person.sphinx_indexes.first.delta_object.should_receive(:`).with(
|
109
|
-
"#{ThinkingSphinx::Configuration.instance.bin_path}indexer --config
|
109
|
+
"#{ThinkingSphinx::Configuration.instance.bin_path}indexer --config \"#{ThinkingSphinx::Configuration.instance.config_file}\" --rotate person_delta"
|
110
110
|
)
|
111
111
|
|
112
112
|
@person.send(:index_delta)
|
@@ -1,28 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe ThinkingSphinx::FacetSearch do
|
4
|
+
let(:search) { stub('search', :append_to => nil, :empty? => true) }
|
5
|
+
let(:config) { ThinkingSphinx::Configuration.instance }
|
6
|
+
let(:client) { stub('client', :run => []) }
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
config.stub!(:client => client)
|
10
|
+
end
|
11
|
+
|
4
12
|
describe 'populate' do
|
5
|
-
|
6
|
-
|
7
|
-
hash_including(:group_by => 'city_facet')
|
8
|
-
).and_return([])
|
9
|
-
ThinkingSphinx.should_receive(:search).with(
|
10
|
-
hash_including(:group_by => 'state_facet')
|
11
|
-
).and_return([])
|
12
|
-
ThinkingSphinx.should_receive(:search).with(
|
13
|
-
hash_including(:group_by => 'birthday')
|
14
|
-
).and_return([])
|
15
|
-
|
16
|
-
ThinkingSphinx::FacetSearch.new(:classes => [Person])
|
13
|
+
before :each do
|
14
|
+
config.configuration.searchd.max_matches = 10_000
|
17
15
|
end
|
18
|
-
|
16
|
+
|
19
17
|
it "should request all shared facets in a multi-model request by default" do
|
20
|
-
ThinkingSphinx.stub!(:search =>
|
18
|
+
ThinkingSphinx.stub!(:search => search)
|
21
19
|
ThinkingSphinx::FacetSearch.new.facet_names.should == ['class_crc']
|
22
20
|
end
|
23
21
|
|
24
22
|
it "should request all facets in a multi-model request if specified" do
|
25
|
-
ThinkingSphinx.stub!(:search =>
|
23
|
+
ThinkingSphinx.stub!(:search => search)
|
26
24
|
ThinkingSphinx::FacetSearch.new(
|
27
25
|
:all_facets => true
|
28
26
|
).facet_names.should == [
|
@@ -30,80 +28,22 @@ describe ThinkingSphinx::FacetSearch do
|
|
30
28
|
]
|
31
29
|
end
|
32
30
|
|
33
|
-
describe ':facets option' do
|
34
|
-
it "should limit facets to the requested set" do
|
35
|
-
ThinkingSphinx.should_receive(:search).once.and_return([])
|
36
|
-
|
37
|
-
ThinkingSphinx::FacetSearch.new(
|
38
|
-
:classes => [Person], :facets => :state
|
39
|
-
)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
describe "empty result set for attributes" do
|
44
|
-
before :each do
|
45
|
-
ThinkingSphinx.stub!(:search => [])
|
46
|
-
@facets = ThinkingSphinx::FacetSearch.new(
|
47
|
-
:classes => [Person], :facets => :state
|
48
|
-
)
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should add key as attribute" do
|
52
|
-
@facets.should have_key(:state)
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should return an empty hash for the facet results" do
|
56
|
-
@facets[:state].should be_empty
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
describe "non-empty result set" do
|
61
|
-
before :each do
|
62
|
-
@person = Person.find(:first)
|
63
|
-
@people = [@person]
|
64
|
-
@people.stub!(:each_with_groupby_and_count).
|
65
|
-
and_yield(@person, @person.city.to_crc32, 1)
|
66
|
-
ThinkingSphinx.stub!(:search => @people)
|
67
|
-
|
68
|
-
@facets = ThinkingSphinx::FacetSearch.new(
|
69
|
-
:classes => [Person], :facets => :city
|
70
|
-
)
|
71
|
-
end
|
72
|
-
|
73
|
-
it "should return a hash" do
|
74
|
-
@facets.should be_a_kind_of(Hash)
|
75
|
-
end
|
76
|
-
|
77
|
-
it "should add key as attribute" do
|
78
|
-
@facets.keys.should include(:city)
|
79
|
-
end
|
80
|
-
|
81
|
-
it "should return a hash" do
|
82
|
-
@facets[:city].should == {@person.city => 1}
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
before :each do
|
87
|
-
@config = ThinkingSphinx::Configuration.instance
|
88
|
-
@config.configuration.searchd.max_matches = 10_000
|
89
|
-
end
|
90
|
-
|
91
31
|
it "should use the system-set max_matches for limit on facet calls" do
|
92
32
|
ThinkingSphinx.should_receive(:search) do |options|
|
93
33
|
options[:max_matches].should == 10_000
|
94
34
|
options[:limit].should == 10_000
|
95
|
-
|
35
|
+
search
|
96
36
|
end
|
97
37
|
|
98
38
|
ThinkingSphinx::FacetSearch.new
|
99
39
|
end
|
100
40
|
|
101
41
|
it "should use the default max-matches if there is no explicit setting" do
|
102
|
-
|
42
|
+
config.configuration.searchd.max_matches = nil
|
103
43
|
ThinkingSphinx.should_receive(:search) do |options|
|
104
44
|
options[:max_matches].should == 1000
|
105
45
|
options[:limit].should == 1000
|
106
|
-
|
46
|
+
search
|
107
47
|
end
|
108
48
|
|
109
49
|
ThinkingSphinx::FacetSearch.new
|
@@ -113,7 +53,7 @@ describe ThinkingSphinx::FacetSearch do
|
|
113
53
|
ThinkingSphinx.should_receive(:search) do |options|
|
114
54
|
options[:max_matches].should == 10_000
|
115
55
|
options[:limit].should == 10_000
|
116
|
-
|
56
|
+
search
|
117
57
|
end
|
118
58
|
|
119
59
|
ThinkingSphinx::FacetSearch.new(
|
@@ -125,7 +65,7 @@ describe ThinkingSphinx::FacetSearch do
|
|
125
65
|
it "should not use an explicit :page" do
|
126
66
|
ThinkingSphinx.should_receive(:search) do |options|
|
127
67
|
options[:page].should == 1
|
128
|
-
|
68
|
+
search
|
129
69
|
end
|
130
70
|
|
131
71
|
ThinkingSphinx::FacetSearch.new(:page => 3)
|
@@ -149,15 +89,69 @@ describe ThinkingSphinx::FacetSearch do
|
|
149
89
|
}.should raise_error
|
150
90
|
end
|
151
91
|
end
|
92
|
+
|
93
|
+
describe ':facets option' do
|
94
|
+
it "should limit facets to the requested set" do
|
95
|
+
ThinkingSphinx.should_receive(:search).once.and_return(search)
|
96
|
+
|
97
|
+
ThinkingSphinx::FacetSearch.new(
|
98
|
+
:classes => [Person], :facets => :state
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "empty result set for attributes" do
|
104
|
+
before :each do
|
105
|
+
ThinkingSphinx.stub!(:search => search)
|
106
|
+
@facets = ThinkingSphinx::FacetSearch.new(
|
107
|
+
:classes => [Person], :facets => :state
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should add key as attribute" do
|
112
|
+
@facets.should have_key(:state)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should return an empty hash for the facet results" do
|
116
|
+
@facets[:state].should be_empty
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "non-empty result set" do
|
121
|
+
before :each do
|
122
|
+
@person = Person.find(:first)
|
123
|
+
@people = [@person]
|
124
|
+
search.stub!(:empty? => false)
|
125
|
+
search.stub!(:each_with_match).
|
126
|
+
and_yield(@person, {:attributes => {'@groupby' => @person.city.to_crc32, '@count' => 1}})
|
127
|
+
ThinkingSphinx::Search.stub!(:bundle_searches => [search])
|
128
|
+
|
129
|
+
@facets = ThinkingSphinx::FacetSearch.new(
|
130
|
+
:classes => [Person], :facets => :city
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should return a hash" do
|
135
|
+
@facets.should be_a_kind_of(Hash)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should add key as attribute" do
|
139
|
+
@facets.keys.should include(:city)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should return a hash" do
|
143
|
+
@facets[:city].should == {@person.city => 1}
|
144
|
+
end
|
145
|
+
end
|
152
146
|
end
|
153
147
|
|
154
148
|
describe "#for" do
|
155
149
|
before do
|
156
150
|
@person = Person.find(:first)
|
157
151
|
@people = [@person]
|
158
|
-
|
159
|
-
and_yield(@person, @person.city.to_crc32, 1)
|
160
|
-
ThinkingSphinx.stub!(:
|
152
|
+
search.stub!(:each_with_match).
|
153
|
+
and_yield(@person, {:attributes => {'@groupby' => @person.city.to_crc32, '@count' => 1}})
|
154
|
+
ThinkingSphinx::Search.stub!(:bundle_searches => [search])
|
161
155
|
|
162
156
|
@facets = ThinkingSphinx::FacetSearch.new(
|
163
157
|
:classes => [Person], :facets => :city
|
@@ -291,13 +291,13 @@ describe ThinkingSphinx::Facet do
|
|
291
291
|
person = Person.find(:first)
|
292
292
|
friendship = Friendship.new(:person => person)
|
293
293
|
|
294
|
-
@facet.value(friendship, 1).should == person.first_name
|
294
|
+
@facet.value(friendship, {'first_name_facet' => 1}).should == person.first_name
|
295
295
|
end
|
296
296
|
|
297
297
|
it "should return nil if the association is nil" do
|
298
298
|
friendship = Friendship.new(:person => nil)
|
299
299
|
|
300
|
-
@facet.value(friendship, 1).should be_nil
|
300
|
+
@facet.value(friendship, {'first_name_facet' => 1}).should be_nil
|
301
301
|
end
|
302
302
|
|
303
303
|
it "should return multi-level association values" do
|
@@ -308,7 +308,7 @@ describe ThinkingSphinx::Facet do
|
|
308
308
|
field = ThinkingSphinx::Field.new(
|
309
309
|
@source, ThinkingSphinx::Index::FauxColumn.new(:person, :tags, :name)
|
310
310
|
)
|
311
|
-
ThinkingSphinx::Facet.new(field).value(friendship, 'buried'.to_crc32).
|
311
|
+
ThinkingSphinx::Facet.new(field).value(friendship, {'name_facet' => 'buried'.to_crc32}).
|
312
312
|
should == 'buried'
|
313
313
|
end
|
314
314
|
end
|
@@ -326,7 +326,7 @@ describe ThinkingSphinx::Facet do
|
|
326
326
|
it "should translate using the given model" do
|
327
327
|
alpha = Alpha.new(:cost => 10.5)
|
328
328
|
|
329
|
-
@facet.value(alpha, 1093140480).should == 10.5
|
329
|
+
@facet.value(alpha, {'cost' => 1093140480}).should == 10.5
|
330
330
|
end
|
331
331
|
end
|
332
332
|
end
|
@@ -135,6 +135,10 @@ describe ThinkingSphinx::SearchMethods do
|
|
135
135
|
end
|
136
136
|
|
137
137
|
describe '.facets' do
|
138
|
+
before :each do
|
139
|
+
ThinkingSphinx::Search.stub!(:bundle_searches => [])
|
140
|
+
end
|
141
|
+
|
138
142
|
it "should return a FacetSearch instance" do
|
139
143
|
Alpha.facets.should be_a(ThinkingSphinx::FacetSearch)
|
140
144
|
end
|
@@ -785,9 +785,10 @@ describe ThinkingSphinx::Search do
|
|
785
785
|
end
|
786
786
|
|
787
787
|
it "should set up the excerpter with the instances and search" do
|
788
|
-
|
789
|
-
|
790
|
-
|
788
|
+
[@alpha_a, @beta_b, @alpha_b, @beta_a].each do |object|
|
789
|
+
ThinkingSphinx::Excerpter.should_receive(:new).with(@search, object)
|
790
|
+
end
|
791
|
+
|
791
792
|
@search.first
|
792
793
|
end
|
793
794
|
end
|
@@ -822,11 +823,6 @@ describe ThinkingSphinx::Search do
|
|
822
823
|
search.first.should respond_to(:matching_fields)
|
823
824
|
end
|
824
825
|
|
825
|
-
it "should not add matching_fields method if using a different ranking mode" do
|
826
|
-
search = ThinkingSphinx::Search.new :rank_mode => :bm25
|
827
|
-
search.first.should_not respond_to(:matching_fields)
|
828
|
-
end
|
829
|
-
|
830
826
|
it "should not add matching_fields method if object already have one" do
|
831
827
|
search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
|
832
828
|
search.last.matching_fields.should_not be_an(Array)
|
@@ -1112,6 +1108,15 @@ describe ThinkingSphinx::Search do
|
|
1112
1108
|
@search.excerpt_for('string')
|
1113
1109
|
end
|
1114
1110
|
|
1111
|
+
it "should respect the provided index option" do
|
1112
|
+
@search = ThinkingSphinx::Search.new(:classes => [Alpha], :index => 'foo')
|
1113
|
+
@client.should_receive(:excerpts) do |options|
|
1114
|
+
options[:index].should == 'foo'
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
@search.excerpt_for('string')
|
1118
|
+
end
|
1119
|
+
|
1115
1120
|
it "should optionally take a second argument to allow for multi-model searches" do
|
1116
1121
|
@client.should_receive(:excerpts) do |options|
|
1117
1122
|
options[:index].should == 'beta_core'
|
data/tasks/distribution.rb
CHANGED
@@ -28,15 +28,8 @@ Jeweler::Tasks.new do |gem|
|
|
28
28
|
"spec/**/*_spec.rb"
|
29
29
|
]
|
30
30
|
|
31
|
-
gem.add_dependency 'activerecord', '>= 3.0.0.
|
32
|
-
gem.add_dependency 'riddle', '>= 1.0.
|
33
|
-
|
34
|
-
gem.add_development_dependency "yard", ">= 0"
|
35
|
-
gem.add_development_dependency "rspec", ">= 1.2.9"
|
36
|
-
gem.add_development_dependency "cucumber", ">= 0"
|
37
|
-
gem.add_development_dependency "will_paginate", "3.0.pre"
|
38
|
-
gem.add_development_dependency "ginger", "1.2.0"
|
39
|
-
gem.add_development_dependency "faker", "0.3.1"
|
31
|
+
gem.add_dependency 'activerecord', '>= 3.0.0.rc'
|
32
|
+
gem.add_dependency 'riddle', '>= 1.0.12'
|
40
33
|
|
41
34
|
gem.post_install_message = <<-MESSAGE
|
42
35
|
If you're upgrading, you should read this:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thinking-sphinx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 977940591
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 0
|
9
9
|
- 0
|
10
|
-
-
|
11
|
-
version: 2.0.0.
|
10
|
+
- rc2
|
11
|
+
version: 2.0.0.rc2
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- Pat Allan
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-08-23 00:00:00 +08:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -27,13 +27,13 @@ dependencies:
|
|
27
27
|
requirements:
|
28
28
|
- - ">="
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
hash:
|
30
|
+
hash: 7712042
|
31
31
|
segments:
|
32
32
|
- 3
|
33
33
|
- 0
|
34
34
|
- 0
|
35
|
-
-
|
36
|
-
version: 3.0.0.
|
35
|
+
- rc
|
36
|
+
version: 3.0.0.rc
|
37
37
|
type: :runtime
|
38
38
|
version_requirements: *id001
|
39
39
|
- !ruby/object:Gem::Dependency
|
@@ -44,106 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
hash:
|
47
|
+
hash: 15
|
48
48
|
segments:
|
49
49
|
- 1
|
50
50
|
- 0
|
51
|
-
-
|
52
|
-
version: 1.0.
|
51
|
+
- 12
|
52
|
+
version: 1.0.12
|
53
53
|
type: :runtime
|
54
54
|
version_requirements: *id002
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: yard
|
57
|
-
prerelease: false
|
58
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
59
|
-
none: false
|
60
|
-
requirements:
|
61
|
-
- - ">="
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
hash: 3
|
64
|
-
segments:
|
65
|
-
- 0
|
66
|
-
version: "0"
|
67
|
-
type: :development
|
68
|
-
version_requirements: *id003
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rspec
|
71
|
-
prerelease: false
|
72
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - ">="
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
hash: 13
|
78
|
-
segments:
|
79
|
-
- 1
|
80
|
-
- 2
|
81
|
-
- 9
|
82
|
-
version: 1.2.9
|
83
|
-
type: :development
|
84
|
-
version_requirements: *id004
|
85
|
-
- !ruby/object:Gem::Dependency
|
86
|
-
name: cucumber
|
87
|
-
prerelease: false
|
88
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - ">="
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
hash: 3
|
94
|
-
segments:
|
95
|
-
- 0
|
96
|
-
version: "0"
|
97
|
-
type: :development
|
98
|
-
version_requirements: *id005
|
99
|
-
- !ruby/object:Gem::Dependency
|
100
|
-
name: will_paginate
|
101
|
-
prerelease: false
|
102
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
103
|
-
none: false
|
104
|
-
requirements:
|
105
|
-
- - "="
|
106
|
-
- !ruby/object:Gem::Version
|
107
|
-
hash: 961915916
|
108
|
-
segments:
|
109
|
-
- 3
|
110
|
-
- 0
|
111
|
-
- pre
|
112
|
-
version: 3.0.pre
|
113
|
-
type: :development
|
114
|
-
version_requirements: *id006
|
115
|
-
- !ruby/object:Gem::Dependency
|
116
|
-
name: ginger
|
117
|
-
prerelease: false
|
118
|
-
requirement: &id007 !ruby/object:Gem::Requirement
|
119
|
-
none: false
|
120
|
-
requirements:
|
121
|
-
- - "="
|
122
|
-
- !ruby/object:Gem::Version
|
123
|
-
hash: 31
|
124
|
-
segments:
|
125
|
-
- 1
|
126
|
-
- 2
|
127
|
-
- 0
|
128
|
-
version: 1.2.0
|
129
|
-
type: :development
|
130
|
-
version_requirements: *id007
|
131
|
-
- !ruby/object:Gem::Dependency
|
132
|
-
name: faker
|
133
|
-
prerelease: false
|
134
|
-
requirement: &id008 !ruby/object:Gem::Requirement
|
135
|
-
none: false
|
136
|
-
requirements:
|
137
|
-
- - "="
|
138
|
-
- !ruby/object:Gem::Version
|
139
|
-
hash: 17
|
140
|
-
segments:
|
141
|
-
- 0
|
142
|
-
- 3
|
143
|
-
- 1
|
144
|
-
version: 0.3.1
|
145
|
-
type: :development
|
146
|
-
version_requirements: *id008
|
147
55
|
description: A concise and easy-to-use Ruby library that connects ActiveRecord to the Sphinx search daemon, managing configuration, indexing and searching.
|
148
56
|
email: pat@freelancing-gods.com
|
149
57
|
executables: []
|