sunspot 2.1.0 → 2.1.1
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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/History.txt +10 -0
- data/lib/sunspot.rb +6 -6
- data/lib/sunspot/batcher.rb +1 -1
- data/lib/sunspot/dsl.rb +1 -1
- data/lib/sunspot/dsl/field_query.rb +47 -7
- data/lib/sunspot/dsl/field_stats.rb +18 -0
- data/lib/sunspot/dsl/fields.rb +10 -1
- data/lib/sunspot/field.rb +15 -0
- data/lib/sunspot/field_factory.rb +33 -0
- data/lib/sunspot/indexer.rb +1 -0
- data/lib/sunspot/query.rb +1 -1
- data/lib/sunspot/query/common_query.rb +5 -0
- data/lib/sunspot/query/field_stats.rb +28 -0
- data/lib/sunspot/query/geofilt.rb +8 -3
- data/lib/sunspot/query/restriction.rb +5 -2
- data/lib/sunspot/query/sort.rb +35 -0
- data/lib/sunspot/search.rb +2 -1
- data/lib/sunspot/search/abstract_search.rb +57 -35
- data/lib/sunspot/search/field_facet.rb +4 -4
- data/lib/sunspot/search/field_stats.rb +21 -0
- data/lib/sunspot/search/stats_facet.rb +25 -0
- data/lib/sunspot/search/stats_row.rb +66 -0
- data/lib/sunspot/session.rb +6 -6
- data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +6 -4
- data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +16 -8
- data/lib/sunspot/setup.rb +6 -0
- data/lib/sunspot/version.rb +1 -1
- data/spec/api/class_set_spec.rb +3 -3
- data/spec/api/indexer/fixed_fields_spec.rb +5 -0
- data/spec/api/indexer/removal_spec.rb +13 -3
- data/spec/api/query/faceting_examples.rb +19 -0
- data/spec/api/query/join_spec.rb +19 -0
- data/spec/api/query/standard_spec.rb +1 -0
- data/spec/api/query/stats_examples.rb +66 -0
- data/spec/api/search/paginated_collection_spec.rb +1 -1
- data/spec/api/search/stats_spec.rb +94 -0
- data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +8 -2
- data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +14 -2
- data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +3 -3
- data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +1 -1
- data/spec/helpers/search_helper.rb +30 -0
- data/spec/integration/highlighting_spec.rb +3 -3
- data/spec/integration/indexing_spec.rb +3 -2
- data/spec/integration/scoped_search_spec.rb +30 -0
- data/spec/integration/stats_spec.rb +47 -0
- data/spec/mocks/photo.rb +14 -1
- data/sunspot.gemspec +1 -2
- data/tasks/rdoc.rake +22 -14
- metadata +32 -41
data/lib/sunspot/search.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
%w(abstract_search standard_search more_like_this_search query_facet field_facet
|
2
|
-
date_facet range_facet facet_row hit highlight field_group group hit_enumerable
|
2
|
+
date_facet range_facet facet_row hit highlight field_group group hit_enumerable
|
3
|
+
stats_row field_stats stats_facet).each do |file|
|
3
4
|
require File.join(File.dirname(__FILE__), 'search', file)
|
4
5
|
end
|
5
6
|
|
@@ -3,24 +3,24 @@ require 'sunspot/search/hit_enumerable'
|
|
3
3
|
|
4
4
|
module Sunspot
|
5
5
|
module Search #:nodoc:
|
6
|
-
|
7
|
-
#
|
6
|
+
|
7
|
+
#
|
8
8
|
# This class encapsulates the results of a Solr search. It provides access
|
9
9
|
# to search results, total result count, facets, and pagination information.
|
10
10
|
# Instances of Search are returned by the Sunspot.search and
|
11
11
|
# Sunspot.new_search methods.
|
12
12
|
#
|
13
13
|
class AbstractSearch
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# Retrieve all facet objects defined for this search, in order they were
|
16
16
|
# defined. To retrieve an individual facet by name, use #facet()
|
17
17
|
#
|
18
|
-
attr_reader :facets, :groups
|
18
|
+
attr_reader :facets, :groups, :stats
|
19
19
|
attr_reader :query #:nodoc:
|
20
20
|
attr_accessor :request_handler
|
21
21
|
|
22
22
|
include HitEnumerable
|
23
|
-
|
23
|
+
|
24
24
|
def initialize(connection, setup, query, configuration) #:nodoc:
|
25
25
|
@connection, @setup, @query = connection, setup, query
|
26
26
|
@query.paginate(1, configuration.pagination.default_per_page)
|
@@ -30,8 +30,11 @@ module Sunspot
|
|
30
30
|
|
31
31
|
@groups_by_name = {}
|
32
32
|
@groups = []
|
33
|
+
|
34
|
+
@stats_by_name = {}
|
35
|
+
@stats = []
|
33
36
|
end
|
34
|
-
|
37
|
+
|
35
38
|
#
|
36
39
|
# Execute the search on the Solr instance and store the results. If you
|
37
40
|
# use Sunspot#search() to construct your searches, there is no need to call
|
@@ -49,8 +52,8 @@ module Sunspot
|
|
49
52
|
def execute! #:nodoc: deprecated
|
50
53
|
execute
|
51
54
|
end
|
52
|
-
|
53
|
-
#
|
55
|
+
|
56
|
+
#
|
54
57
|
# Get the collection of results as instantiated objects. If WillPaginate is
|
55
58
|
# available, the results will be a WillPaginate::Collection instance; if
|
56
59
|
# not, it will be a vanilla Array.
|
@@ -65,8 +68,8 @@ module Sunspot
|
|
65
68
|
def results
|
66
69
|
@results ||= paginate_collection(verified_hits.map { |hit| hit.instance })
|
67
70
|
end
|
68
|
-
|
69
|
-
#
|
71
|
+
|
72
|
+
#
|
70
73
|
# Access raw Solr result information. Returns a collection of Hit objects
|
71
74
|
# that contain the class name, primary key, keyword relevance score (if
|
72
75
|
# applicable), and any stored fields.
|
@@ -92,7 +95,7 @@ module Sunspot
|
|
92
95
|
end
|
93
96
|
alias_method :raw_results, :hits
|
94
97
|
|
95
|
-
#
|
98
|
+
#
|
96
99
|
# The total number of documents matching the query parameters
|
97
100
|
#
|
98
101
|
# ==== Returns
|
@@ -102,8 +105,8 @@ module Sunspot
|
|
102
105
|
def total
|
103
106
|
@total ||= solr_response['numFound'] || 0
|
104
107
|
end
|
105
|
-
|
106
|
-
#
|
108
|
+
|
109
|
+
#
|
107
110
|
# The time elapsed to generate the Solr response
|
108
111
|
#
|
109
112
|
# ==== Returns
|
@@ -113,8 +116,8 @@ module Sunspot
|
|
113
116
|
def query_time
|
114
117
|
@query_time ||= solr_response_header['QTime']
|
115
118
|
end
|
116
|
-
|
117
|
-
#
|
119
|
+
|
120
|
+
#
|
118
121
|
# Get the facet object for the given name. `name` can either be the name
|
119
122
|
# given to a query facet, or the field name of a field facet. Returns a
|
120
123
|
# Sunspot::Facet object.
|
@@ -164,28 +167,38 @@ module Sunspot
|
|
164
167
|
end
|
165
168
|
end
|
166
169
|
|
170
|
+
def stats(name)
|
171
|
+
if name
|
172
|
+
@stats_by_name[name.to_sym]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
167
176
|
def group(name)
|
168
177
|
if name
|
169
178
|
@groups_by_name[name.to_sym]
|
170
179
|
end
|
171
180
|
end
|
172
|
-
|
173
|
-
#
|
181
|
+
|
182
|
+
#
|
174
183
|
# Deprecated in favor of optional second argument to #facet
|
175
184
|
#
|
176
185
|
def dynamic_facet(base_name, dynamic_name) #:nodoc:
|
177
186
|
facet(base_name, dynamic_name)
|
178
187
|
end
|
179
|
-
|
188
|
+
|
180
189
|
def facet_response #:nodoc:
|
181
190
|
@solr_result['facet_counts']
|
182
191
|
end
|
183
192
|
|
193
|
+
def stats_response #:nodoc:
|
194
|
+
@solr_result['stats']['stats_fields']
|
195
|
+
end
|
196
|
+
|
184
197
|
def group_response #:nodoc:
|
185
198
|
@solr_result['grouped']
|
186
199
|
end
|
187
|
-
|
188
|
-
#
|
200
|
+
|
201
|
+
#
|
189
202
|
# Build this search using a DSL block. This method can be called more than
|
190
203
|
# once on an unexecuted search (e.g., Sunspot.new_search) in order to build
|
191
204
|
# a search incrementally.
|
@@ -202,8 +215,8 @@ module Sunspot
|
|
202
215
|
Util.instance_eval_or_call(dsl, &block)
|
203
216
|
self
|
204
217
|
end
|
205
|
-
|
206
|
-
|
218
|
+
|
219
|
+
|
207
220
|
def inspect #:nodoc:
|
208
221
|
"<Sunspot::Search:#{query.to_params.inspect}>"
|
209
222
|
end
|
@@ -211,16 +224,16 @@ module Sunspot
|
|
211
224
|
def add_field_group(field) #:nodoc:
|
212
225
|
add_group(field.name, FieldGroup.new(field, self))
|
213
226
|
end
|
214
|
-
|
227
|
+
|
215
228
|
def add_field_facet(field, options = {}) #:nodoc:
|
216
229
|
name = (options[:name] || field.name)
|
217
230
|
add_facet(name, FieldFacet.new(field, self, options))
|
218
231
|
end
|
219
|
-
|
232
|
+
|
220
233
|
def add_query_facet(name, options) #:nodoc:
|
221
234
|
add_facet(name, QueryFacet.new(name, self, options))
|
222
235
|
end
|
223
|
-
|
236
|
+
|
224
237
|
def add_date_facet(field, options) #:nodoc:
|
225
238
|
name = (options[:name] || field.name)
|
226
239
|
add_facet(name, DateFacet.new(field, self, options))
|
@@ -231,26 +244,30 @@ module Sunspot
|
|
231
244
|
add_facet(name, RangeFacet.new(field, self, options))
|
232
245
|
end
|
233
246
|
|
247
|
+
def add_field_stats(field) #:nodoc:
|
248
|
+
add_stats(field.name, FieldStats.new(field, self))
|
249
|
+
end
|
250
|
+
|
234
251
|
def highlights_for(doc) #:nodoc:
|
235
252
|
if @solr_result['highlighting']
|
236
253
|
@solr_result['highlighting'][doc['id']]
|
237
254
|
end
|
238
255
|
end
|
239
|
-
|
256
|
+
|
240
257
|
private
|
241
|
-
|
258
|
+
|
242
259
|
def dsl
|
243
|
-
raise NotImplementedError
|
260
|
+
raise NotImplementedError
|
244
261
|
end
|
245
|
-
|
262
|
+
|
246
263
|
def execute_request(params)
|
247
264
|
raise NotImplementedError
|
248
265
|
end
|
249
|
-
|
266
|
+
|
250
267
|
def solr_response
|
251
268
|
@solr_response ||= @solr_result['response'] || {}
|
252
269
|
end
|
253
|
-
|
270
|
+
|
254
271
|
def solr_response_header
|
255
272
|
@solr_response_header ||= @solr_result['responseHeader'] || {}
|
256
273
|
end
|
@@ -258,25 +275,30 @@ module Sunspot
|
|
258
275
|
def solr_docs
|
259
276
|
solr_response['docs']
|
260
277
|
end
|
261
|
-
|
278
|
+
|
262
279
|
def verified_hits
|
263
280
|
@verified_hits ||= paginate_collection(super)
|
264
281
|
end
|
265
|
-
|
282
|
+
|
266
283
|
def paginate_collection(collection)
|
267
284
|
PaginatedCollection.new(collection, @query.page, @query.per_page, total)
|
268
285
|
end
|
269
|
-
|
286
|
+
|
270
287
|
def add_facet(name, facet)
|
271
288
|
@facets << facet
|
272
289
|
@facets_by_name[name.to_sym] = facet
|
273
290
|
end
|
274
291
|
|
292
|
+
def add_stats(name, stats)
|
293
|
+
@stats << stats
|
294
|
+
@stats_by_name[name.to_sym] = stats
|
295
|
+
end
|
296
|
+
|
275
297
|
def add_group(name, group)
|
276
298
|
@groups << group
|
277
299
|
@groups_by_name[name.to_sym] = group
|
278
300
|
end
|
279
|
-
|
301
|
+
|
280
302
|
# Clear out all the cached ivars so the search can be called again.
|
281
303
|
def reset
|
282
304
|
@results = @hits = @verified_hits = @total = @solr_response = @doc_ids = nil
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Sunspot
|
2
2
|
module Search
|
3
|
-
#
|
3
|
+
#
|
4
4
|
# A FieldFacet is a facet whose rows are all values for a certain field, in
|
5
5
|
# contrast to a QueryFacet, whose rows represent arbitrary queries.
|
6
6
|
#
|
@@ -14,7 +14,7 @@ module Sunspot
|
|
14
14
|
@field.name
|
15
15
|
end
|
16
16
|
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# Get the rows returned for this facet.
|
19
19
|
#
|
20
20
|
# ==== Options (options)
|
@@ -50,7 +50,7 @@ module Sunspot
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
#
|
53
|
+
#
|
54
54
|
# If this facet references a model class, populate the rows with instances
|
55
55
|
# of the model class by loading them out of the appropriate adapter.
|
56
56
|
#
|
@@ -79,7 +79,7 @@ module Sunspot
|
|
79
79
|
rows
|
80
80
|
end
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
def key
|
84
84
|
@key ||= (@options[:name] || @field.indexed_name).to_s
|
85
85
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
class FieldStats < StatsRow
|
4
|
+
def initialize(field, search) #:nodoc:
|
5
|
+
@field, @search, @facet_fields = field, search, []
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_facet field
|
9
|
+
@facet_fields << field
|
10
|
+
end
|
11
|
+
|
12
|
+
def field_name
|
13
|
+
@field.name
|
14
|
+
end
|
15
|
+
|
16
|
+
def data
|
17
|
+
@search.stats_response[@field.indexed_name]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
class StatsFacet < FieldFacet
|
4
|
+
attr_reader :field
|
5
|
+
|
6
|
+
def initialize(field, data) #:nodoc:
|
7
|
+
@field, @data = field, data
|
8
|
+
end
|
9
|
+
|
10
|
+
def rows(options = {})
|
11
|
+
if options[:verified]
|
12
|
+
verified_rows
|
13
|
+
else
|
14
|
+
@rows ||= @data.map do |value, data|
|
15
|
+
StatsRow.new(data, self, @field.type.cast(value))
|
16
|
+
end.sort_by { |row| row.value.to_s }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"<Sunspot::Search::StatsFacet:#{@field}>"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
class StatsRow
|
4
|
+
attr_reader :data, :value
|
5
|
+
attr_writer :instance #:nodoc:
|
6
|
+
|
7
|
+
def initialize(data, facet = nil, value = nil) #:nodoc:
|
8
|
+
@data, @facet, @value = data, facet, value
|
9
|
+
@facet_fields = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def min
|
13
|
+
data['min']
|
14
|
+
end
|
15
|
+
|
16
|
+
def max
|
17
|
+
data['max']
|
18
|
+
end
|
19
|
+
|
20
|
+
def count
|
21
|
+
data['count']
|
22
|
+
end
|
23
|
+
|
24
|
+
def sum
|
25
|
+
data['sum']
|
26
|
+
end
|
27
|
+
|
28
|
+
def missing
|
29
|
+
data['missing']
|
30
|
+
end
|
31
|
+
|
32
|
+
def sum_of_squares
|
33
|
+
data['sumOfSquares']
|
34
|
+
end
|
35
|
+
|
36
|
+
def mean
|
37
|
+
data['mean']
|
38
|
+
end
|
39
|
+
|
40
|
+
def standard_deviation
|
41
|
+
data['stddev']
|
42
|
+
end
|
43
|
+
|
44
|
+
def facet name
|
45
|
+
facets.find { |facet| facet.field.name == name.to_sym }
|
46
|
+
end
|
47
|
+
|
48
|
+
def facets
|
49
|
+
@facets ||= @facet_fields.map do |field|
|
50
|
+
StatsFacet.new(field, data['facets'][field.indexed_name])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def instance
|
55
|
+
if !defined?(@instance)
|
56
|
+
@facet.populate_instances
|
57
|
+
end
|
58
|
+
@instance
|
59
|
+
end
|
60
|
+
|
61
|
+
def inspect
|
62
|
+
"<Sunspot::Search::StatsRow:#{value.inspect} min=#{min} max=#{max} count=#{count}>"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/sunspot/session.rb
CHANGED
@@ -142,29 +142,29 @@ module Sunspot
|
|
142
142
|
#
|
143
143
|
# See Sunspot.remove!
|
144
144
|
#
|
145
|
-
def remove!(*objects)
|
146
|
-
remove(*objects)
|
145
|
+
def remove!(*objects, &block)
|
146
|
+
remove(*objects, &block)
|
147
147
|
commit
|
148
148
|
end
|
149
149
|
|
150
150
|
#
|
151
151
|
# See Sunspot.remove_by_id
|
152
152
|
#
|
153
|
-
def remove_by_id(clazz,
|
153
|
+
def remove_by_id(clazz, *ids)
|
154
154
|
class_name =
|
155
155
|
if clazz.is_a?(Class)
|
156
156
|
clazz.name
|
157
157
|
else
|
158
158
|
clazz.to_s
|
159
159
|
end
|
160
|
-
indexer.remove_by_id(class_name,
|
160
|
+
indexer.remove_by_id(class_name, ids)
|
161
161
|
end
|
162
162
|
|
163
163
|
#
|
164
164
|
# See Sunspot.remove_by_id!
|
165
165
|
#
|
166
|
-
def remove_by_id!(clazz,
|
167
|
-
remove_by_id(clazz,
|
166
|
+
def remove_by_id!(clazz, *ids)
|
167
|
+
remove_by_id(clazz, ids)
|
168
168
|
commit
|
169
169
|
end
|
170
170
|
|
@@ -23,15 +23,17 @@ module Sunspot
|
|
23
23
|
#
|
24
24
|
# See Sunspot.remove_by_id
|
25
25
|
#
|
26
|
-
def remove_by_id(clazz,
|
27
|
-
|
26
|
+
def remove_by_id(clazz, *ids)
|
27
|
+
ids.flatten!
|
28
|
+
session_for_class(clazz).remove_by_id(clazz, ids)
|
28
29
|
end
|
29
30
|
|
30
31
|
#
|
31
32
|
# See Sunspot.remove_by_id!
|
32
33
|
#
|
33
|
-
def remove_by_id!(clazz,
|
34
|
-
|
34
|
+
def remove_by_id!(clazz, *ids)
|
35
|
+
ids.flatten!
|
36
|
+
session_for_class(clazz).remove_by_id!(clazz, ids)
|
35
37
|
end
|
36
38
|
|
37
39
|
#
|