sunspot 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sunspot/adapters.rb +14 -3
  3. data/lib/sunspot/data_extractor.rb +1 -1
  4. data/lib/sunspot/dsl/fulltext.rb +1 -1
  5. data/lib/sunspot/dsl/standard_query.rb +29 -1
  6. data/lib/sunspot/dsl.rb +2 -2
  7. data/lib/sunspot/field.rb +15 -4
  8. data/lib/sunspot/indexer.rb +37 -8
  9. data/lib/sunspot/query/abstract_fulltext.rb +7 -3
  10. data/lib/sunspot/query/abstract_json_field_facet.rb +3 -0
  11. data/lib/sunspot/query/composite_fulltext.rb +21 -2
  12. data/lib/sunspot/query/date_field_json_facet.rb +2 -16
  13. data/lib/sunspot/query/dismax.rb +10 -4
  14. data/lib/sunspot/query/function_query.rb +25 -1
  15. data/lib/sunspot/query/join.rb +1 -1
  16. data/lib/sunspot/query/range_json_facet.rb +5 -2
  17. data/lib/sunspot/query/restriction.rb +10 -9
  18. data/lib/sunspot/query/standard_query.rb +12 -0
  19. data/lib/sunspot/search/field_json_facet.rb +14 -3
  20. data/lib/sunspot/session.rb +6 -4
  21. data/lib/sunspot/setup.rb +38 -0
  22. data/lib/sunspot/util.rb +4 -11
  23. data/lib/sunspot/version.rb +1 -1
  24. data/lib/sunspot.rb +9 -1
  25. data/spec/api/indexer/removal_spec.rb +87 -0
  26. data/spec/api/query/connective_boost_examples.rb +85 -0
  27. data/spec/api/query/join_spec.rb +2 -2
  28. data/spec/api/query/standard_spec.rb +10 -0
  29. data/spec/api/setup_spec.rb +99 -0
  30. data/spec/helpers/indexer_helper.rb +22 -0
  31. data/spec/integration/atomic_updates_spec.rb +169 -5
  32. data/spec/integration/faceting_spec.rb +68 -34
  33. data/spec/integration/join_spec.rb +22 -3
  34. data/spec/integration/scoped_search_spec.rb +78 -0
  35. data/spec/mocks/connection.rb +6 -0
  36. data/spec/mocks/photo.rb +11 -7
  37. data/spec/mocks/post.rb +35 -1
  38. data/sunspot.gemspec +0 -2
  39. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 179de5f5261bdb69d21038a0b2e36fbd2812e1fc71ef4c1efd77979a03e58179
4
- data.tar.gz: 1f7f40d9737400cf61c23515c61ebde70140b1c1bf9df800d7e19ce27c3cbd0d
3
+ metadata.gz: c13ef43c3d9e3c45ca9d1ccc170ba55947797e788911fda1982b817d34207c5d
4
+ data.tar.gz: 8a2dc3da8797d0ff4945c99732c9808e1ff9d06f94d0611b932a20cc49210d08
5
5
  SHA512:
6
- metadata.gz: ad59d5868f3461648d29c77b6368d7c5145ef6247ecca38878bf4f03bb3d2ab73fea2decf629936d58282b8067345198f6bff82be1347bb0cc92cb4537722d28
7
- data.tar.gz: 3c4fd67940331fbdacffd665ca624119038bf12da2aa2b041adbd8260aef3921660b83803a912cf81384c7269f6b46d3b7fad1c883c34e1a1ba2ccbade2fd6dc
6
+ metadata.gz: dbdf307d46210db9f3430a7a46d615709aa181c61c2400f6e42d23edd5dfa0c619ecb249cf148eb7215d4dd37d9907c630acf4d13ec83319cff1cbdd2652882e
7
+ data.tar.gz: f77be8b5ee7ea9226565214951f4af0930b994f9fe568b4c79ac7d0733d414644d32018b4bdfb0ebd23a19b30ecece2c45da5d29b8bd46e28aafaf33ff58796f
@@ -55,6 +55,20 @@ module Sunspot
55
55
  @instance = instance
