sunspot 2.2.0 → 2.2.1

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 +7 -0
  2. data/lib/sunspot/dsl/field_query.rb +11 -9
  3. data/lib/sunspot/dsl/fulltext.rb +9 -1
  4. data/lib/sunspot/dsl/group.rb +108 -0
  5. data/lib/sunspot/dsl/search.rb +1 -1
  6. data/lib/sunspot/dsl.rb +1 -1
  7. data/lib/sunspot/field_factory.rb +28 -15
  8. data/lib/sunspot/indexer.rb +63 -17
  9. data/lib/sunspot/query/abstract_fulltext.rb +9 -2
  10. data/lib/sunspot/query/dismax.rb +11 -4
  11. data/lib/sunspot/query/{field_group.rb → group.rb} +16 -6
  12. data/lib/sunspot/query/group_query.rb +17 -0
  13. data/lib/sunspot/query/restriction.rb +45 -1
  14. data/lib/sunspot/query.rb +1 -1
  15. data/lib/sunspot/schema.rb +2 -1
  16. data/lib/sunspot/search/abstract_search.rb +7 -3
  17. data/lib/sunspot/search/query_group.rb +74 -0
  18. data/lib/sunspot/search/standard_search.rb +1 -1
  19. data/lib/sunspot/search.rb +1 -1
  20. data/lib/sunspot/session.rb +16 -0
  21. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +2 -2
  22. data/lib/sunspot/session_proxy/retry_5xx_session_proxy.rb +1 -1
  23. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +4 -2
  24. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +1 -1
  25. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +3 -1
  26. data/lib/sunspot/type.rb +20 -0
  27. data/lib/sunspot/version.rb +1 -1
  28. data/lib/sunspot.rb +36 -2
  29. data/spec/api/indexer/attributes_spec.rb +5 -0
  30. data/spec/api/query/function_spec.rb +103 -0
  31. data/spec/api/query/group_spec.rb +23 -1
  32. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +1 -1
  33. data/spec/helpers/integration_helper.rb +9 -0
  34. data/spec/integration/atomic_updates_spec.rb +44 -0
  35. data/spec/integration/field_grouping_spec.rb +13 -0
  36. data/spec/integration/scoped_search_spec.rb +51 -0
  37. data/spec/mocks/post.rb +3 -1
  38. metadata +11 -15
  39. 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['suggestions'][-1]
73
+ collation = solr_spellcheck['collations'][-1]
74
74
  end
75
75
 
76
76
  collation
@@ -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
 
@@ -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
- :remove_by_id!, :to => :master_session
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!, :new_search, :optimize, :remove, :remove!, :remove_all, :remove_all!, :remove_by_id, :remove_by_id!, :search, :more_like_this, :new_more_like_this, :to => :session
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'
@@ -1,3 +1,3 @@
1
1
  module Sunspot
2
- VERSION = '2.2.0'
2
+ VERSION = '2.2.1'
3
3
  end
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.add(post)
527
+ # Sunspot.index(post)
494
528
  # comment = Comment.new
495
- # Sunspot.add(comment)
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 "field grouping" do
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