outoftime-sunspot 0.7.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +26 -1
- data/LICENSE +18 -0
- data/README.rdoc +31 -25
- data/Rakefile +1 -1
- data/TODO +1 -5
- data/VERSION.yml +2 -2
- data/lib/sunspot/adapters.rb +28 -11
- data/lib/sunspot/data_extractor.rb +37 -0
- data/lib/sunspot/dsl/fields.rb +6 -17
- data/lib/sunspot/dsl/query.rb +22 -120
- data/lib/sunspot/dsl/restriction.rb +25 -0
- data/lib/sunspot/dsl/scope.rb +127 -15
- data/lib/sunspot/dsl.rb +1 -1
- data/lib/sunspot/field.rb +151 -81
- data/lib/sunspot/indexer.rb +1 -1
- data/lib/sunspot/query/dynamic_query.rb +86 -0
- data/lib/sunspot/{facets.rb → query/field_facet.rb} +1 -1
- data/lib/sunspot/query/pagination.rb +39 -0
- data/lib/sunspot/query/restriction.rb +223 -0
- data/lib/sunspot/query/sort.rb +33 -0
- data/lib/sunspot/query.rb +217 -117
- data/lib/sunspot/search.rb +43 -5
- data/lib/sunspot/session.rb +20 -5
- data/lib/sunspot/setup.rb +39 -10
- data/lib/sunspot/type.rb +15 -7
- data/lib/sunspot/util.rb +17 -0
- data/lib/sunspot.rb +83 -2
- data/spec/api/build_search_spec.rb +120 -2
- data/spec/api/indexer_spec.rb +44 -0
- data/spec/api/query_spec.rb +129 -0
- data/spec/api/search_retrieval_spec.rb +6 -0
- data/spec/integration/dynamic_fields_spec.rb +55 -0
- data/spec/mocks/post.rb +27 -1
- data/spec/spec_helper.rb +10 -3
- data/tasks/gemspec.rake +6 -1
- data/tasks/rdoc.rake +14 -1
- metadata +79 -56
- data/lib/sunspot/restriction.rb +0 -216
data/lib/sunspot/query.rb
CHANGED
@@ -1,92 +1,124 @@
|
|
1
|
+
%w(dynamic_query field_facet pagination restriction sort).each do |file|
|
2
|
+
require File.join(File.dirname(__FILE__), 'query', file)
|
3
|
+
end
|
4
|
+
|
1
5
|
module Sunspot
|
2
6
|
#
|
3
7
|
# This class encapsulates a query that is to be sent to Solr. The query is
|
4
8
|
# constructed in the block passed to the Sunspot.search method, using the
|
5
|
-
# Sunspot::DSL::Query interface.
|
6
|
-
#
|
7
|
-
#
|
9
|
+
# Sunspot::DSL::Query interface. It can also be accessed directly by calling
|
10
|
+
# #query on a Search object (presumably a not-yet-run one created using
|
11
|
+
# Sunspot#new_search), which might be more suitable than the DSL when an
|
12
|
+
# intermediate object has responsibility for building the query dynamically.
|
13
|
+
#--
|
14
|
+
# Instances of Query, as well as all of the components it contains, respond to
|
15
|
+
# the #to_params method, which returns a hash of parameters in the format
|
16
|
+
# recognized by the solr-ruby API.
|
8
17
|
#
|
9
|
-
class Query
|
18
|
+
class Query
|
10
19
|
attr_writer :keywords # <String> full-text keyword boolean phrase
|
11
20
|
|
12
|
-
def initialize(types,
|
21
|
+
def initialize(types, configuration) #:nodoc:
|
13
22
|
@types, @configuration = types, configuration
|
14
|
-
@
|
15
|
-
|
23
|
+
@components = []
|
24
|
+
@components << @pagination = Pagination.new(@configuration)
|
16
25
|
end
|
17
26
|
|
18
27
|
#
|
19
|
-
#
|
20
|
-
# by deep-merging scope and facet parameters, adding in various other
|
21
|
-
# parameters from instance data.
|
22
|
-
#
|
23
|
-
# Note that solr-ruby takes the :q parameter as a separate argument; for
|
24
|
-
# the sake of consistency, the Query object ignores this fact (the Search
|
25
|
-
# object extracts it back out).
|
26
|
-
#
|
27
|
-
# ==== Returns
|
28
|
-
#
|
29
|
-
# Hash:: Representation of query in solr-ruby form
|
30
|
-
#
|
31
|
-
def to_params
|
32
|
-
params = {}
|
33
|
-
query_components = []
|
34
|
-
query_components << @keywords if @keywords
|
35
|
-
query_components << types_phrase if types_phrase
|
36
|
-
params[:q] = query_components.map { |component| "(#{component})"} * ' AND '
|
37
|
-
params[:sort] = @sort if @sort
|
38
|
-
params[:start] = @start if @start
|
39
|
-
params[:rows] = @rows if @rows
|
40
|
-
for component in components
|
41
|
-
Util.deep_merge!(params, component.to_params)
|
42
|
-
end
|
43
|
-
params
|
44
|
-
end
|
45
|
-
|
28
|
+
# Add a restriction to the query.
|
46
29
|
#
|
47
|
-
# Add a query component
|
48
|
-
#
|
49
30
|
# ==== Parameters
|
50
31
|
#
|
51
|
-
#
|
32
|
+
# field_name<Symbol>:: Name of the field to which the restriction applies
|
33
|
+
# restriction_type<Class,Symbol>::
|
34
|
+
# Subclass of Sunspot::Query::Restriction::Base, or snake_cased name as symbol
|
35
|
+
# (e.g., +:equal_to+)
|
36
|
+
# value<Object>::
|
37
|
+
# Value against which the restriction applies (e.g. less_than(2) has a
|
38
|
+
# value of 2)
|
39
|
+
# negated::
|
40
|
+
# Whether this restriction should be negated (use add_negated_restriction)
|
52
41
|
#
|
53
|
-
def
|
54
|
-
|
42
|
+
def add_restriction(field_name, restriction_type, value, negated = false)
|
43
|
+
if restriction_type.is_a?(Symbol)
|
44
|
+
restriction_type = Restriction[restriction_type]
|
45
|
+
end
|
46
|
+
@components << restriction = restriction_type.new(field(field_name), value, negated)
|
47
|
+
restriction
|
55
48
|
end
|
56
49
|
|
57
50
|
#
|
58
|
-
# Add
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
51
|
+
# Add a negated restriction to the query. The restriction will be taken as
|
52
|
+
# the opposite of its usual meaning (e.g., an :equal_to restriction will
|
53
|
+
# be "not equal to".
|
54
|
+
#
|
63
55
|
# ==== Parameters
|
64
56
|
#
|
65
57
|
# field_name<Symbol>:: Name of the field to which the restriction applies
|
66
|
-
#
|
67
|
-
# Subclass of Sunspot::Restriction::Base to instantiate
|
58
|
+
# restriction_type<Class>::
|
59
|
+
# Subclass of Sunspot::Query::Restriction::Base to instantiate
|
68
60
|
# value<Object>::
|
69
61
|
# Value against which the restriction applies (e.g. less_than(2) has a
|
70
62
|
# value of 2)
|
71
|
-
# negative:: Whether this restriction should be negated
|
72
63
|
#
|
73
|
-
|
64
|
+
def add_negated_restriction(field_name, restriction_type, value)
|
65
|
+
add_restriction(field_name, restriction_type, value, true)
|
66
|
+
end
|
67
|
+
|
74
68
|
#
|
75
|
-
#
|
69
|
+
# Exclude a particular instance from the search results
|
76
70
|
#
|
77
|
-
|
78
|
-
|
71
|
+
# ==== Parameters
|
72
|
+
#
|
73
|
+
# instance<Object>:: instance to exclude from results
|
74
|
+
#
|
75
|
+
def exclude_instance(instance)
|
76
|
+
@components << Restriction::SameAs.new(instance, true)
|
79
77
|
end
|
80
78
|
|
81
79
|
#
|
82
|
-
# Add a field facet
|
80
|
+
# Add a field facet. See Sunspot::Facet for more information.
|
83
81
|
#
|
84
82
|
# ==== Parameters
|
85
83
|
#
|
86
84
|
# field_name<Symbol>:: Name of the field on which to get a facet
|
87
85
|
#
|
88
86
|
def add_field_facet(field_name)
|
89
|
-
|
87
|
+
@components << FieldFacet.new(field(field_name))
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Generate a DynamicQuery instance for the given base name.
|
92
|
+
# This gives you access to a subset of the Query API but the operations
|
93
|
+
# apply to dynamic fields inside the dynamic field definition specified
|
94
|
+
# by +base_name+.
|
95
|
+
#
|
96
|
+
# ==== Parameters
|
97
|
+
#
|
98
|
+
# base_name<Symbol>::
|
99
|
+
# Base name of the dynamic field definition to use in the dynamic query
|
100
|
+
# operations
|
101
|
+
#
|
102
|
+
# ==== Returns
|
103
|
+
#
|
104
|
+
# DynamicQuery::
|
105
|
+
# Instance providing dynamic query functionality for the given field
|
106
|
+
# definitions.
|
107
|
+
#
|
108
|
+
def dynamic_query(base_name)
|
109
|
+
DynamicQuery.new(dynamic_field(base_name), self)
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Add a component to the query. Used by objects that proxy to the query
|
114
|
+
# object.
|
115
|
+
#
|
116
|
+
# ==== Parameters
|
117
|
+
#
|
118
|
+
# component<~to_params>:: Query component to add.
|
119
|
+
#
|
120
|
+
def add_component(component) #:nodoc:
|
121
|
+
@components << component
|
90
122
|
end
|
91
123
|
|
92
124
|
#
|
@@ -100,9 +132,7 @@ module Sunspot
|
|
100
132
|
# Sunspot.config.pagination.default_per_page
|
101
133
|
#
|
102
134
|
def paginate(page, per_page = nil)
|
103
|
-
|
104
|
-
@start = (page - 1) * per_page
|
105
|
-
@rows = per_page
|
135
|
+
@pagination.page, @pagination.per_page = page, per_page
|
106
136
|
end
|
107
137
|
|
108
138
|
#
|
@@ -114,8 +144,44 @@ module Sunspot
|
|
114
144
|
# direction<Symbol>:: :asc or :desc (default :asc)
|
115
145
|
#
|
116
146
|
def order_by(field_name, direction = nil)
|
117
|
-
|
118
|
-
|
147
|
+
@components << Sort.new(field(field_name), direction)
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Build the query using the DSL block passed into Sunspot.search
|
152
|
+
#
|
153
|
+
# ==== Returns
|
154
|
+
#
|
155
|
+
# Sunspot::Query:: self
|
156
|
+
#
|
157
|
+
def build(&block)
|
158
|
+
Util.instance_eval_or_call(dsl, &block)
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
#
|
163
|
+
# Representation of this query as solr-ruby parameters. Constructs the hash
|
164
|
+
# by deep-merging scope and facet parameters, adding in various other
|
165
|
+
# parameters from instance data.
|
166
|
+
#
|
167
|
+
# Note that solr-ruby takes the :q parameter as a separate argument; for
|
168
|
+
# the sake of consistency, the Query object ignores this fact (the Search
|
169
|
+
# object extracts it back out).
|
170
|
+
#
|
171
|
+
# ==== Returns
|
172
|
+
#
|
173
|
+
# Hash:: Representation of query in solr-ruby form
|
174
|
+
#
|
175
|
+
def to_params #:nodoc:
|
176
|
+
params = {}
|
177
|
+
query_components = []
|
178
|
+
query_components << @keywords if @keywords
|
179
|
+
query_components << types_phrase if types_phrase
|
180
|
+
params[:q] = query_components.map { |component| "(#{component})"} * ' AND '
|
181
|
+
for component in @components
|
182
|
+
Util.deep_merge!(params, component.to_params)
|
183
|
+
end
|
184
|
+
params
|
119
185
|
end
|
120
186
|
|
121
187
|
#
|
@@ -126,12 +192,8 @@ module Sunspot
|
|
126
192
|
#
|
127
193
|
# Integer:: Page number
|
128
194
|
#
|
129
|
-
def page
|
130
|
-
|
131
|
-
@start / @rows + 1
|
132
|
-
else
|
133
|
-
1
|
134
|
-
end
|
195
|
+
def page #:nodoc:
|
196
|
+
@pagination.page
|
135
197
|
end
|
136
198
|
|
137
199
|
#
|
@@ -142,8 +204,8 @@ module Sunspot
|
|
142
204
|
#
|
143
205
|
# Integer:: Rows per page
|
144
206
|
#
|
145
|
-
def per_page
|
146
|
-
@
|
207
|
+
def per_page #:nodoc:
|
208
|
+
@pagination.per_page
|
147
209
|
end
|
148
210
|
|
149
211
|
#
|
@@ -153,7 +215,7 @@ module Sunspot
|
|
153
215
|
#
|
154
216
|
# Sunspot::DSL::Query:: DSL instance
|
155
217
|
#
|
156
|
-
def dsl
|
218
|
+
def dsl #:nodoc:
|
157
219
|
@dsl ||= DSL::Query.new(self)
|
158
220
|
end
|
159
221
|
|
@@ -173,20 +235,70 @@ module Sunspot
|
|
173
235
|
# ArgumentError::
|
174
236
|
# If the given field name is not configured for the types being queried
|
175
237
|
#
|
176
|
-
def field(field_name)
|
238
|
+
def field(field_name) #:nodoc:
|
177
239
|
fields_hash[field_name.to_sym] || raise(UnrecognizedFieldError, "No field configured for #{@types * ', '} with name '#{field_name}'")
|
178
240
|
end
|
179
241
|
|
180
|
-
|
242
|
+
def dynamic_field(field_name)
|
243
|
+
field = dynamic_fields_hash[field_name.to_sym] || raise(UnrecognizedFieldError, "No dynamic field configured for #{@types * ', '} with name #{field_name.inspect}")
|
244
|
+
end
|
181
245
|
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
|
187
|
-
|
246
|
+
#
|
247
|
+
# Pass in search options as a hash. This is not the preferred way of
|
248
|
+
# building a Sunspot search, but it is made available as experience shows
|
249
|
+
# Ruby developers like to pass in hashes. Probably nice for quick one-offs
|
250
|
+
# on the console, anyway.
|
251
|
+
#
|
252
|
+
# ==== Options (+options+)
|
253
|
+
#
|
254
|
+
# :keywords:: Keyword string for fulltext search
|
255
|
+
# :conditions::
|
256
|
+
# Hash of key-value pairs, where keys are field names, and values are one
|
257
|
+
# of scalar, Array, or Range. Scalars are evaluated as EqualTo
|
258
|
+
# restrictions; Arrays are AnyOf restrictions, and Ranges are Between
|
259
|
+
# restrictions.
|
260
|
+
# :order::
|
261
|
+
# Order the search results. Either a string or array of strings of the
|
262
|
+
# form "field_name direction"
|
263
|
+
# :page::
|
264
|
+
# Page to use for pagination
|
265
|
+
# :per_page::
|
266
|
+
# Number of results to show per page
|
267
|
+
#
|
268
|
+
def options=(options) #:nodoc:
|
269
|
+
if options.has_key?(:keywords)
|
270
|
+
self.keywords = options[:keywords]
|
271
|
+
end
|
272
|
+
if options.has_key?(:conditions)
|
273
|
+
options[:conditions].each_pair do |field_name, value|
|
274
|
+
begin
|
275
|
+
restriction_type =
|
276
|
+
case value
|
277
|
+
when Array
|
278
|
+
Restriction::AnyOf
|
279
|
+
when Range
|
280
|
+
Restriction::Between
|
281
|
+
else
|
282
|
+
Restriction::EqualTo
|
283
|
+
end
|
284
|
+
add_restriction(field_name, restriction_type, value)
|
285
|
+
rescue UnrecognizedFieldError
|
286
|
+
# ignore fields we don't recognize
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
if options.has_key?(:order)
|
291
|
+
for order in Array(options[:order])
|
292
|
+
order_by(*order.split(' '))
|
293
|
+
end
|
294
|
+
end
|
295
|
+
if options.has_key?(:page)
|
296
|
+
paginate(options[:page], options[:per_page])
|
297
|
+
end
|
188
298
|
end
|
189
299
|
|
300
|
+
private
|
301
|
+
|
190
302
|
#
|
191
303
|
# Boolean phrase that restricts results to objects of the type(s) under
|
192
304
|
# query. If this is an open query (no types specified) then it sends a
|
@@ -218,55 +330,43 @@ module Sunspot
|
|
218
330
|
# Hash:: field names keyed to field objects
|
219
331
|
#
|
220
332
|
def fields_hash
|
221
|
-
@fields_hash ||=
|
222
|
-
|
223
|
-
|
224
|
-
(
|
333
|
+
@fields_hash ||=
|
334
|
+
begin
|
335
|
+
fields_hash = @types.inject({}) do |hash, type|
|
336
|
+
Setup.for(type).fields.each do |field|
|
337
|
+
(hash[field.name.to_sym] ||= {})[type.name] = field
|
338
|
+
end
|
339
|
+
hash
|
225
340
|
end
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
fields_hash[field_name] = field_configurations_hash.values.first
|
341
|
+
fields_hash.each_pair do |field_name, field_configurations_hash|
|
342
|
+
if @types.any? { |type| field_configurations_hash[type.name].nil? } # at least one type doesn't have this field configured
|
343
|
+
fields_hash.delete(field_name)
|
344
|
+
elsif field_configurations_hash.values.map { |configuration| configuration.indexed_name }.uniq.length != 1 # fields with this name have different configs
|
345
|
+
fields_hash.delete(field_name)
|
346
|
+
else
|
347
|
+
fields_hash[field_name] = field_configurations_hash.values.first
|
348
|
+
end
|
235
349
|
end
|
236
350
|
end
|
237
|
-
end
|
238
351
|
end
|
239
352
|
|
240
|
-
def
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
end
|
256
|
-
add_restriction(field_name, restriction_type, value)
|
257
|
-
rescue UnrecognizedFieldError
|
258
|
-
# ignore fields we don't recognize
|
353
|
+
def dynamic_fields_hash
|
354
|
+
@dynamic_fields_hash ||=
|
355
|
+
begin
|
356
|
+
dynamic_fields_hash = @types.inject({}) do |hash, type|
|
357
|
+
Setup.for(type).dynamic_fields.each do |field|
|
358
|
+
(hash[field.name.to_sym] ||= {})[type.name] = field
|
359
|
+
end
|
360
|
+
hash
|
361
|
+
end
|
362
|
+
dynamic_fields_hash.each_pair do |field_name, field_configurations_hash|
|
363
|
+
if @types.any? { |type| field_configurations_hash[type.name].nil? }
|
364
|
+
dynamic_fields_hash.delete(field_name)
|
365
|
+
else
|
366
|
+
dynamic_fields_hash[field_name] = field_configurations_hash.values.first
|
367
|
+
end
|
259
368
|
end
|
260
369
|
end
|
261
|
-
end
|
262
|
-
if params.has_key?(:order)
|
263
|
-
for order in Array(params[:order])
|
264
|
-
order_by(*order.split(' '))
|
265
|
-
end
|
266
|
-
end
|
267
|
-
if params.has_key?(:page)
|
268
|
-
paginate(params[:page], params[:per_page])
|
269
|
-
end
|
270
370
|
end
|
271
371
|
end
|
272
372
|
end
|
data/lib/sunspot/search.rb
CHANGED
@@ -5,14 +5,18 @@ module Sunspot
|
|
5
5
|
# Instances of Search are returned by the Sunspot.search method.
|
6
6
|
#
|
7
7
|
class Search
|
8
|
+
# Objects of this class are returned by the #raw_results method.
|
8
9
|
RawResult = Struct.new(:class_name, :primary_key)
|
9
10
|
|
10
|
-
|
11
|
+
# Query information for this search. If you wish to build the query without
|
12
|
+
# using the search DSL, this method allows you to access the query API
|
13
|
+
# directly. See Sunspot#new_search for how to construct the search object
|
14
|
+
# in this case.
|
15
|
+
attr_reader :query
|
16
|
+
|
17
|
+
def initialize(connection, query) #:nodoc:
|
11
18
|
@connection = connection
|
12
|
-
|
13
|
-
@query = Query.new(types, params, configuration)
|
14
|
-
@query.dsl.instance_eval(&block) if block
|
15
|
-
@types = types
|
19
|
+
@query = query
|
16
20
|
end
|
17
21
|
|
18
22
|
#
|
@@ -88,6 +92,40 @@ module Sunspot
|
|
88
92
|
end
|
89
93
|
end
|
90
94
|
|
95
|
+
#
|
96
|
+
# Get the facet object for a given dynamic field. This dynamic field will
|
97
|
+
# need to have been requested as a field facet inside the search block.
|
98
|
+
#
|
99
|
+
# ==== Parameters
|
100
|
+
#
|
101
|
+
# base_name<Symbol>::
|
102
|
+
# Base name of the dynamic field definiton (as specified in the setup
|
103
|
+
# block)
|
104
|
+
# dynamic_name<Symbol>::
|
105
|
+
# Dynamic field name to facet on
|
106
|
+
#
|
107
|
+
# ==== Returns
|
108
|
+
#
|
109
|
+
# Sunspot::Facet:: Facet object for given dynamic field
|
110
|
+
#
|
111
|
+
# ==== Example
|
112
|
+
#
|
113
|
+
# search = Sunspot.search(Post) do
|
114
|
+
# dynamic :custom do
|
115
|
+
# facet :cuisine
|
116
|
+
# end
|
117
|
+
# end
|
118
|
+
# search.dynamic_facet(:custom, :cuisine)
|
119
|
+
# #=> Facet for the dynamic field :cuisine in the :custom field definition
|
120
|
+
#
|
121
|
+
def dynamic_facet(base_name, dynamic_name)
|
122
|
+
(@dynamic_facets_cache ||= {})[[base_name.to_sym, dynamic_name.to_sym]] ||=
|
123
|
+
begin
|
124
|
+
field = @query.dynamic_field(base_name).build(dynamic_name)
|
125
|
+
Facet.new(@solr_result.field_facets(field.indexed_name), field)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
91
129
|
private
|
92
130
|
|
93
131
|
#
|
data/lib/sunspot/session.rb
CHANGED
@@ -22,12 +22,23 @@ module Sunspot
|
|
22
22
|
@updates = 0
|
23
23
|
end
|
24
24
|
|
25
|
+
#
|
26
|
+
# See Sunspot.new_search
|
27
|
+
#
|
28
|
+
def new_search(*types)
|
29
|
+
types.flatten!
|
30
|
+
Search.new(connection, Query.new(types, @config))
|
31
|
+
end
|
32
|
+
|
25
33
|
#
|
26
34
|
# See Sunspot.search
|
27
35
|
#
|
28
36
|
def search(*types, &block)
|
29
|
-
types.
|
30
|
-
|
37
|
+
options = types.last.is_a?(Hash) ? types.pop : {}
|
38
|
+
search = new_search(*types)
|
39
|
+
search.query.build(&block) if block
|
40
|
+
search.query.options = options
|
41
|
+
search.execute!
|
31
42
|
end
|
32
43
|
|
33
44
|
#
|
@@ -37,7 +48,7 @@ module Sunspot
|
|
37
48
|
objects.flatten!
|
38
49
|
@updates += objects.length
|
39
50
|
for object in objects
|
40
|
-
|
51
|
+
indexer_for(object).add(object)
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
@@ -50,7 +61,7 @@ module Sunspot
|
|
50
61
|
end
|
51
62
|
|
52
63
|
#
|
53
|
-
# See Sunspot.commit
|
64
|
+
# See Sunspot.commit
|
54
65
|
#
|
55
66
|
def commit
|
56
67
|
@updates = 0
|
@@ -64,7 +75,7 @@ module Sunspot
|
|
64
75
|
objects.flatten!
|
65
76
|
@updates += objects.length
|
66
77
|
for object in objects
|
67
|
-
|
78
|
+
indexer_for(object).remove(object)
|
68
79
|
end
|
69
80
|
end
|
70
81
|
|
@@ -131,6 +142,10 @@ module Sunspot
|
|
131
142
|
Setup.for(object.class) || raise(NoSetupError, "Sunspot is not configured for #{object.class.inspect}")
|
132
143
|
end
|
133
144
|
|
145
|
+
def indexer_for(object)
|
146
|
+
setup_for(object).indexer(connection)
|
147
|
+
end
|
148
|
+
|
134
149
|
#
|
135
150
|
# Retrieve the Solr connection for this session, creating one if it does not
|
136
151
|
# already exist.
|
data/lib/sunspot/setup.rb
CHANGED
@@ -6,7 +6,7 @@ module Sunspot
|
|
6
6
|
class Setup #:nodoc:
|
7
7
|
def initialize(clazz)
|
8
8
|
@class_name = clazz.name
|
9
|
-
@fields, @text_fields = [], []
|
9
|
+
@fields, @text_fields, @dynamic_fields = [], [], []
|
10
10
|
@dsl = DSL::Fields.new(self)
|
11
11
|
end
|
12
12
|
|
@@ -32,6 +32,17 @@ module Sunspot
|
|
32
32
|
@text_fields.concat(Array(fields))
|
33
33
|
end
|
34
34
|
|
35
|
+
#
|
36
|
+
# Add dynamic fields
|
37
|
+
#
|
38
|
+
# ==== Parameters
|
39
|
+
#
|
40
|
+
# fields<Array>:: Array of dynamic field objects
|
41
|
+
#
|
42
|
+
def add_dynamic_fields(fields)
|
43
|
+
@dynamic_fields.concat(Array(fields))
|
44
|
+
end
|
45
|
+
|
35
46
|
#
|
36
47
|
# Builder method for evaluating the setup DSL
|
37
48
|
#
|
@@ -47,9 +58,7 @@ module Sunspot
|
|
47
58
|
# Array:: Collection of all fields associated with this setup
|
48
59
|
#
|
49
60
|
def fields
|
50
|
-
fields
|
51
|
-
fields.concat(parent.fields) if parent
|
52
|
-
fields
|
61
|
+
get_inheritable_collection(:fields)
|
53
62
|
end
|
54
63
|
|
55
64
|
#
|
@@ -61,21 +70,32 @@ module Sunspot
|
|
61
70
|
# Array:: Collection of all text fields associated with this setup
|
62
71
|
#
|
63
72
|
def text_fields
|
64
|
-
text_fields
|
65
|
-
text_fields.concat(parent.text_fields) if parent
|
66
|
-
text_fields
|
73
|
+
get_inheritable_collection(:text_fields)
|
67
74
|
end
|
68
75
|
|
69
76
|
#
|
70
|
-
# Get all
|
71
|
-
# inherited fields
|
77
|
+
# Get all static, dynamic, and text fields associated with this setup as
|
78
|
+
# well as all inherited fields
|
72
79
|
#
|
73
80
|
# ==== Returns
|
74
81
|
#
|
75
82
|
# Array:: Collection of all text and scope fields associated with this setup
|
76
83
|
#
|
77
84
|
def all_fields
|
78
|
-
|
85
|
+
all_fields = []
|
86
|
+
all_fields.concat(fields).concat(text_fields).concat(dynamic_fields)
|
87
|
+
all_fields
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Get all dynamic fields for this and parent setups
|
92
|
+
#
|
93
|
+
# ==== Returns
|
94
|
+
#
|
95
|
+
# Array:: Dynamic fields
|
96
|
+
#
|
97
|
+
def dynamic_fields
|
98
|
+
get_inheritable_collection(:dynamic_fields)
|
79
99
|
end
|
80
100
|
|
81
101
|
#
|
@@ -84,6 +104,7 @@ module Sunspot
|
|
84
104
|
# ==== Returns
|
85
105
|
#
|
86
106
|
# Sunspot::Indexer:: Indexer configured with this setup
|
107
|
+
#
|
87
108
|
def indexer(connection)
|
88
109
|
Indexer.new(connection, self)
|
89
110
|
end
|
@@ -112,6 +133,14 @@ module Sunspot
|
|
112
133
|
Setup.for(clazz.superclass)
|
113
134
|
end
|
114
135
|
|
136
|
+
private
|
137
|
+
|
138
|
+
def get_inheritable_collection(name)
|
139
|
+
collection = instance_variable_get(:"@#{name}").dup
|
140
|
+
collection.concat(parent.send(name)) if parent
|
141
|
+
collection
|
142
|
+
end
|
143
|
+
|
115
144
|
class <<self
|
116
145
|
#
|
117
146
|
# Retrieve or create the Setup instance for the given class, evaluating
|