56
56
  end
57
57
 
58
+ #
59
+ # An ID prefix to be added to the index_id
60
+ #
61
+ # ==== Returns
62
+ #
63
+ # String:: ID prefix for use in index ID to determine
64
+ # the shard a document is sent to for indexing
65
+ #
66
+ def id_prefix
67
+ setup = Sunspot::Setup.for(@instance.class)
68
+
69
+ setup && setup.id_prefix_for(@instance)
70
+ end
71
+
58
72
  #
59
73
  # The universally-unique ID for this instance that will be stored in solr
60
74
  #
@@ -63,9 +77,6 @@ module Sunspot
63
77
  # String:: ID for use in Solr
64
78
  #
65
79
  def index_id #:nodoc:
66
- setup = Sunspot::Setup.for(@instance.class)
67
- id_prefix = setup ? setup.id_prefix_for(@instance) : nil
68
-
69
80
  InstanceAdapter.index_id_for("#{id_prefix}#{@instance.class.name}", id)
70
81
  end
71
82
 
@@ -9,7 +9,7 @@ module Sunspot
9
9
  # Abstract extractor to perform common actions on extracted values
10
10
  #
11
11
  class AbstractExtractor
12
- BLACKLIST_REGEXP = /[\x0-\x8\xB\xC\xE-\x1F\x7f]/
12
+ BLACKLIST_REGEXP = /[\x0-\x8\xB\xC\xE-\x1F\x7f\uFFFE-\uFFFF]/
13
13
 
14
14
  def value_for(object)
15
15
  extract_value_from(object)
@@ -176,7 +176,7 @@ module Sunspot
176
176
  @query.add_additive_boost_function(factor_or_function)
177
177
  else
178
178
  Sunspot::Util.instance_eval_or_call(
179
- Scope.new(@query.create_boost_query(factor_or_function), @setup),
179
+ Scope.new(@query.add_boost_query(factor_or_function), @setup),
180
180
  &block
181
181
  )
182
182
  end
@@ -9,7 +9,7 @@ module Sunspot
9
9
  # See Sunspot.search for usage examples
10
10
  #
11
11
  class StandardQuery < FieldQuery
12
- include Paginatable, Adjustable, Spellcheckable
12
+ include Paginatable, Adjustable, Spellcheckable, Functional
13
13
 
14
14
  # Specify a phrase that should be searched as fulltext. Only +text+
15
15
  # fields are searched - see DSL::Fields.text
@@ -123,6 +123,34 @@ module Sunspot
123
123
  end
124
124
  end
125
125
 
126
+ #
127
+ # Defines a boost query
128
+ #
129
+ # === Examples
130
+ #
131
+ # Sunspot.search(Post) do
132
+ # with(:blog_id, 1)
133
+ #
134
+ # boost(10) do
135
+ # with(:category_id, 2)
136
+ # end
137
+ # end
138
+ #
139
+ def boost(factor_or_function, &block)
140
+ if factor_or_function.is_a?(Sunspot::Query::FunctionQuery)
141
+ @query.add_boost_function(factor_or_function)
142
+ else
143
+ Sunspot::Util.instance_eval_or_call(
144
+ Scope.new(@query.add_boost_query(factor_or_function), @setup),
145
+ &block
146
+ )
147
+ end
148
+ end
149
+
150
+ def boost_multiplicative(factor_or_function)
151
+ @query.add_multiplicative_boost_function(factor_or_function)
152
+ end
153
+
126
154
  private
127
155
 
128
156
  def add_fulltext(keywords, field_names)
