sunspot 2.2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|