sunspot 2.2.0 → 2.2.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/lib/sunspot/dsl/field_query.rb +11 -9
- data/lib/sunspot/dsl/fulltext.rb +9 -1
- data/lib/sunspot/dsl/group.rb +108 -0
- data/lib/sunspot/dsl/search.rb +1 -1
- data/lib/sunspot/dsl.rb +1 -1
- data/lib/sunspot/field_factory.rb +28 -15
- data/lib/sunspot/indexer.rb +63 -17
- data/lib/sunspot/query/abstract_fulltext.rb +9 -2
- data/lib/sunspot/query/dismax.rb +11 -4
- data/lib/sunspot/query/{field_group.rb → group.rb} +16 -6
- data/lib/sunspot/query/group_query.rb +17 -0
- data/lib/sunspot/query/restriction.rb +45 -1
- data/lib/sunspot/query.rb +1 -1
- data/lib/sunspot/schema.rb +2 -1
- data/lib/sunspot/search/abstract_search.rb +7 -3
- data/lib/sunspot/search/query_group.rb +74 -0
- data/lib/sunspot/search/standard_search.rb +1 -1
- data/lib/sunspot/search.rb +1 -1
- data/lib/sunspot/session.rb +16 -0
- data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +2 -2
- data/lib/sunspot/session_proxy/retry_5xx_session_proxy.rb +1 -1
- data/lib/sunspot/session_proxy/sharding_session_proxy.rb +4 -2
- data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +1 -1
- data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +3 -1
- data/lib/sunspot/type.rb +20 -0
- data/lib/sunspot/version.rb +1 -1
- data/lib/sunspot.rb +36 -2
- data/spec/api/indexer/attributes_spec.rb +5 -0
- data/spec/api/query/function_spec.rb +103 -0
- data/spec/api/query/group_spec.rb +23 -1
- data/spec/api/session_proxy/sharding_session_proxy_spec.rb +1 -1
- data/spec/helpers/integration_helper.rb +9 -0
- data/spec/integration/atomic_updates_spec.rb +44 -0
- data/spec/integration/field_grouping_spec.rb +13 -0
- data/spec/integration/scoped_search_spec.rb +51 -0
- data/spec/mocks/post.rb +3 -1
- metadata +11 -15
- data/lib/sunspot/dsl/field_group.rb +0 -57
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'sunspot/search/paginated_collection'
|
2
|
+
|
3
|
+
module Sunspot
|
4
|
+
module Search
|
5
|
+
class QueryGroup
|
6
|
+
def initialize(queries, search) #:nodoc:
|
7
|
+
@queries, @search = queries, search
|
8
|
+
end
|
9
|
+
|
10
|
+
def groups
|
11
|
+
@groups ||=
|
12
|
+
if solr_response_for_first
|
13
|
+
groups = @queries.map { |query| Group.new(query.label, doclist_for(query), @search) }
|
14
|
+
|
15
|
+
paginate_collection(groups)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def matches
|
20
|
+
if solr_response_for_first
|
21
|
+
solr_response_for_first['matches'].to_i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def total
|
26
|
+
@queries.count
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# It populates all grouped hits at once.
|
31
|
+
# Useful for eager loading fall grouped results at once.
|
32
|
+
#
|
33
|
+
def populate_all_hits
|
34
|
+
# Init a 2 dimension Hash that contains an array per key
|
35
|
+
id_hit_hash = Hash.new { |hash, key| hash[key] = Hash.new{ |h, k| h[k] = [] } }
|
36
|
+
groups.each do |g|
|
37
|
+
# Take all hits to being populated later on
|
38
|
+
g.hits.each do |hit|
|
39
|
+
id_hit_hash[hit.class_name][hit.primary_key] |= [hit]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
# Go for each class and load the results' objects into each of the hits
|
43
|
+
id_hit_hash.each_pair do |class_name, many_hits|
|
44
|
+
ids = many_hits.keys
|
45
|
+
data_accessor = @search.data_accessor_for(Util.full_const_get(class_name))
|
46
|
+
hits_for_class = id_hit_hash[class_name]
|
47
|
+
data_accessor.load_all(ids).each do |result|
|
48
|
+
hits = hits_for_class.delete(Adapters::InstanceAdapter.adapt(result).id.to_s)
|
49
|
+
hits.each{ |hit| hit.result = result }
|
50
|
+
end
|
51
|
+
hits_for_class.values.each { |hits| hits.each{|hit| hit.result = nil } }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def doclist_for(query)
|
58
|
+
solr_response_for(query.to_boolean_phrase)['doclist']
|
59
|
+
end
|
60
|
+
|
61
|
+
def solr_response_for(query_string)
|
62
|
+
@search.group_response[query_string]
|
63
|
+
end
|
64
|
+
|
65
|
+
def solr_response_for_first
|
66
|
+
solr_response_for(@queries[0].to_boolean_phrase)
|
67
|
+
end
|
68
|
+
|
69
|
+
def paginate_collection(collection)
|
70
|
+
PaginatedCollection.new(collection, @search.query.page, @search.query.per_page, total)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -70,7 +70,7 @@ module Sunspot
|
|
70
70
|
# If no query was given, or all terms are present in the index,
|
71
71
|
# return Solr's suggested collation.
|
72
72
|
if terms.length == 0
|
73
|
-
collation = solr_spellcheck['
|
73
|
+
collation = solr_spellcheck['collations'][-1]
|
74
74
|
end
|
75
75
|
|
76
76
|
collation
|
data/lib/sunspot/search.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
%w(abstract_search standard_search more_like_this_search query_facet field_facet
|
2
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
|
+
stats_row field_stats stats_facet query_group).each do |file|
|
4
4
|
require File.join(File.dirname(__FILE__), 'search', file)
|
5
5
|
end
|
6
6
|
|
data/lib/sunspot/session.rb
CHANGED
@@ -99,6 +99,22 @@ module Sunspot
|
|
99
99
|
commit
|
100
100
|
end
|
101
101
|
|
102
|
+
#
|
103
|
+
# See Sunspot.atomic_update
|
104
|
+
#
|
105
|
+
def atomic_update(clazz, updates = {})
|
106
|
+
@adds += updates.keys.length
|
107
|
+
indexer.add_atomic_update(clazz, updates)
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# See Sunspot.atomic_update!
|
112
|
+
#
|
113
|
+
def atomic_update!(clazz, updates = {})
|
114
|
+
atomic_update(clazz, updates)
|
115
|
+
commit
|
116
|
+
end
|
117
|
+
|
102
118
|
#
|
103
119
|
# See Sunspot.commit
|
104
120
|
#
|
@@ -19,8 +19,8 @@ module Sunspot
|
|
19
19
|
|
20
20
|
delegate :batch, :commit, :commit_if_delete_dirty, :commit_if_dirty,
|
21
21
|
:config, :delete_dirty?, :dirty?, :index, :index!, :optimize, :remove,
|
22
|
-
:remove!, :remove_all, :remove_all!, :remove_by_id,
|
23
|
-
:
|
22
|
+
:remove!, :remove_all, :remove_all!, :remove_by_id, :remove_by_id!,
|
23
|
+
:atomic_update, :atomic_update!, :to => :master_session
|
24
24
|
delegate :new_search, :search, :new_more_like_this, :more_like_this, :to => :slave_session
|
25
25
|
|
26
26
|
def initialize(master_session, slave_session)
|
@@ -59,7 +59,7 @@ module Sunspot
|
|
59
59
|
|
60
60
|
delegate :batch, :commit, :commit_if_dirty, :commit_if_delete_dirty,
|
61
61
|
:dirty?, :index!, :index, :optimize, :remove!, :remove, :remove_all!,
|
62
|
-
:remove_all, :remove_by_id!, :remove_by_id,
|
62
|
+
:remove_all, :remove_by_id!, :remove_by_id, :atomic_update, :atomic_update!,
|
63
63
|
:to => :retry_handler
|
64
64
|
|
65
65
|
end
|
@@ -27,9 +27,11 @@ module Sunspot
|
|
27
27
|
# * remove_by_id!
|
28
28
|
# * remove_all with an argument
|
29
29
|
# * remove_all! with an argument
|
30
|
+
# * atomic_update with arguments
|
31
|
+
# * atomic_update! with arguments
|
30
32
|
#
|
31
33
|
class ShardingSessionProxy < AbstractSessionProxy
|
32
|
-
not_supported :batch, :config, :remove_by_id, :remove_by_id!
|
34
|
+
not_supported :batch, :config, :remove_by_id, :remove_by_id!, :atomic_update, :atomic_update!
|
33
35
|
|
34
36
|
#
|
35
37
|
# +search_session+ is the session that should be used for searching.
|
@@ -70,7 +72,7 @@ module Sunspot
|
|
70
72
|
using_sharded_session(objects) { |session, group| session.index!(group) }
|
71
73
|
end
|
72
74
|
|
73
|
-
#
|
75
|
+
#
|
74
76
|
# See Sunspot.remove
|
75
77
|
#
|
76
78
|
def remove(*objects)
|
@@ -22,7 +22,7 @@ module Sunspot
|
|
22
22
|
SUPPORTED_METHODS = [
|
23
23
|
:batch, :commit, :commit_if_dirty, :commit_if_delete_dirty, :dirty?,
|
24
24
|
:index!, :index, :optimize, :remove!, :remove, :remove_all!, :remove_all,
|
25
|
-
:remove_by_id!, :remove_by_id
|
25
|
+
:remove_by_id!, :remove_by_id, :atomic_update, :atomic_update!
|
26
26
|
]
|
27
27
|
|
28
28
|
SUPPORTED_METHODS.each do |method|
|
@@ -17,7 +17,9 @@ module Sunspot
|
|
17
17
|
attr_reader :config
|
18
18
|
@@next_id = 0
|
19
19
|
|
20
|
-
delegate :batch, :commit, :commit_if_delete_dirty, :commit_if_dirty, :delete_dirty?, :dirty?, :index, :index!,
|
20
|
+
delegate :batch, :commit, :commit_if_delete_dirty, :commit_if_dirty, :delete_dirty?, :dirty?, :index, :index!,
|
21
|
+
:new_search, :optimize, :remove, :remove!, :remove_all, :remove_all!, :remove_by_id, :remove_by_id!,
|
22
|
+
:search, :more_like_this, :new_more_like_this, :atomic_update, :atomic_update!, :to => :session
|
21
23
|
|
22
24
|
#
|
23
25
|
# Optionally pass an existing Sunspot::Configuration object. If none is
|
data/lib/sunspot/type.rb
CHANGED
@@ -375,6 +375,26 @@ module Sunspot
|
|
375
375
|
end
|
376
376
|
end
|
377
377
|
|
378
|
+
class DateRangeType < DateType
|
379
|
+
def indexed_name(name)
|
380
|
+
"#{name}_dr"
|
381
|
+
end
|
382
|
+
|
383
|
+
def to_indexed(value)
|
384
|
+
if value.respond_to?(:first) && value.respond_to?(:last)
|
385
|
+
"[#{super value.first} TO #{super value.last}]"
|
386
|
+
else
|
387
|
+
super value
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def cast(value)
|
392
|
+
return super unless m = value.match(/^\[(?<start>.+) TO (?<end>.+)\]$/)
|
393
|
+
Range.new super(m[:start]), super(m[:end])
|
394
|
+
end
|
395
|
+
end
|
396
|
+
register DateRangeType, Range
|
397
|
+
|
378
398
|
class ClassType < AbstractType
|
379
399
|
def indexed_name(name) #:nodoc:
|
380
400
|
'class_name'
|
data/lib/sunspot/version.rb
CHANGED
data/lib/sunspot.rb
CHANGED
@@ -196,6 +196,40 @@ module Sunspot
|
|
196
196
|
session.index!(*objects)
|
197
197
|
end
|
198
198
|
|
199
|
+
# Atomic update object properties on the singleton session.
|
200
|
+
#
|
201
|
+
# ==== Parameters
|
202
|
+
#
|
203
|
+
# clazz<Class>:: the class of the objects to be updated
|
204
|
+
# updates<Hash>:: hash of updates where keys are model ids
|
205
|
+
# and values are hash with property name/values to be updated
|
206
|
+
#
|
207
|
+
# ==== Example
|
208
|
+
#
|
209
|
+
# post1, post2 = new Array(2) { Post.create }
|
210
|
+
# Sunspot.atomic_update(Post, post1.id => {title: 'New Title'}, post2.id => {description: 'new description'})
|
211
|
+
#
|
212
|
+
# Note that indexed objects won't be reflected in search until a commit is
|
213
|
+
# sent - see Sunspot.index! and Sunspot.commit
|
214
|
+
#
|
215
|
+
def atomic_update(clazz, updates = {})
|
216
|
+
session.atomic_update(clazz, updates)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Atomic update object properties on the singleton session.
|
220
|
+
#
|
221
|
+
# See: Sunspot.atomic_update and Sunspot.commit
|
222
|
+
#
|
223
|
+
# ==== Parameters
|
224
|
+
#
|
225
|
+
# clazz<Class>:: the class of the objects to be updated
|
226
|
+
# updates<Hash>:: hash of updates where keys are model ids
|
227
|
+
# and values are hash with property name/values to be updated
|
228
|
+
#
|
229
|
+
def atomic_update!(clazz, updates = {})
|
230
|
+
session.atomic_update!(clazz, updates)
|
231
|
+
end
|
232
|
+
|
199
233
|
# Commits (soft or hard) the singleton session
|
200
234
|
#
|
201
235
|
# When documents are added to or removed from Solr, the changes are
|
@@ -490,9 +524,9 @@ module Sunspot
|
|
490
524
|
#
|
491
525
|
# Sunspot.batch do
|
492
526
|
# post = Post.new
|
493
|
-
# Sunspot.
|
527
|
+
# Sunspot.index(post)
|
494
528
|
# comment = Comment.new
|
495
|
-
# Sunspot.
|
529
|
+
# Sunspot.index(comment)
|
496
530
|
# end
|
497
531
|
#
|
498
532
|
# Sunspot will send both the post and the comment in a single request.
|
@@ -71,6 +71,11 @@ describe 'indexing attribute fields', :type => :indexer do
|
|
71
71
|
connection.should have_add_with(:expire_date_d => '2009-07-13T00:00:00Z')
|
72
72
|
end
|
73
73
|
|
74
|
+
it 'should correctly index a date range field' do
|
75
|
+
session.index(post(:featured_for => Date.new(2009, 07, 13)..Date.new(2009, 12, 25)))
|
76
|
+
connection.should have_add_with(:featured_for_dr => '[2009-07-13T00:00:00Z TO 2009-12-25T00:00:00Z]')
|
77
|
+
end
|
78
|
+
|
74
79
|
it 'should correctly index a boolean field' do
|
75
80
|
session.index(post(:featured => true))
|
76
81
|
connection.should have_add_with(:featured_bs => 'true')
|
@@ -102,5 +102,108 @@ describe 'function query' do
|
|
102
102
|
end
|
103
103
|
end.should raise_error(Sunspot::UnrecognizedFieldError)
|
104
104
|
end
|
105
|
+
|
106
|
+
it "should send query to solr with multiplicative boost function" do
|
107
|
+
session.search Post do
|
108
|
+
keywords('pizza') do
|
109
|
+
multiplicative_boost(function { :average_rating })
|
110
|
+
end
|
111
|
+
end
|
112
|
+
connection.should have_last_search_including(:boost, 'average_rating_ft')
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should send query to solr with multiplicative boost function and boost amount" do
|
116
|
+
session.search Post do
|
117
|
+
keywords('pizza') do
|
118
|
+
multiplicative_boost(function { :average_rating }^5)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
connection.should have_last_search_including(:boost, 'average_rating_ft^5')
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should handle multiplicative boost function with constant float" do
|
125
|
+
session.search Post do
|
126
|
+
keywords('pizza') do
|
127
|
+
multiplicative_boost(function { 10.5 })
|
128
|
+
end
|
129
|
+
end
|
130
|
+
connection.should have_last_search_including(:boost, '10.5')
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should handle multiplicative boost function with constant float and boost amount" do
|
134
|
+
session.search Post do
|
135
|
+
keywords('pizza') do
|
136
|
+
multiplicative_boost(function { 10.5 }^5)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
connection.should have_last_search_including(:boost, '10.5^5')
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should handle multiplicative boost function with time literal" do
|
143
|
+
session.search Post do
|
144
|
+
keywords('pizza') do
|
145
|
+
multiplicative_boost(function { Time.parse('2010-03-25 14:13:00 EDT') })
|
146
|
+
end
|
147
|
+
end
|
148
|
+
connection.should have_last_search_including(:boost, '2010-03-25T18:13:00Z')
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should handle arbitrary functions in a function query block" do
|
152
|
+
session.search Post do
|
153
|
+
keywords('pizza') do
|
154
|
+
multiplicative_boost(function { product(:average_rating, 10) })
|
155
|
+
end
|
156
|
+
end
|
157
|
+
connection.should have_last_search_including(:boost, 'product(average_rating_ft,10)')
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should handle the sub function in a multiplicative boost function query block" do
|
161
|
+
session.search Post do
|
162
|
+
keywords('pizza') do
|
163
|
+
multiplicative_boost(function { sub(:average_rating, 10) })
|
164
|
+
end
|
165
|
+
end
|
166
|
+
connection.should have_last_search_including(:boost, 'sub(average_rating_ft,10)')
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should handle boost amounts on multiplicative boost function query block" do
|
170
|
+
session.search Post do
|
171
|
+
keywords('pizza') do
|
172
|
+
multiplicative_boost(function { sub(:average_rating, 10)^5 })
|
173
|
+
end
|
174
|
+
end
|
175
|
+
connection.should have_last_search_including(:boost, 'sub(average_rating_ft,10)^5')
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should handle nested functions in a multiplicative boost function query block" do
|
179
|
+
session.search Post do
|
180
|
+
keywords('pizza') do
|
181
|
+
multiplicative_boost(function { product(:average_rating, sum(:average_rating, 20)) })
|
182
|
+
end
|
183
|
+
end
|
184
|
+
connection.should have_last_search_including(:boost, 'product(average_rating_ft,sum(average_rating_ft,20))')
|
185
|
+
end
|
186
|
+
|
187
|
+
# TODO SOLR 1.5
|
188
|
+
it "should raise ArgumentError if string literal passed to multiplicative boost" do
|
189
|
+
lambda do
|
190
|
+
session.search Post do
|
191
|
+
keywords('pizza') do
|
192
|
+
multiplicative_boost(function { "hello world" })
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end.should raise_error(ArgumentError)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should raise UnrecognizedFieldError if bogus field name passed to multiplicative boost" do
|
199
|
+
lambda do
|
200
|
+
session.search Post do
|
201
|
+
keywords('pizza') do
|
202
|
+
multiplicative_boost(function { :bogus })
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end.should raise_error(Sunspot::UnrecognizedFieldError)
|
206
|
+
end
|
207
|
+
|
105
208
|
end
|
106
209
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
2
|
|
3
|
-
describe "
|
3
|
+
describe "grouping" do
|
4
4
|
it "sends grouping parameters to solr" do
|
5
5
|
session.search Post do
|
6
6
|
group :title
|
@@ -29,4 +29,26 @@ describe "field grouping" do
|
|
29
29
|
|
30
30
|
connection.should have_last_search_including(:"group.sort", "average_rating_ft asc")
|
31
31
|
end
|
32
|
+
|
33
|
+
it "sends grouping field parameters to solr" do
|
34
|
+
session.search Post do
|
35
|
+
group do
|
36
|
+
field :title
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
connection.should have_last_search_including(:"group.field", "title_ss")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "sends grouping query parameters to solr" do
|
44
|
+
session.search Post do
|
45
|
+
group do
|
46
|
+
query 'category 1' do
|
47
|
+
with(:category_ids, 1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
connection.should have_last_search_including(:"group.query", "category_ids_im:1")
|
53
|
+
end
|
32
54
|
end
|
@@ -15,7 +15,7 @@ describe Sunspot::SessionProxy::ShardingSessionProxy do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
[:remove_by_id, :remove_by_id!].each do |method|
|
18
|
+
[:remove_by_id, :remove_by_id!, :atomic_update, :atomic_update!].each do |method|
|
19
19
|
it "should raise NotSupportedError when #{method} called" do
|
20
20
|
lambda { @proxy.send(method, Post, 1) }.should raise_error(Sunspot::SessionProxy::NotSupportedError)
|
21
21
|
end
|
@@ -5,4 +5,13 @@ module IntegrationHelper
|
|
5
5
|
Sunspot.reset!(true)
|
6
6
|
end
|
7
7
|
end
|
8
|
+
|
9
|
+
def featured_for_posts(method, param, negated = false)
|
10
|
+
with_method = negated ? :without : :with
|
11
|
+
param = date_ranges[param] if param.is_a? String
|
12
|
+
|
13
|
+
Sunspot.search(Post) do
|
14
|
+
send(with_method, :featured_for).send(method, param)
|
15
|
+
end.results
|
16
|
+
end
|
8
17
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe 'atomic updates' do
|
4
|
+
before :all do
|
5
|
+
Sunspot.remove_all
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate_hit(hit, title, featured)
|
9
|
+
hit.stored(:title).should == title
|
10
|
+
hit.stored(:featured).should == featured
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_indexed_post(id)
|
14
|
+
hit = Sunspot.search(Post).hits.find{ |h| h.primary_key.to_i == id }
|
15
|
+
hit.should_not be_nil
|
16
|
+
hit
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should update single record fields one by one' do
|
20
|
+
post = Post.new(title: 'A Title', featured: true)
|
21
|
+
Sunspot.index!(post)
|
22
|
+
|
23
|
+
validate_hit(find_indexed_post(post.id), post.title, post.featured)
|
24
|
+
|
25
|
+
Sunspot.atomic_update!(Post, post.id => {title: 'A New Title'})
|
26
|
+
validate_hit(find_indexed_post(post.id), 'A New Title', true)
|
27
|
+
|
28
|
+
Sunspot.atomic_update!(Post, post.id => {featured: false})
|
29
|
+
validate_hit(find_indexed_post(post.id), 'A New Title', false)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should update fields for multiple records' do
|
33
|
+
post1 = Post.new(title: 'A First Title', featured: true)
|
34
|
+
post2 = Post.new(title: 'A Second Title', featured: false)
|
35
|
+
Sunspot.index!(post1, post2)
|
36
|
+
|
37
|
+
validate_hit(find_indexed_post(post1.id), post1.title, post1.featured)
|
38
|
+
validate_hit(find_indexed_post(post2.id), post2.title, post2.featured)
|
39
|
+
|
40
|
+
Sunspot.atomic_update!(Post, post1.id => {title: 'A New Title'}, post2.id => {featured: true})
|
41
|
+
validate_hit(find_indexed_post(post1.id), 'A New Title', true)
|
42
|
+
validate_hit(find_indexed_post(post2.id), 'A Second Title', true)
|
43
|
+
end
|
44
|
+
end
|
@@ -72,6 +72,19 @@ describe "field grouping" do
|
|
72
72
|
title1_group.hits.first.primary_key.to_i.should == highest_ranked_post.id
|
73
73
|
end
|
74
74
|
|
75
|
+
it "allows specification of an ordering function within groups" do
|
76
|
+
search = Sunspot.search(Post) do
|
77
|
+
group :title do
|
78
|
+
order_by_function(:product, :average_rating, -2, :asc)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
highest_ranked_post = @posts.sort_by { |p| -p.ratings_average }.first
|
83
|
+
|
84
|
+
title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
|
85
|
+
title1_group.hits.first.primary_key.to_i.should == highest_ranked_post.id
|
86
|
+
end
|
87
|
+
|
75
88
|
it "allows pagination within groups" do
|
76
89
|
search = Sunspot.search(Post) do
|
77
90
|
group :title
|
@@ -127,6 +127,57 @@ describe 'scoped_search' do
|
|
127
127
|
test_field_type 'Trie Time', :created_at, :created_at, Photo, *(['1970-01-01 00:00:00 UTC', '1983-07-08 04:00:00 UTC', '1983-07-08 02:00:00 -0500',
|
128
128
|
'2005-11-05 10:00:00 UTC', Time.now.to_s].map { |t| Time.parse(t) })
|
129
129
|
|
130
|
+
describe 'Date range field type' do
|
131
|
+
let(:january) { Date.new(2015,1,1)..Date.new(2015,1,31) }
|
132
|
+
let(:february) { Date.new(2015,2,1)..Date.new(2015,2,28) }
|
133
|
+
let(:date_ranges) do
|
134
|
+
{
|
135
|
+
'December and January' => Date.new(2014,12,25)..Date.new(2015,1,10),
|
136
|
+
'January only' => Date.new(2015,1,5)..Date.new(2015,1,20),
|
137
|
+
'January and February' => Date.new(2015,1,25)..Date.new(2015,2,10),
|
138
|
+
'February only' => Date.new(2015,2,5)..Date.new(2015,2,20),
|
139
|
+
'December to February' => Date.new(2014,12,25)..Date.new(2015,2,10),
|
140
|
+
'January to March' => Date.new(2015,1,25)..Date.new(2015,3,10),
|
141
|
+
'December to March' => Date.new(2014,12,25)..Date.new(2015,3,20)
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
before :all do
|
146
|
+
Sunspot.remove_all
|
147
|
+
@posts = [Post.new(featured_for: january), Post.new(featured_for: february), Post.new]
|
148
|
+
Sunspot.index!(@posts)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should filter by Contains' do
|
152
|
+
featured_for_posts(:containing, Date.new(2015,1,15) ).should == [@posts[0]]
|
153
|
+
featured_for_posts(:containing, 'December and January').should be_empty
|
154
|
+
featured_for_posts(:containing, 'January only').should == [@posts[0]]
|
155
|
+
featured_for_posts(:containing, 'January only', negated = true).should == @posts[1..-1]
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should filter by Intersects' do
|
159
|
+
featured_for_posts(:intersecting, Date.new(2015,1,15) ).should == [@posts[0]]
|
160
|
+
featured_for_posts(:intersecting, 'January only').should == [@posts[0]]
|
161
|
+
featured_for_posts(:intersecting, 'January and February').should == @posts[0..1]
|
162
|
+
featured_for_posts(:intersecting, 'January and February', negated = true).should == [@posts[2]]
|
163
|
+
featured_for_posts(:intersecting, 'February only').should == [@posts[1]]
|
164
|
+
featured_for_posts(:intersecting, 'February only', negated = true).should == [@posts[0], @posts[2]]
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should filter by Within' do
|
168
|
+
featured_for_posts(:within, Date.new(2015,1,15) ).should be_empty
|
169
|
+
(date_ranges.keys - date_ranges.keys.grep(/ to /)).each do |key|
|
170
|
+
featured_for_posts(:within, key).should be_empty
|
171
|
+
end
|
172
|
+
featured_for_posts(:within, 'December to February').should == [@posts[0]]
|
173
|
+
featured_for_posts(:within, 'December to February', negated = true).should == @posts[1..-1]
|
174
|
+
featured_for_posts(:within, 'January to March').should == [@posts[1]]
|
175
|
+
featured_for_posts(:within, 'January to March', negated = true).should == [@posts[0], @posts[2]]
|
176
|
+
featured_for_posts(:within, 'December to March').should == @posts[0..1]
|
177
|
+
featured_for_posts(:within, 'December to March', negated = true).should == [@posts[2]]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
130
181
|
describe 'Boolean field type' do
|
131
182
|
before :all do
|
132
183
|
Sunspot.remove_all
|
data/spec/mocks/post.rb
CHANGED
@@ -3,7 +3,8 @@ require File.join(File.dirname(__FILE__), 'super_class')
|
|
3
3
|
|
4
4
|
class Post < SuperClass
|
5
5
|
attr_accessor :title, :body, :blog_id, :published_at, :ratings_average,
|
6
|
-
:author_name, :featured, :expire_date, :coordinates, :tags
|
6
|
+
:author_name, :featured, :expire_date, :coordinates, :tags,
|
7
|
+
:featured_for
|
7
8
|
alias_method :featured?, :featured
|
8
9
|
|
9
10
|
def category_ids
|
@@ -43,6 +44,7 @@ Sunspot.setup(Post) do
|
|
43
44
|
float :average_rating, :using => :ratings_average, :trie => true
|
44
45
|
time :published_at, :trie => true
|
45
46
|
date :expire_date
|
47
|
+
date_range :featured_for
|
46
48
|
boolean :featured, :using => :featured?, :stored => true
|
47
49
|
string :sort_title do
|
48
50
|
title.downcase.sub(/^(a|an|the)\W+/, '') if title
|