data/lib/sunspot/dsl.rb CHANGED
@@ -1,5 +1,5 @@
1
- %w(spellcheckable fields scope paginatable adjustable field_query
2
- standard_query query_facet functional fulltext restriction
1
+ %w(spellcheckable fields functional scope paginatable adjustable field_query
2
+ standard_query query_facet fulltext restriction
3
3
  restriction_with_near search more_like_this_query function
4
4
  group field_stats).each do |file|
5
5
  require File.join(File.dirname(__FILE__), 'dsl', file)
data/lib/sunspot/field.rb CHANGED
@@ -195,7 +195,7 @@ module Sunspot
195
195
  # Could be of any type
196
196
  #
197
197
  class JoinField < Field #:nodoc:
198
- attr_reader :default_boost, :target
198
+ attr_reader :default_boost
199
199
 
200
200
  def initialize(name, type, options = {})
201
201
  @multiple = !!options.delete(:multiple)
@@ -213,15 +213,21 @@ module Sunspot
213
213
  end
214
214
 
215
215
  def from
216
- Sunspot::Setup.for(@target).field(@join[:from]).indexed_name
216
+ Sunspot::Setup.for(target).field(@join[:from]).indexed_name
217
217
  end
218
218
 
219
219
  def to
220
220
  Sunspot::Setup.for(@clazz).field(@join[:to]).indexed_name
221
221
  end
222
222
 
223
- def local_params
224
- "{!join from=#{from} to=#{to}}"
223
+ def local_params(value)
224
+ query = ["type:\"#{target.name}\""] | Util.Array(value)
225
+
226
+ "{!join from=#{from} to=#{to} v='#{query.join(' AND ')}'}"
227
+ end
228
+
229
+ def to_solr_conditional(value)
230
+ "\"#{value}\""
225
231
  end
226
232
 
227
233
  def eql?(field)
@@ -229,6 +235,11 @@ module Sunspot
229
235
  end
230
236
 
231
237
  alias_method :==, :eql?
238
+
239
+ def target
240
+ @target = Util.full_const_get(@target) if @target.is_a?(String)
241
+ @target
242
+ end
232
243
  end
233
244
 
234
245
  class TypeField #:nodoc:
@@ -54,9 +54,24 @@ module Sunspot
54
54
  # Remove the model from the Solr index by specifying the class and ID
55
55
  #
56
56
  def remove_by_id(class_name, *ids)
57
+ if class_name.is_a?(String) and class_name.index("!")
58
+ partition = class_name.rpartition("!")
59
+ id_prefix = partition[0..1].join
60
+ class_name = partition[2]
61
+ else
62
+ clazz_setup = setup_for_class(Util.full_const_get(class_name))
63
+ id_prefix = if clazz_setup.id_prefix_defined?
64
+ if clazz_setup.id_prefix_requires_instance?
65
+ warn(Sunspot::RemoveByIdNotSupportCompositeIdMessage.call(class_name))
66
+ else
67
+ clazz_setup.id_prefix_for_class
68
+ end
69
+ end
70
+ end
71
+
57
72
  ids.flatten!
58
73
  @connection.delete_by_id(
59
- ids.map { |id| Adapters::InstanceAdapter.index_id_for(class_name, id) }
74
+ ids.map { |id| Adapters::InstanceAdapter.index_id_for("#{id_prefix}#{class_name}", id) }
60
75
  )
61
76
  end
62
77
 
@@ -147,13 +162,27 @@ module Sunspot
147
162
  )
148
163
  end
149
164
 
150
- def document_for_atomic_update(clazz, id)
151
- if Adapters::InstanceAdapter.for(clazz)
152
- RSolr::Xml::Document.new(
153
- id: Adapters::InstanceAdapter.index_id_for(clazz.name, id),
154
- type: Util.superclasses_for(clazz).map(&:name)
155
- )
156
- end
165
+ def document_for_atomic_update(clazz, key)
166
+ return unless Adapters::InstanceAdapter.for(clazz)
167
+
168
+ clazz_setup = setup_for_class(clazz)
169
+ id_prefix = if clazz_setup.id_prefix_defined?
170
+ if clazz_setup.id_prefix_requires_instance?
171
+ if key.respond_to?(:id)
172
+ clazz_setup.id_prefix_for(key)
173
+ else
174
+ warn(Sunspot::AtomicUpdateRequireInstanceForCompositeIdMessage.call(clazz.name))
175
+ end
176
+ else
177
+ clazz_setup.id_prefix_for_class
178
+ end
179
+ end
180
+
181
+ instance_id = key.respond_to?(:id) ? key.id : key
182
+ RSolr::Xml::Document.new(
183
+ id: Adapters::InstanceAdapter.index_id_for("#{id_prefix}#{clazz.name}", instance_id),
184
+ type: Util.superclasses_for(clazz).map(&:name)
185
+ )
157
186
  end
