sunspot 2.2.7 → 2.5.0
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 +5 -5
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Appraisals +7 -0
- data/Gemfile +0 -8
- data/lib/sunspot/adapters.rb +4 -1
- data/lib/sunspot/configuration.rb +1 -0
- data/lib/sunspot/data_extractor.rb +36 -6
- data/lib/sunspot/dsl/field_query.rb +11 -0
- data/lib/sunspot/dsl/field_stats.rb +7 -0
- data/lib/sunspot/dsl/fields.rb +16 -0
- data/lib/sunspot/dsl/group.rb +10 -0
- data/lib/sunspot/dsl/scope.rb +23 -18
- data/lib/sunspot/field.rb +11 -0
- data/lib/sunspot/field_factory.rb +6 -2
- data/lib/sunspot/query/abstract_json_field_facet.rb +70 -0
- data/lib/sunspot/query/bbox.rb +5 -1
- data/lib/sunspot/query/date_field_json_facet.rb +25 -0
- data/lib/sunspot/query/field_json_facet.rb +19 -0
- data/lib/sunspot/query/field_stats.rb +35 -2
- data/lib/sunspot/query/group.rb +4 -5
- data/lib/sunspot/query/join.rb +2 -4
- data/lib/sunspot/query/range_json_facet.rb +28 -0
- data/lib/sunspot/query/restriction.rb +19 -4
- data/lib/sunspot/query.rb +3 -3
- data/lib/sunspot/schema.rb +10 -2
- data/lib/sunspot/search/abstract_search.rb +14 -1
- data/lib/sunspot/search/field_json_facet.rb +33 -0
- data/lib/sunspot/search/hit.rb +6 -1
- data/lib/sunspot/search/hit_enumerable.rb +4 -1
- data/lib/sunspot/search/json_facet_row.rb +40 -0
- data/lib/sunspot/search/json_facet_stats.rb +23 -0
- data/lib/sunspot/search/standard_search.rb +2 -3
- data/lib/sunspot/search/stats_json_row.rb +82 -0
- data/lib/sunspot/search/stats_row.rb +3 -1
- data/lib/sunspot/search.rb +4 -3
- data/lib/sunspot/session.rb +13 -5
- data/lib/sunspot/setup.rb +31 -0
- data/lib/sunspot/util.rb +23 -0
- data/lib/sunspot/version.rb +1 -1
- data/spec/api/adapters_spec.rb +32 -19
- data/spec/api/batcher_spec.rb +15 -15
- data/spec/api/binding_spec.rb +3 -3
- data/spec/api/class_set_spec.rb +3 -3
- data/spec/api/data_extractor_spec.rb +39 -0
- data/spec/api/hit_enumerable_spec.rb +32 -9
- data/spec/api/indexer/attributes_spec.rb +31 -31
- data/spec/api/indexer/batch_spec.rb +8 -7
- data/spec/api/indexer/dynamic_fields_spec.rb +8 -8
- data/spec/api/indexer/fixed_fields_spec.rb +12 -12
- data/spec/api/indexer/fulltext_spec.rb +8 -8
- data/spec/api/indexer/removal_spec.rb +14 -14
- data/spec/api/indexer_spec.rb +2 -2
- data/spec/api/query/advanced_manipulation_examples.rb +3 -3
- data/spec/api/query/connectives_examples.rb +26 -14
- data/spec/api/query/dsl_spec.rb +17 -9
- data/spec/api/query/dynamic_fields_examples.rb +18 -18
- data/spec/api/query/faceting_examples.rb +62 -62
- data/spec/api/query/fulltext_examples.rb +63 -58
- data/spec/api/query/function_spec.rb +26 -26
- data/spec/api/query/geo_examples.rb +6 -6
- data/spec/api/query/group_spec.rb +6 -6
- data/spec/api/query/highlighting_examples.rb +26 -26
- data/spec/api/query/join_spec.rb +2 -2
- data/spec/api/query/more_like_this_spec.rb +29 -29
- data/spec/api/query/ordering_pagination_examples.rb +25 -25
- data/spec/api/query/scope_examples.rb +39 -39
- data/spec/api/query/spatial_examples.rb +3 -3
- data/spec/api/query/spellcheck_examples.rb +3 -3
- data/spec/api/query/standard_spec.rb +1 -1
- data/spec/api/query/stats_examples.rb +8 -8
- data/spec/api/query/text_field_scoping_examples.rb +5 -5
- data/spec/api/query/types_spec.rb +4 -4
- data/spec/api/search/cursor_paginated_collection_spec.rb +12 -12
- data/spec/api/search/dynamic_fields_spec.rb +4 -4
- data/spec/api/search/faceting_spec.rb +55 -52
- data/spec/api/search/highlighting_spec.rb +7 -7
- data/spec/api/search/hits_spec.rb +43 -29
- data/spec/api/search/paginated_collection_spec.rb +18 -18
- data/spec/api/search/results_spec.rb +13 -13
- data/spec/api/search/search_spec.rb +3 -3
- data/spec/api/search/stats_spec.rb +10 -10
- data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +19 -18
- data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +9 -9
- data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +10 -6
- data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +10 -10
- data/spec/api/session_proxy/sharding_session_proxy_spec.rb +14 -13
- data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +2 -2
- data/spec/api/session_proxy/spec_helper.rb +1 -1
- data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +9 -5
- data/spec/api/session_spec.rb +42 -42
- data/spec/api/sunspot_spec.rb +7 -4
- data/spec/helpers/integration_helper.rb +1 -0
- data/spec/integration/atomic_updates_spec.rb +25 -11
- data/spec/integration/dynamic_fields_spec.rb +10 -10
- data/spec/integration/faceting_spec.rb +252 -39
- data/spec/integration/field_grouping_spec.rb +35 -16
- data/spec/integration/field_lists_spec.rb +57 -0
- data/spec/integration/geospatial_spec.rb +34 -8
- data/spec/integration/highlighting_spec.rb +5 -5
- data/spec/integration/indexing_spec.rb +5 -5
- data/spec/integration/join_spec.rb +45 -0
- data/spec/integration/keyword_search_spec.rb +47 -45
- data/spec/integration/local_search_spec.rb +4 -4
- data/spec/integration/more_like_this_spec.rb +7 -7
- data/spec/integration/scoped_search_spec.rb +108 -108
- data/spec/integration/spellcheck_spec.rb +52 -7
- data/spec/integration/stats_spec.rb +54 -13
- data/spec/integration/stored_fields_spec.rb +1 -1
- data/spec/integration/test_pagination.rb +4 -4
- data/spec/integration/unicode_spec.rb +1 -1
- data/spec/mocks/adapters.rb +33 -0
- data/spec/mocks/photo.rb +14 -4
- data/spec/mocks/post.rb +9 -1
- data/spec/spec_helper.rb +11 -10
- data/sunspot.gemspec +3 -1
- metadata +49 -6
@@ -16,7 +16,7 @@ module Sunspot
|
|
16
16
|
# Retrieve all facet objects defined for this search, in order they were
|
17
17
|
# defined. To retrieve an individual facet by name, use #facet()
|
18
18
|
#
|
19
|
-
attr_reader :facets, :groups
|
19
|
+
attr_reader :facets, :groups
|
20
20
|
attr_reader :query #:nodoc:
|
21
21
|
attr_accessor :request_handler
|
22
22
|
|
@@ -182,6 +182,10 @@ module Sunspot
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
+
def json_facet_stats(name, options = {})
|
186
|
+
JsonFacetStats.new(name, self, options)
|
187
|
+
end
|
188
|
+
|
185
189
|
#
|
186
190
|
# Deprecated in favor of optional second argument to #facet
|
187
191
|
#
|
@@ -193,6 +197,10 @@ module Sunspot
|
|
193
197
|
@solr_result['facet_counts']
|
194
198
|
end
|
195
199
|
|
200
|
+
def json_facet_response #:nodoc:
|
201
|
+
@solr_result['facets']
|
202
|
+
end
|
203
|
+
|
196
204
|
def stats_response #:nodoc:
|
197
205
|
@solr_result['stats']['stats_fields']
|
198
206
|
end
|
@@ -255,6 +263,11 @@ module Sunspot
|
|
255
263
|
add_stats(field.name, FieldStats.new(field, self))
|
256
264
|
end
|
257
265
|
|
266
|
+
def add_json_facet(field, options = {})
|
267
|
+
name = (options[:name] || field.name)
|
268
|
+
add_facet(name, FieldJsonFacet.new(field, self, options))
|
269
|
+
end
|
270
|
+
|
258
271
|
def highlights_for(doc) #:nodoc:
|
259
272
|
if @solr_result['highlighting']
|
260
273
|
@solr_result['highlighting'][doc['id']]
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
class FieldJsonFacet
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(field, search, options)
|
8
|
+
@name, @search, @options = name, search, options
|
9
|
+
@field = field
|
10
|
+
end
|
11
|
+
|
12
|
+
def rows
|
13
|
+
@rows ||=
|
14
|
+
begin
|
15
|
+
json_facet_response = @search.json_facet_response[@field.name.to_s]
|
16
|
+
data = json_facet_response.nil? ? [] : json_facet_response['buckets']
|
17
|
+
rows = []
|
18
|
+
data.each do |d|
|
19
|
+
rows << JsonFacetRow.new(d, self)
|
20
|
+
end
|
21
|
+
|
22
|
+
if @options[:sort] == :count
|
23
|
+
rows.sort! { |lrow, rrow| rrow.count <=> lrow.count }
|
24
|
+
else
|
25
|
+
rows.sort! { |lrow, rrow| lrow.value <=> rrow.value }
|
26
|
+
end
|
27
|
+
rows
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/sunspot/search/hit.rb
CHANGED
@@ -17,6 +17,10 @@ module Sunspot
|
|
17
17
|
# Class name of object associated with this hit, as string.
|
18
18
|
#
|
19
19
|
attr_reader :class_name
|
20
|
+
#
|
21
|
+
# ID prefix used for compositeId shard router
|
22
|
+
#
|
23
|
+
attr_reader :id_prefix
|
20
24
|
#
|
21
25
|
# Keyword relevance score associated with this result. Nil if this hit
|
22
26
|
# is not from a keyword search.
|
@@ -27,7 +31,8 @@ module Sunspot
|
|
27
31
|
attr_writer :result #:nodoc:
|
28
32
|
|
29
33
|
def initialize(raw_hit, highlights, search) #:nodoc:
|
30
|
-
@class_name, @primary_key =
|
34
|
+
@id_prefix, @class_name, @primary_key =
|
35
|
+
*raw_hit['id'].match(/((?:[^!]+!)+)*([^\s]+)\s(.+)/)[1..3]
|
31
36
|
@score = raw_hit['score']
|
32
37
|
@search = search
|
33
38
|
@stored_values = raw_hit
|
@@ -11,6 +11,9 @@ module Sunspot
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
#
|
15
|
+
# Returns all of the hits that have a result
|
16
|
+
#
|
14
17
|
def verified_hits
|
15
18
|
hits.select { |h| h.result }
|
16
19
|
end
|
@@ -31,7 +34,7 @@ module Sunspot
|
|
31
34
|
hits_for_class = id_hit_hash[class_name]
|
32
35
|
data_accessor.load_all(ids).each do |result|
|
33
36
|
hit = hits_for_class.delete(Adapters::InstanceAdapter.adapt(result).id.to_s)
|
34
|
-
hit.result = result
|
37
|
+
hit.result = result if hit
|
35
38
|
end
|
36
39
|
hits_for_class.values.each { |hit| hit.result = nil }
|
37
40
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
class JsonFacetRow
|
4
|
+
attr_reader :value, :count, :nested
|
5
|
+
attr_writer :instance #:nodoc:
|
6
|
+
|
7
|
+
def initialize(data, facet) #:nodoc:
|
8
|
+
@value = data['val']
|
9
|
+
@count = data['distinct'] || data['count']
|
10
|
+
@facet = facet
|
11
|
+
@nested_key = data.keys.select { |k| data[k].is_a?(Hash) }.first
|
12
|
+
@nested = recursive_nested_initialization(data) unless @nested_key.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# Return the instance referenced by this facet row. Only valid for field
|
17
|
+
# facets whose fields are defined with the :references key.
|
18
|
+
#
|
19
|
+
def instance
|
20
|
+
if !defined?(@instance)
|
21
|
+
@facet.populate_instances
|
22
|
+
end
|
23
|
+
@instance
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"<Sunspot::Search::FacetRow:#{value.inspect} (#{count}) #{nested.nil? ? '' : " nested_count=#{nested.size}"}>"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def recursive_nested_initialization(data)
|
33
|
+
data[@nested_key]['buckets'].map do |d|
|
34
|
+
JsonFacetRow.new(d, @facet)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
class JsonFacetStats
|
4
|
+
def initialize(field, search, options)
|
5
|
+
@field, @search, @options = field, search, options
|
6
|
+
end
|
7
|
+
|
8
|
+
def rows
|
9
|
+
@rows ||=
|
10
|
+
begin
|
11
|
+
json_facet_response = @search.json_facet_response[@field.to_s]
|
12
|
+
data = json_facet_response.nil? ? [] : json_facet_response['buckets']
|
13
|
+
rows = []
|
14
|
+
data.each do |d|
|
15
|
+
rows << StatsJsonRow.new(d, nil, d['val'])
|
16
|
+
end
|
17
|
+
rows
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -49,12 +49,11 @@ module Sunspot
|
|
49
49
|
# the index. Otherwise return Solr's suggested collation.
|
50
50
|
#
|
51
51
|
# Solr's suggested collation is more liberal, replacing even terms that
|
52
|
-
# are present in the index.
|
53
|
-
# misspelled and preventing useful results.
|
52
|
+
# are present in the index.
|
54
53
|
#
|
55
54
|
# Mix and match in your views for a blend of strict and liberal collations.
|
56
55
|
def spellcheck_collation(*terms)
|
57
|
-
if solr_spellcheck['suggestions'] && solr_spellcheck['suggestions'].length >
|
56
|
+
if solr_spellcheck['suggestions'] && solr_spellcheck['suggestions'].length > 0
|
58
57
|
collation = terms.join(" ").dup if terms
|
59
58
|
|
60
59
|
# If we are given a query string, tokenize it and strictly replace
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
class StatsJsonRow
|
4
|
+
attr_reader :data, :value, :nested
|
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
|
+
@nested_key = data.keys.select { |k| data[k].is_a?(Hash) }.first
|
11
|
+
@nested = recursive_nested_initialization(data) unless @nested_key.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
def min
|
15
|
+
data['min']
|
16
|
+
end
|
17
|
+
|
18
|
+
def max
|
19
|
+
data['max']
|
20
|
+
end
|
21
|
+
|
22
|
+
def count
|
23
|
+
data['count']
|
24
|
+
end
|
25
|
+
|
26
|
+
def sum
|
27
|
+
data['sum']
|
28
|
+
end
|
29
|
+
|
30
|
+
def missing
|
31
|
+
data['missing']
|
32
|
+
end
|
33
|
+
|
34
|
+
def sum_of_squares
|
35
|
+
data['sumsq']
|
36
|
+
end
|
37
|
+
alias :sumsq :sum_of_squares
|
38
|
+
|
39
|
+
def mean
|
40
|
+
data['avg']
|
41
|
+
end
|
42
|
+
alias :avg :mean
|
43
|
+
|
44
|
+
def standard_deviation
|
45
|
+
data['stddev']
|
46
|
+
end
|
47
|
+
|
48
|
+
def facet name
|
49
|
+
facets.find { |facet| facet.field.name == name.to_sym }
|
50
|
+
end
|
51
|
+
|
52
|
+
def facets
|
53
|
+
@facets ||= @facet_fields.map do |field|
|
54
|
+
StatsFacet.new(field, data['facets'][field.indexed_name])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def instance
|
59
|
+
if !defined?(@instance)
|
60
|
+
@facet.populate_instances
|
61
|
+
end
|
62
|
+
@instance
|
63
|
+
end
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
"<Sunspot::Search::StatsJsonRow:#{value.inspect} min=#{min} max=#{max}"\
|
67
|
+
" count=#{count} sum=#{sum} missing=#{missing} sum_of_squares=#{sum_of_squares}"\
|
68
|
+
" mean=#{mean} standard_deviation=#{standard_deviation}"\
|
69
|
+
" #{nested.nil? ? '' : "nested_count=#{nested.size}"}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def recursive_nested_initialization(data)
|
75
|
+
data[@nested_key]['buckets'].map do |d|
|
76
|
+
StatsJsonRow.new(d, @facet, d['val'])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -59,7 +59,9 @@ module Sunspot
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def inspect
|
62
|
-
"<Sunspot::Search::StatsRow:#{value.inspect} min=#{min} max=#{max}
|
62
|
+
"<Sunspot::Search::StatsRow:#{value.inspect} min=#{min} max=#{max}"\
|
63
|
+
" count=#{self.count} sum=#{sum} missing=#{missing} sum_of_squares=#{sum_of_squares}"\
|
64
|
+
" mean=#{mean} standard_deviation=#{standard_deviation}>"
|
63
65
|
end
|
64
66
|
end
|
65
67
|
end
|
data/lib/sunspot/search.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
%w(abstract_search standard_search more_like_this_search query_facet
|
2
|
-
date_facet range_facet
|
3
|
-
|
1
|
+
%w(abstract_search standard_search more_like_this_search query_facet
|
2
|
+
field_facet field_json_facet date_facet range_facet json_facet_stats
|
3
|
+
facet_row json_facet_row hit highlight field_group group hit_enumerable
|
4
|
+
stats_row stats_json_row field_stats stats_facet query_group).each do |file|
|
4
5
|
require File.join(File.dirname(__FILE__), 'search', file)
|
5
6
|
end
|
6
7
|
|
data/lib/sunspot/session.rb
CHANGED
@@ -254,11 +254,13 @@ module Sunspot
|
|
254
254
|
# RSolr::Connection::Base:: The connection for this session
|
255
255
|
#
|
256
256
|
def connection
|
257
|
-
@connection ||=
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
257
|
+
@connection ||= self.class.connection_class.connect(
|
258
|
+
url: config.solr.url,
|
259
|
+
read_timeout: config.solr.read_timeout,
|
260
|
+
open_timeout: config.solr.open_timeout,
|
261
|
+
proxy: config.solr.proxy,
|
262
|
+
update_format: update_format_generator
|
263
|
+
)
|
262
264
|
end
|
263
265
|
|
264
266
|
def indexer
|
@@ -275,5 +277,11 @@ module Sunspot
|
|
275
277
|
CompositeSetup.for(types)
|
276
278
|
end
|
277
279
|
end
|
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
|
284
|
+
end
|
285
|
+
end
|
278
286
|
end
|
279
287
|
end
|
data/lib/sunspot/setup.rb
CHANGED
@@ -15,6 +15,7 @@ module Sunspot
|
|
15
15
|
@more_like_this_field_factories_cache = Hash.new { |h, k| h[k] = [] }
|
16
16
|
@dsl = DSL::Fields.new(self)
|
17
17
|
@document_boost_extractor = nil
|
18
|
+
@id_prefix_extractor = nil
|
18
19
|
add_field_factory(:class, Type::ClassType.instance)
|
19
20
|
end
|
20
21
|
|
@@ -61,6 +62,7 @@ module Sunspot
|
|
61
62
|
field_factory = FieldFactory::Static.new(name, Type::TextType.instance, options, &block)
|
62
63
|
@text_field_factories[name] = field_factory
|
63
64
|
@text_field_factories_cache[field_factory.name] = field_factory
|
65
|
+
@field_factories_cache[field_factory.name] = field_factory
|
64
66
|
if stored
|
65
67
|
@stored_field_factories_cache[field_factory.name] << field_factory
|
66
68
|
end
|
@@ -81,6 +83,7 @@ module Sunspot
|
|
81
83
|
field_factory = FieldFactory::Dynamic.new(name, type, options, &block)
|
82
84
|
@dynamic_field_factories[field_factory.signature] = field_factory
|
83
85
|
@dynamic_field_factories_cache[field_factory.name] = field_factory
|
86
|
+
@field_factories_cache[field_factory.name] = field_factory
|
84
87
|
if stored
|
85
88
|
@stored_field_factories_cache[field_factory.name] << field_factory
|
86
89
|
end
|
@@ -107,6 +110,24 @@ module Sunspot
|
|
107
110
|
end
|
108
111
|
end
|
109
112
|
|
113
|
+
#
|
114
|
+
# Add id prefix for compositeId router
|
115
|
+
#
|
116
|
+
def add_id_prefix(attr_name, &block)
|
117
|
+
@id_prefix_extractor =
|
118
|
+
case attr_name
|
119
|
+
when Symbol
|
120
|
+
DataExtractor::AttributeExtractor.new(attr_name)
|
121
|
+
when String
|
122
|
+
DataExtractor::Constant.new(attr_name)
|
123
|
+
when nil
|
124
|
+
DataExtractor::BlockExtractor.new(&block) if block_given?
|
125
|
+
else
|
126
|
+
raise ArgumentError,
|
127
|
+
"The ID prefix has to be either a Symbol, a String or a Proc"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
110
131
|
#
|
111
132
|
# Builder method for evaluating the setup DSL
|
112
133
|
#
|
@@ -271,6 +292,16 @@ module Sunspot
|
|
271
292
|
end
|
272
293
|
end
|
273
294
|
|
295
|
+
def id_prefix_for(model)
|
296
|
+
if @id_prefix_extractor
|
297
|
+
value = @id_prefix_extractor.value_for(model)
|
298
|
+
|
299
|
+
if value.is_a?(String) and value.size > 0
|
300
|
+
value[-1] == "!" ? value : "#{value}!"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
274
305
|
protected
|
275
306
|
|
276
307
|
#
|
data/lib/sunspot/util.rb
CHANGED
@@ -188,6 +188,29 @@ module Sunspot
|
|
188
188
|
RSolr.solr_escape(value).gsub(/([\s\.])/, '\\\\\1')
|
189
189
|
end
|
190
190
|
|
191
|
+
def parse_json_facet(field_name, options, setup)
|
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]
|
202
|
+
unless [Sunspot::Type::TimeType, Sunspot::Type::FloatType, Sunspot::Type::IntegerType ].find{|type| field.type.is_a?(type)}
|
203
|
+
raise(
|
204
|
+
ArgumentError,
|
205
|
+
':range can only be specified for date or numeric fields'
|
206
|
+
)
|
207
|
+
end
|
208
|
+
Sunspot::Query::RangeJsonFacet.new(field, options, setup)
|
209
|
+
else
|
210
|
+
Sunspot::Query::FieldJsonFacet.new(field, options, setup)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
191
214
|
private
|
192
215
|
|
193
216
|
#
|
data/lib/sunspot/version.rb
CHANGED
data/spec/api/adapters_spec.rb
CHANGED
@@ -2,45 +2,58 @@ require File.expand_path('spec_helper', File.dirname(__FILE__))
|
|
2
2
|
|
3
3
|
describe Sunspot::Adapters::InstanceAdapter do
|
4
4
|
it "finds adapter by superclass" do
|
5
|
-
Sunspot::Adapters::InstanceAdapter::for(Model).
|
5
|
+
expect(Sunspot::Adapters::InstanceAdapter::for(Model)).to be(AbstractModelInstanceAdapter)
|
6
6
|
end
|
7
7
|
|
8
8
|
it "finds adapter by mixin" do
|
9
|
-
Sunspot::Adapters::InstanceAdapter::for(MixModel).
|
9
|
+
expect(Sunspot::Adapters::InstanceAdapter::for(MixModel)).to be(MixInModelInstanceAdapter)
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'throws NoAdapterError if anonymous module passed in' do
|
13
|
-
|
13
|
+
expect do
|
14
14
|
Sunspot::Adapters::InstanceAdapter::for(Module.new)
|
15
|
-
end.
|
15
|
+
end.to raise_error(Sunspot::NoAdapterError)
|
16
16
|
end
|
17
17
|
|
18
18
|
it "registers adapters found by ancestor lookup with the descendant class" do
|
19
|
-
Sunspot::Adapters::InstanceAdapter::registered_adapter_for(UnseenModel).
|
19
|
+
expect(Sunspot::Adapters::InstanceAdapter::registered_adapter_for(UnseenModel)).to be(nil)
|
20
20
|
Sunspot::Adapters::InstanceAdapter::for(UnseenModel)
|
21
|
-
Sunspot::Adapters::InstanceAdapter::registered_adapter_for(UnseenModel).
|
21
|
+
expect(Sunspot::Adapters::InstanceAdapter::registered_adapter_for(UnseenModel)).to be(AbstractModelInstanceAdapter)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "appends ID prefix when configured" do
|
25
|
+
expect(AbstractModelInstanceAdapter.new(ModelWithPrefixId.new).index_id).to eq "USERDATA!ModelWithPrefixId 1"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "supports nested ID prefixes" do
|
29
|
+
expect(AbstractModelInstanceAdapter.
|
30
|
+
new(ModelWithNestedPrefixId.new).index_id).to eq "USER!USERDATA!ModelWithNestedPrefixId 1"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "doesn't appends ID prefix when not configured" do
|
34
|
+
expect(AbstractModelInstanceAdapter.new(ModelWithoutPrefixId.new).index_id).to eq "ModelWithoutPrefixId 1"
|
22
35
|
end
|
23
36
|
end
|
24
37
|
|
25
38
|
describe Sunspot::Adapters::DataAccessor do
|
26
39
|
it "finds adapter by superclass" do
|
27
|
-
Sunspot::Adapters::DataAccessor::for(Model).
|
40
|
+
expect(Sunspot::Adapters::DataAccessor::for(Model)).to be(AbstractModelDataAccessor)
|
28
41
|
end
|
29
42
|
|
30
43
|
it "finds adapter by mixin" do
|
31
|
-
Sunspot::Adapters::DataAccessor::for(MixModel).
|
44
|
+
expect(Sunspot::Adapters::DataAccessor::for(MixModel)).to be(MixInModelDataAccessor)
|
32
45
|
end
|
33
46
|
|
34
47
|
it 'throws NoAdapterError if anonymous module passed in' do
|
35
|
-
|
48
|
+
expect do
|
36
49
|
Sunspot::Adapters::DataAccessor::for(Module.new)
|
37
|
-
end.
|
50
|
+
end.to raise_error(Sunspot::NoAdapterError)
|
38
51
|
end
|
39
52
|
|
40
53
|
it "registers adapters found by ancestor lookup with the descendant class" do
|
41
|
-
Sunspot::Adapters::DataAccessor::registered_accessor_for(UnseenModel).
|
54
|
+
expect(Sunspot::Adapters::DataAccessor::registered_accessor_for(UnseenModel)).to be(nil)
|
42
55
|
Sunspot::Adapters::DataAccessor::for(UnseenModel)
|
43
|
-
Sunspot::Adapters::DataAccessor::registered_accessor_for(UnseenModel).
|
56
|
+
expect(Sunspot::Adapters::DataAccessor::registered_accessor_for(UnseenModel)).to be(AbstractModelDataAccessor)
|
44
57
|
end
|
45
58
|
end
|
46
59
|
|
@@ -51,30 +64,30 @@ describe Sunspot::Adapters::Registry do
|
|
51
64
|
let(:mixin_accessor){ Sunspot::Adapters::DataAccessor::for(MixModel) }
|
52
65
|
|
53
66
|
it "registers and retrieves a data accessor for abstractclass" do
|
54
|
-
registry.retrieve(AbstractModel).
|
67
|
+
expect(registry.retrieve(AbstractModel)).to be_a(abstractclass_accessor)
|
55
68
|
end
|
56
69
|
|
57
70
|
it "registers and retrieves a data accessor for superclass" do
|
58
|
-
registry.retrieve(Model).
|
71
|
+
expect(registry.retrieve(Model)).to be_a(superclass_accessor)
|
59
72
|
end
|
60
73
|
|
61
74
|
it "registers and retrieves a data accessor for mixin" do
|
62
|
-
registry.retrieve(MixModel).
|
75
|
+
expect(registry.retrieve(MixModel)).to be_a(mixin_accessor)
|
63
76
|
end
|
64
77
|
|
65
78
|
it "injects inherited attributes" do
|
66
|
-
AbstractModelDataAccessor.
|
79
|
+
allow_any_instance_of(AbstractModelDataAccessor).to receive(:inherited_attributes).and_return([:to_be_injected])
|
67
80
|
in_registry_data_accessor = registry.retrieve(AbstractModel)
|
68
81
|
in_registry_data_accessor.to_be_injected = "value"
|
69
|
-
registry.retrieve(Model).to_be_injected.
|
82
|
+
expect(registry.retrieve(Model).to_be_injected).to eq("value")
|
70
83
|
end
|
71
84
|
|
72
85
|
it "not overrides inherited attributes" do
|
73
|
-
AbstractModelDataAccessor.
|
86
|
+
allow_any_instance_of(AbstractModelDataAccessor).to receive(:inherited_attributes).and_return([:to_be_injected])
|
74
87
|
parent_data_accessor = registry.retrieve(AbstractModel)
|
75
88
|
current_data_accessor = registry.retrieve(Model)
|
76
89
|
parent_data_accessor.to_be_injected = "value"
|
77
90
|
current_data_accessor.to_be_injected = "safe-value"
|
78
|
-
registry.retrieve(Model).to_be_injected.
|
91
|
+
expect(registry.retrieve(Model).to_be_injected).to eq("safe-value")
|
79
92
|
end
|
80
93
|
end
|
data/spec/api/batcher_spec.rb
CHANGED
@@ -2,12 +2,12 @@ require File.expand_path('spec_helper', File.dirname(__FILE__))
|
|
2
2
|
|
3
3
|
describe Sunspot::Batcher do
|
4
4
|
it "includes Enumerable" do
|
5
|
-
described_class.
|
5
|
+
expect(described_class).to include Enumerable
|
6
6
|
end
|
7
7
|
|
8
8
|
describe "#each" do
|
9
9
|
let(:current) { [:foo, :bar] }
|
10
|
-
before { subject.
|
10
|
+
before { allow(subject).to receive(:current).and_return current }
|
11
11
|
|
12
12
|
it "iterates over current" do
|
13
13
|
yielded_values = []
|
@@ -16,25 +16,25 @@ describe Sunspot::Batcher do
|
|
16
16
|
yielded_values << value
|
17
17
|
end
|
18
18
|
|
19
|
-
yielded_values.
|
19
|
+
expect(yielded_values).to eq current
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
describe "adding to current batch" do
|
24
24
|
it "#push pushes to current" do
|
25
25
|
subject.push :foo
|
26
|
-
subject.current.
|
26
|
+
expect(subject.current).to include :foo
|
27
27
|
end
|
28
28
|
|
29
29
|
it "#<< pushes to current" do
|
30
30
|
subject.push :foo
|
31
|
-
subject.current.
|
31
|
+
expect(subject.current).to include :foo
|
32
32
|
end
|
33
33
|
|
34
34
|
it "#concat concatinates on current batch" do
|
35
35
|
subject << :foo
|
36
36
|
subject.concat [:bar, :mix]
|
37
|
-
|
37
|
+
is_expected.to include :foo, :bar, :mix
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -46,7 +46,7 @@ describe Sunspot::Batcher do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it "is empty by default" do
|
49
|
-
subject.current.
|
49
|
+
expect(subject.current).to be_empty
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -58,7 +58,7 @@ describe Sunspot::Batcher do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
it "returns the same as last time" do
|
61
|
-
subject.current.
|
61
|
+
expect(subject.current).to eq subject.current
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -71,7 +71,7 @@ describe Sunspot::Batcher do
|
|
71
71
|
it "changes current" do
|
72
72
|
subject << :foo
|
73
73
|
subject.start_new
|
74
|
-
|
74
|
+
is_expected.not_to include :foo
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -88,25 +88,25 @@ describe Sunspot::Batcher do
|
|
88
88
|
it "changes current" do
|
89
89
|
subject << :foo
|
90
90
|
subject.end_current
|
91
|
-
|
91
|
+
is_expected.not_to include :foo
|
92
92
|
end
|
93
93
|
|
94
94
|
it "returns current" do
|
95
95
|
subject << :foo
|
96
|
-
subject.end_current.
|
96
|
+
expect(subject.end_current).to include :foo
|
97
97
|
end
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
101
|
describe "#batching?" do
|
102
102
|
it "is false when depth is 0" do
|
103
|
-
subject.
|
104
|
-
|
103
|
+
expect(subject).to receive(:depth).and_return 0
|
104
|
+
is_expected.not_to be_batching
|
105
105
|
end
|
106
106
|
|
107
107
|
it "is true when depth is more than 0" do
|
108
|
-
subject.
|
109
|
-
|
108
|
+
expect(subject).to receive(:depth).and_return 1
|
109
|
+
is_expected.to be_batching
|
110
110
|
end
|
111
111
|
end
|
112
112
|
end
|
data/spec/api/binding_spec.rb
CHANGED
@@ -6,7 +6,7 @@ describe "DSL bindings" do
|
|
6
6
|
session.search(Post) do
|
7
7
|
value = test_method
|
8
8
|
end
|
9
|
-
value.
|
9
|
+
expect(value).to eq('value')
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'should give access to calling context\'s id method in search DSL' do
|
@@ -14,7 +14,7 @@ describe "DSL bindings" do
|
|
14
14
|
session.search(Post) do
|
15
15
|
value = id
|
16
16
|
end
|
17
|
-
value.
|
17
|
+
expect(value).to eq(16)
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'should give access to calling context\'s methods in nested DSL block' do
|
@@ -24,7 +24,7 @@ describe "DSL bindings" do
|
|
24
24
|
value = test_method
|
25
25
|
end
|
26
26
|
end
|
27
|
-
value.
|
27
|
+
expect(value).to eq('value')
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'should give access to calling context\'s methods in double-nested DSL block' do
|