158
187
  #
159
188
  # Get the Setup object for the given object's class.
@@ -10,7 +10,7 @@ module Sunspot
10
10
  #
11
11
  # Assign a new boost query and return it.
12
12
  #
13
- def create_boost_query(factor)
13
+ def add_boost_query(factor)
14
14
  @boost_queries << boost_query = BoostQuery.new(factor)
15
15
  boost_query
16
16
  end
@@ -19,14 +19,18 @@ module Sunspot
19
19
  # Add a boost function
20
20
  #
21
21
  def add_additive_boost_function(function_query)
22
- @additive_boost_functions << function_query
22
+ unless @additive_boost_functions.include?(function_query)
23
+ @additive_boost_functions << function_query
24
+ end
23
25
  end
24
26
 
25
27
  #
26
28
  # Add a multiplicative boost function
27
29
  #
28
30
  def add_multiplicative_boost_function(function_query)
29
- @multiplicative_boost_functions << function_query
31
+ unless @multiplicative_boost_functions.include?(function_query)
32
+ @multiplicative_boost_functions << function_query
33
+ end
30
34
  end
31
35
 
32
36
  #
@@ -17,6 +17,9 @@ module Sunspot
17
17
  params[:sort] = { @options[:sort] => @options[:sort_type]||'desc' } unless @options[:sort].nil?
18
18
  params[:prefix] = @options[:prefix] unless @options[:prefix].nil?
19
19
  params[:offset] = @options[:offset] unless @options[:offset].nil?
20
+ params[:allBuckets] = @options[:all_buckets] unless @options[:all_buckets].nil?
21
+ params[:missing] = @options[:missing] unless @options[:missing].nil?
22
+ params[:method] = @options[:method] unless @options[:method].nil?
20
23
 
21
24
  if !@options[:distinct].nil?
22
25
  dist_opts = @options[:distinct]
@@ -3,11 +3,30 @@ module Sunspot
3
3
  class CompositeFulltext
4
4
  def initialize
5
5
  @components = []
6
+ @dismax = nil
6
7
  end
7
8
 
8
9
  def add_fulltext(keywords)
9
- @components << dismax = Dismax.new(keywords)
10
- dismax
10
+ @components << @dismax = Dismax.new(keywords)
11
+ @dismax
12
+ end
13
+
14
+ def add_boost_query(factor)
15
+ @dismax ||= Dismax.new("*:*")
16
+ @components << @dismax unless @components.include?(@dismax)
17
+ @dismax.add_boost_query(factor)
18
+ end
19
+
20
+ def add_boost_function(function)
21
+ @dismax ||= Dismax.new("*:*")
22
+ @components << @dismax unless @components.include?(@dismax)
23
+ @dismax.add_additive_boost_function(function)
24
+ end
25
+
26
+ def add_multiplicative_boost_function(function)
27
+ @dismax ||= Dismax.new("*:*")
28
+ @components << @dismax unless @components.include?(@dismax)
29
+ @dismax.add_multiplicative_boost_function(function)
11
30
  end
12
31
 
13
32
  def add_join(keywords, target, from, to)
@@ -1,24 +1,10 @@
1
1
  module Sunspot
2
2
  module Query
3
- class DateFieldJsonFacet < AbstractJsonFieldFacet
3
+ class DateFieldJsonFacet < RangeJsonFacet
4
4
 
5
5
  def initialize(field, options, setup)
6
- raise Exception.new('Need to specify a time_range') if options[:time_range].nil?
7
- @start = options[:time_range].first
8
- @end = options[:time_range].last
9
- @gap = "+#{options[:gap] || 86400}SECONDS"
10
6
  super
11
- end
12
-
13
- def field_name_with_local_params
14
- params = {}
15
- params[:type] = 'range'
16
- params[:field] = @field.indexed_name
17
- params[:start] = @field.to_indexed(@start)
18
- params[:end] = @field.to_indexed(@end)
19
- params[:gap] = @gap
20
- params.merge!(init_params)
21
- { @field.name => params }
7
+ @gap = "+#{@gap}#{options[:gap_unit] || 'SECONDS'}"
22
8
  end
23
9
  end
24
10
  end
@@ -28,10 +28,16 @@ module Sunspot
28
28
  # The query as Solr parameters
29
29
  #
30
30
  def to_params
31
- params = { :q => @keywords }
32
- params[:fl] = '* score'
33
- params[:qf] = @fulltext_fields.values.map { |field| field.to_boosted_field }.join(' ')
34
- params[:defType] = 'edismax'
31
+ params = {
32
+ :q => @keywords,
33
+ :defType => 'edismax',
34
+ :fl => '* score'
35
+ }
36
+
37
+ if @fulltext_fields.any?
38
+ params[:qf] = @fulltext_fields.values.map { |field| field.to_boosted_field }.join(' ')
39
+ end
40
+
35
41
  params[:mm] = @minimum_match if @minimum_match
36
42
  params[:ps] = @phrase_slop if @phrase_slop
37
43
  params[:qs] = @query_phrase_slop if @query_phrase_slop
@@ -3,18 +3,25 @@ module Sunspot
3
3
  #
4
4
  # Abstract class for function queries.
5
5
  #
6
- class FunctionQuery
6
+ class FunctionQuery
7
+ attr_reader :boost_amount
7
8
 
8
9
  def ^(y)
9
10
  @boost_amount = y
10
11
  self
11
12
  end
13
+
14
+ def ==(other)
15
+ @boost_amount == other.boost_amount
16
+ end
12
17
  end
13
18
 
14
19
  #
15
20
  # Function query which represents a constant.
16
21
  #
17
22
  class ConstantFunctionQuery < FunctionQuery
23
+ attr_reader :constant
24
+
18
25
  def initialize(constant)
19
26
  @constant = constant
20
27
  end
@@ -22,12 +29,18 @@ module Sunspot
22
29
  def to_s
23
30
  Type.to_literal(@constant) << (@boost_amount ? "^#{@boost_amount}" : "")
24
31
  end
32
+
33
+ def ==(other)
34
+ super and @constant == other.constant
35
+ end
25
36
  end
26
37
 
27
38
  #
28
39
  # Function query which represents a field.
29
40
  #
30
41
  class FieldFunctionQuery < FunctionQuery
42
+ attr_reader :field
43
+
31
44
  def initialize(field)
32
45
  @field = field
33
46
  end
@@ -35,6 +48,10 @@ module Sunspot
35
48
  def to_s
36
49
  "#{Util.escape(@field.indexed_name)}" << (@boost_amount ? "^#{@boost_amount}" : "")
37
50
  end
51
+
52
+ def ==(other)
53
+ super and @field == other.field
54
+ end
38
55
  end
39
56
 
40
57
  #
@@ -43,6 +60,8 @@ module Sunspot
43
60
  # Arguments are in turn FunctionQuery objects.
44
61
  #
45
62
  class FunctionalFunctionQuery < FunctionQuery
63
+ attr_reader :function_name, :function_args
64
+
46
65
  def initialize(function_name, function_args)
47
66
  @function_name, @function_args = function_name, function_args
48
67
  end
@@ -51,6 +70,11 @@ module Sunspot
51
70
  params = @function_args.map { |arg| arg.to_s }.join(",")
52
71
  "#{@function_name}(#{params})" << (@boost_amount ? "^#{@boost_amount}" : "")
53
72
  end
73
+
74
+ def ==(other)
75
+ super and
76
+ @function_name == other.function_name and @function_args == other.function_args
77
+ end
54
78
  end
55
79
  end
56
80
  end
@@ -52,7 +52,7 @@ module Sunspot
52
52
  #
53
53
  # Assign a new boost query and return it.
54
54
  #
55
- def create_boost_query(factor)
55
+ def add_boost_query(factor)
56
56
  end
57
57
 
58
58
  #
@@ -5,10 +5,12 @@ module Sunspot
5
5
  SECONDS_IN_DAY = 86400
6
6
 
7
7
  def initialize(field, options, setup)
8
- raise Exception.new("Need to specify a range") if options[:range].nil?
8
+ options[:range] ||= options[:time_range]
9
+ raise Exception.new("Need to specify a range") if options[:range].nil? && options[:time_range].nil?
9
10
  @start = options[:range].first
10
11
  @end = options[:range].last
11
12
  @gap = options[:gap] || SECONDS_IN_DAY
13
+ @other = options[:other]
12
14
  super
13
15
  end
14
16
 
@@ -19,7 +21,8 @@ module Sunspot
19
21
  field: @field.indexed_name,
20
22
  start: @field.to_indexed(@start),
21
23
  end: @field.to_indexed(@end),
22
- gap: @gap
24
+ gap: @gap,
25
+ other: @other
23
26
  }.merge!(init_params)
24
27
  }
25
28
  end
@@ -78,14 +78,14 @@ module Sunspot
78
78
  # on whether this restriction is negated.
79
79
  #
80
80
  def to_boolean_phrase
81
- phrase = []
82
- phrase << @field.local_params if @field.respond_to? :local_params
83
- unless negated?
84
- phrase << to_positive_boolean_phrase
85
- else
86
- phrase << to_negated_boolean_phrase
87
- end
88
- phrase.join
81
+ value = if negated?
82
+ to_negated_boolean_phrase
83
+ else
84
+ to_positive_boolean_phrase
85
+ end
86
+
87
+ @field.respond_to?(:local_params) ?
88
+ @field.local_params(value) : value
89
89
  end
90
90
 
91
91
  #
@@ -208,7 +208,8 @@ module Sunspot
208
208
  private
209
209
 
210
210
  def to_solr_conditional
211
- "#{solr_value}"
211
+ @field.respond_to?(:to_solr_conditional) ?
212
+ @field.to_solr_conditional(solr_value) : "#{solr_value}"
212
213
  end
213
214
  end
214
215
 
@@ -16,6 +16,18 @@ module Sunspot
16
16
  @fulltext.add_join(keywords, target, from, to)
17
17
  end
18
18
 
19
+ def add_boost_query(factor)
20
+ @fulltext.add_boost_query(factor)
21
+ end
22
+
23
+ def add_boost_function(function)
24
+ @fulltext.add_boost_function(function)
25
+ end
26
+
27
+ def add_multiplicative_boost_function(function)
28
+ @fulltext.add_multiplicative_boost_function(function)
29
+ end
30
+
19
31
  def disjunction
20
32
  parent_fulltext = @fulltext
21
33
  @fulltext = @fulltext.add_disjunction
@@ -5,15 +5,14 @@ module Sunspot
5
5
  attr_reader :name
6
6
 
7
7
  def initialize(field, search, options)
8
- @name, @search, @options = name, search, options
8
+ @name, @search, @options = (options[:name] || field.name), search, options
9
9
  @field = field
10
10
  end
11
11
 
12
12
  def rows
13
13
  @rows ||=
14
14
  begin
15
- json_facet_response = @search.json_facet_response[@field.name.to_s]
16
- data = json_facet_response.nil? ? [] : json_facet_response['buckets']
15
+ data = no_data? ? [] : @search.json_facet_response[@field.name.to_s]['buckets']
17
16
  rows = []
18
17
  data.each do |d|
19
18
  rows << JsonFacetRow.new(d, self)
@@ -28,6 +27,18 @@ module Sunspot
28
27
  end
29
28
 
30
29
  end
30
+
31
+ def no_data?
32
+ @search.json_facet_response[@field.name.to_s].nil?
33
+ end
34
+
35
+ def other_count(type)
36
+ json_facet_for_field = @search.json_facet_response[@field.name.to_s]
37
+ return 0 if json_facet_for_field.nil?
38
+
39
+ other = json_facet_for_field[type.to_s] || {}
40
+ other['count']
41
+ end
31
42
  end
32
43
  end
33
44
  end
@@ -259,7 +259,7 @@ module Sunspot
259
259
  read_timeout: config.solr.read_timeout,
260
260
  open_timeout: config.solr.open_timeout,
261
261
  proxy: config.solr.proxy,
262
- update_format: update_format_generator
262
+ update_format: update_format
263
263
  )
264
264
  end
265
265
 
@@ -278,9 +278,11 @@ module Sunspot
278
278
  end
279
279
  end
280
280
 
281
- def update_format_generator
282
- if config.solr.update_format && RSolr.version.to_i > 1
283
- config.solr.update_format.downcase.to_sym == :json ? RSolr::JSON::Generator : RSolr::Xml::Generator
281
+ def update_format
282
+ if config.solr.update_format && config.solr.update_format.to_s.match(/xml|json/i)
283
+ config.solr.update_format.downcase.to_sym
284
+ else
285
+ :xml
284
286
  end
285
287
  end
286
288
  end
data/lib/sunspot/setup.rb CHANGED
@@ -302,6 +302,44 @@ module Sunspot
302
302
  end
303
303
  end
304
304
 
305
+ #
306
+ # Get value for `id_prefix` defined as String
307
+ #
308
+ # ==== Returns
309
+ #
310
+ # String:: value for `id_prefix` defined as String
311
+ #
312
+ def id_prefix_for_class
313
+ return if !id_prefix_defined? || id_prefix_requires_instance?
314
+
315
+ @id_prefix_extractor.value_for(nil)
316
+ end
317
+
318
+ #
319
+ # Check if `id_prefix` is defined for class associated with this setup.
320
+ #
321
+ # ==== Returns
322
+ #
323
+ # Boolean:: True if class associated with this setup has defined `id_prefix`
324
+ #
325
+ def id_prefix_defined?
326
+ !@id_prefix_extractor.nil?
327
+ end
328
+
329
+ #
330
+ # Check if instance is required to get `id_prefix` value (instance is required for Proc and
331
+ # Symbol `id_prefix` only. Value for String `id_prefix` can be get on class level)
332
+ #
333
+ # ==== Returns
334
+ #
335
+ # Boolean:: True if instance is required to get `id_prefix` value
336
+ #
337
+ def id_prefix_requires_instance?
338
+ return false unless id_prefix_defined?
339
+
340
+ !@id_prefix_extractor.is_a?(DataExtractor::Constant)
341
+ end
342
+
305
343
  protected
306
344
 
307
345
  #
data/lib/sunspot/util.rb CHANGED
@@ -190,22 +190,15 @@ module Sunspot
190
190
 
191
191
  def parse_json_facet(field_name, options, setup)
192
192
  field = setup.field(field_name)
193
- if options[:time_range]
194
- unless field.type.is_a?(Sunspot::Type::TimeType)
195
- raise(
196
- ArgumentError,
197
- ':time_range can only be specified for Date or Time fields'
198
- )
199
- end
200
- Sunspot::Query::DateFieldJsonFacet.new(field, options, setup)
201
- elsif options[:range]
193
+ if options[:range] || options[:time_range]
202
194
  unless [Sunspot::Type::TimeType, Sunspot::Type::FloatType, Sunspot::Type::IntegerType ].find{|type| field.type.is_a?(type)}
203
195
  raise(
204
196
  ArgumentError,
205
- ':range can only be specified for date or numeric fields'
197
+ ':range can only be specified for date, time, or numeric fields'
206
198
  )
207
199
  end
208
- Sunspot::Query::RangeJsonFacet.new(field, options, setup)
200
+ facet_klass = field.type.is_a?(Sunspot::Type::TimeType) ? Sunspot::Query::DateFieldJsonFacet : Sunspot::Query::RangeJsonFacet
201
+ facet_klass.new(field, options, setup)
209
202
  else
210
203
  Sunspot::Query::FieldJsonFacet.new(field, options, setup)
211
204
  end
@@ -1,3 +1,3 @@
1
1
  module Sunspot
2
- VERSION = '2.5.0'
2
+ VERSION = '2.6.0'
3
3
  end