outoftime-sunspot 0.8.9 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/README.rdoc +13 -21
  2. data/Rakefile +0 -2
  3. data/TODO +2 -15
  4. data/VERSION.yml +2 -2
  5. data/bin/sunspot-configure-solr +46 -0
  6. data/bin/sunspot-solr +15 -7
  7. data/lib/sunspot/adapters.rb +5 -1
  8. data/lib/sunspot/composite_setup.rb +186 -0
  9. data/lib/sunspot/configuration.rb +7 -1
  10. data/lib/sunspot/data_extractor.rb +10 -0
  11. data/lib/sunspot/date_facet.rb +36 -0
  12. data/lib/sunspot/date_facet_row.rb +17 -0
  13. data/lib/sunspot/dsl/field_query.rb +72 -0
  14. data/lib/sunspot/dsl/fields.rb +30 -3
  15. data/lib/sunspot/dsl/query.rb +16 -35
  16. data/lib/sunspot/dsl/query_facet.rb +31 -0
  17. data/lib/sunspot/dsl/scope.rb +76 -20
  18. data/lib/sunspot/dsl/search.rb +30 -0
  19. data/lib/sunspot/dsl.rb +1 -1
  20. data/lib/sunspot/facet.rb +17 -3
  21. data/lib/sunspot/facet_row.rb +4 -4
  22. data/lib/sunspot/field.rb +130 -207
  23. data/lib/sunspot/field_factory.rb +126 -0
  24. data/lib/sunspot/indexer.rb +61 -14
  25. data/lib/sunspot/instantiated_facet.rb +38 -0
  26. data/lib/sunspot/instantiated_facet_row.rb +12 -0
  27. data/lib/sunspot/query/base_query.rb +90 -0
  28. data/lib/sunspot/query/connective.rb +77 -0
  29. data/lib/sunspot/query/dynamic_query.rb +39 -56
  30. data/lib/sunspot/query/field_facet.rb +132 -4
  31. data/lib/sunspot/query/field_query.rb +57 -0
  32. data/lib/sunspot/query/pagination.rb +1 -1
  33. data/lib/sunspot/query/query_facet.rb +72 -0
  34. data/lib/sunspot/query/query_facet_row.rb +19 -0
  35. data/lib/sunspot/query/restriction.rb +9 -7
  36. data/lib/sunspot/query/scope.rb +165 -0
  37. data/lib/sunspot/query/sort.rb +17 -14
  38. data/lib/sunspot/query/sort_composite.rb +33 -0
  39. data/lib/sunspot/query.rb +162 -351
  40. data/lib/sunspot/query_facet.rb +33 -0
  41. data/lib/sunspot/query_facet_row.rb +21 -0
  42. data/lib/sunspot/schema.rb +165 -0
  43. data/lib/sunspot/search/hit.rb +62 -0
  44. data/lib/sunspot/search.rb +104 -41
  45. data/lib/sunspot/session.rb +64 -32
  46. data/lib/sunspot/setup.rb +119 -48
  47. data/lib/sunspot/type.rb +48 -2
  48. data/lib/sunspot.rb +74 -8
  49. data/solr/solr/conf/schema.xml +44 -225
  50. data/spec/api/build_search_spec.rb +557 -63
  51. data/spec/api/indexer_spec.rb +156 -74
  52. data/spec/api/query_spec.rb +55 -31
  53. data/spec/api/search_retrieval_spec.rb +210 -33
  54. data/spec/api/session_spec.rb +81 -26
  55. data/spec/api/sunspot_spec.rb +5 -7
  56. data/spec/integration/faceting_spec.rb +130 -0
  57. data/spec/integration/keyword_search_spec.rb +72 -31
  58. data/spec/integration/scoped_search_spec.rb +13 -0
  59. data/spec/integration/stored_fields_spec.rb +10 -0
  60. data/spec/mocks/blog.rb +3 -0
  61. data/spec/mocks/comment.rb +12 -23
  62. data/spec/mocks/connection.rb +84 -0
  63. data/spec/mocks/mock_adapter.rb +11 -3
  64. data/spec/mocks/mock_record.rb +41 -0
  65. data/spec/mocks/photo.rb +8 -0
  66. data/spec/mocks/post.rb +18 -23
  67. data/spec/spec_helper.rb +29 -14
  68. data/tasks/gemspec.rake +4 -3
  69. data/tasks/rdoc.rake +2 -2
  70. data/tasks/schema.rake +19 -0
  71. data/templates/schema.xml.haml +24 -0
  72. metadata +48 -7
  73. data/spec/mocks/base_class.rb +0 -2
@@ -15,34 +15,86 @@ describe 'retrieving search' do
15
15
  session.search(Post).results.should == [post_2, post_1]
16
16
  end
17
17
 
18
- it 'should return search total as attribute of results if pagination is provided' do
19
- stub_results(Post.new, 4)
20
- session.search(Post, :page => 1).results.total_entries.should == 4
21
- end
18
+ if ENV['USE_WILL_PAGINATE']
19
+
20
+ it 'should return search total as attribute of results if pagination is provided' do
21
+ stub_results(Post.new, 4)
22
+ session.search(Post, :page => 1).results.total_entries.should == 4
23
+ end
22
24
 
23
- it 'should return vanilla array if pagination is provided but WillPaginate is not available' do
24
- stub_results(Post.new)
25
- without_class(WillPaginate) do
25
+ else
26
+
27
+ it 'should return vanilla array if pagination is provided but WillPaginate is not available' do
28
+ stub_results(Post.new)
26
29
  session.search(Post, :page => 1).results.should_not respond_to(:total_entries)
27
30
  end
31
+
28
32
  end
29
33
 
30
- it 'should return raw results without loading instances' do
34
+ it 'should return hits without loading instances' do
31
35
  post_1, post_2 = Array.new(2) { Post.new }
32
36
  stub_results(post_1, post_2)
33
- %w(load load_all).each { |message| MockAdapter::DataAccessor.should_not_receive(message) }
34
- session.search(Post, :page => 1).raw_results.map do |raw_result|
35
- [raw_result.class_name, raw_result.primary_key]
37
+ %w(load load_all).each do |message|
38
+ MockAdapter::DataAccessor.should_not_receive(message)
39
+ end
40
+ session.search(Post).hits.map do |hit|
41
+ [hit.class_name, hit.primary_key]
36
42
  end.should == [['Post', post_1.id.to_s], ['Post', post_2.id.to_s]]
37
43
  end
38
44
 
45
+ it 'should return instance from hit' do
46
+ posts = Array.new(2) { Post.new }
47
+ stub_results(*posts)
48
+ session.search(Post).hits.first.instance.should == posts.first
49
+ end
50
+
51
+ it 'should hydrate all hits when an instance is requested from a hit' do
52
+ posts = Array.new(2) { Post.new }
53
+ stub_results(*posts)
54
+ search = session.search(Post)
55
+ search.hits.first.instance
56
+ %w(load load_all).each do |message|
57
+ MockAdapter::DataAccessor.should_not_receive(message)
58
+ end
59
+ search.hits.last.instance.should == posts.last
60
+ end
61
+
62
+ it 'should attach score to hits' do
63
+ stub_full_results('instance' => Post.new, 'score' => 1.23)
64
+ session.search(Post).hits.first.score.should == 1.23
65
+ end
66
+
67
+ it 'should return stored field values in hits' do
68
+ stub_full_results('instance' => Post.new, 'title_ss' => 'Title')
69
+ session.search(Post).hits.first.stored(:title).should == 'Title'
70
+ end
71
+
72
+ it 'should return stored field values for searches against multiple types' do
73
+ stub_full_results('instance' => Post.new, 'title_ss' => 'Title')
74
+ session.search(Post, Namespaced::Comment).hits.first.stored(:title).should == 'Title'
75
+ end
76
+
77
+ it 'should typecast stored field values in hits' do
78
+ time = Time.utc(2008, 7, 8, 2, 45)
79
+ stub_full_results('instance' => Post.new, 'last_indexed_at_ds' => time.xmlschema)
80
+ session.search(Post).hits.first.stored(:last_indexed_at).should == time
81
+ end
82
+
83
+ it 'should allow access to the data accessor' do
84
+ stub_results(posts = Post.new)
85
+ search = session.search Post do
86
+ data_accessor_for(Post).custom_title = 'custom title'
87
+ end
88
+ search.results.first.title.should == 'custom title'
89
+ end
90
+
39
91
  it 'should return total' do
40
92
  stub_results(Post.new, Post.new, 4)
41
93
  session.search(Post, :page => 1).total.should == 4
42
94
  end
43
95
 
44
96
  it 'should return field name for facet' do
45
- stub_facet(:title_s, {})
97
+ stub_facet(:title_ss, {})
46
98
  result = session.search Post do
47
99
  facet :title
48
100
  end
@@ -50,7 +102,7 @@ describe 'retrieving search' do
50
102
  end
51
103
 
52
104
  it 'should return string facet' do
53
- stub_facet(:title_s, 'Author 1' => 2, 'Author 2' => 1)
105
+ stub_facet(:title_ss, 'Author 1' => 2, 'Author 2' => 1)
54
106
  result = session.search Post do
55
107
  facet :title
56
108
  end
@@ -58,7 +110,7 @@ describe 'retrieving search' do
58
110
  end
59
111
 
60
112
  it 'should return counts for facet' do
61
- stub_facet(:title_s, 'Author 1' => 2, 'Author 2' => 1)
113
+ stub_facet(:title_ss, 'Author 1' => 2, 'Author 2' => 1)
62
114
  result = session.search Post do
63
115
  facet :title
64
116
  end
@@ -82,12 +134,31 @@ describe 'retrieving search' do
82
134
  end
83
135
 
84
136
  it 'should return time facet' do
85
- stub_facet(:published_at_d, '2009-04-07T20:25:23Z' => 3, '2009-04-07T20:26:19Z' => 1)
137
+ stub_facet(
138
+ :published_at_d,
139
+ '2009-04-07T20:25:23Z' => 3,
140
+ '2009-04-07T20:26:19Z' => 1
141
+ )
86
142
  result = session.search Post do
87
143
  facet :published_at
88
144
  end
89
- facet_values(result, :published_at).should == [Time.gm(2009, 04, 07, 20, 25, 23),
90
- Time.gm(2009, 04, 07, 20, 26, 19)]
145
+ facet_values(result, :published_at).should ==
146
+ [Time.gm(2009, 04, 07, 20, 25, 23),
147
+ Time.gm(2009, 04, 07, 20, 26, 19)]
148
+ end
149
+
150
+ it 'should return date facet' do
151
+ stub_facet(
152
+ :expire_date_d,
153
+ '2009-07-13T00:00:00Z' => 3,
154
+ '2009-04-01T00:00:00Z' => 1
155
+ )
156
+ result = session.search(Post) do
157
+ facet :expire_date
158
+ end
159
+ facet_values(result, :expire_date).should ==
160
+ [Date.new(2009, 07, 13),
161
+ Date.new(2009, 04, 01)]
91
162
  end
92
163
 
93
164
  it 'should return boolean facet' do
@@ -96,34 +167,140 @@ describe 'retrieving search' do
96
167
  facet_values(result, :featured).should == [true, false]
97
168
  end
98
169
 
170
+ it 'should return class facet' do
171
+ stub_facet(:class_name, 'Post' => 3, 'Namespaced::Comment' => 1)
172
+ result = session.search(Post) { facet(:class) }
173
+ facet_values(result, :class).should == [Post, Namespaced::Comment]
174
+ end
175
+
176
+ it 'should return date range facet' do
177
+ stub_date_facet(:published_at_d, 60*60*24, '2009-07-08T04:00:00Z' => 2, '2009-07-07T04:00:00Z' => 1)
178
+ start_time = Time.utc(2009, 7, 7, 4)
179
+ end_time = start_time + 2*24*60*60
180
+ result = session.search(Post) { facet(:published_at, :time_range => start_time..end_time) }
181
+ facet = result.facet(:published_at)
182
+ facet.rows.first.value.should == (start_time..(start_time+24*60*60))
183
+ facet.rows.last.value.should == ((start_time+24*60*60)..end_time)
184
+ end
185
+
186
+ it 'should return query facet' do
187
+ stub_query_facet(
188
+ 'average_rating_f:[3\.0 TO 5\.0]' => 3,
189
+ 'average_rating_f:[1\.0 TO 3\.0]' => 1
190
+ )
191
+ search = session.search(Post) do
192
+ facet :average_rating do
193
+ row 3.0..5.0 do
194
+ with :average_rating, 3.0..5.0
195
+ end
196
+ row 1.0..3.0 do
197
+ with :average_rating, 1.0..3.0
198
+ end
199
+ end
200
+ end
201
+ facet = search.facet(:average_rating)
202
+ facet.rows.first.value.should == (3.0..5.0)
203
+ facet.rows.first.count.should == 3
204
+ facet.rows.last.value.should == (1.0..3.0)
205
+ facet.rows.last.count.should == 1
206
+ end
207
+
208
+ it 'should return query facet specified in dynamic call' do
209
+ stub_query_facet(
210
+ 'custom_string\:test_s:(foo OR bar)' => 3
211
+ )
212
+ search = session.search(Post) do
213
+ dynamic :custom_string do
214
+ facet :test do
215
+ row :foo_bar do
216
+ with :test, %w(foo bar)
217
+ end
218
+ end
219
+ end
220
+ end
221
+ facet = search.facet(:test)
222
+ facet.rows.first.value.should == :foo_bar
223
+ facet.rows.first.count.should == 3
224
+ end
225
+
99
226
  it 'should return dynamic string facet' do
100
227
  stub_facet(:"custom_string:test_s", 'two' => 2, 'one' => 1)
101
228
  result = session.search(Post) { dynamic(:custom_string) { facet(:test) }}
102
229
  result.dynamic_facet(:custom_string, :test).rows.map { |row| row.value }.should == ['two', 'one']
103
230
  end
104
231
 
232
+ it 'should return instantiated facet values' do
233
+ blogs = Array.new(2) { Blog.new }
234
+ stub_facet(:blog_id_i, blogs[0].id.to_s => 2, blogs[1].id.to_s => 1)
235
+ result = session.search(Post) { facet(:blog_id) }
236
+ result.facet(:blog_id).rows.map { |row| row.instance }.should == blogs
237
+ end
238
+
239
+ it 'should only query the persistent store once for an instantiated facet' do
240
+ query_count = Blog.query_count
241
+ blogs = Array.new(2) { Blog.new }
242
+ stub_facet(:blog_id_i, blogs[0].id.to_s => 2, blogs[1].id.to_s => 1)
243
+ result = session.search(Post) { facet(:blog_id) }
244
+ result.facet(:blog_id).rows.each { |row| row.instance }
245
+ (Blog.query_count - query_count).should == 1
246
+ end
247
+
105
248
  private
106
249
 
250
+ def stub_full_results(*results)
251
+ count =
252
+ if results.last.is_a?(Integer) then results.pop
253
+ else results.length
254
+ end
255
+ docs = results.map do |result|
256
+ instance = result.delete('instance')
257
+ result.merge('id' => "#{instance.class.name} #{instance.id}")
258
+ end
259
+ response = {
260
+ 'response' => {
261
+ 'docs' => docs,
262
+ 'numFound' => count
263
+ }
264
+ }
265
+ connection.stub!(:select).and_return(response)
266
+ end
267
+
107
268
  def stub_results(*results)
108
- total_hits = if results.last.is_a?(Integer) then results.pop
109
- else results.length
110
- end
111
- response = mock('response')
112
- response.stub!(:hits).and_return(results.map { |result| { 'id' => "#{result.class.name} #{result.id}" }})
113
- response.stub!(:total_hits).and_return(total_hits)
114
- connection.stub!(:query).and_return(response)
269
+ stub_full_results(
270
+ *results.map do |result|
271
+ if result.is_a?(Integer)
272
+ result
273
+ else
274
+ { 'instance' => result }
275
+ end
276
+ end
277
+ )
115
278
  end
116
279
 
117
280
  def stub_facet(name, values)
118
- response = mock('response')
119
- facets = values.map do |data, count|
120
- value = Solr::Response::Standard::FacetValue.new
121
- value.name = data
122
- value.value = count
123
- value
124
- end.sort_by { |value| -value.value }
125
- response.stub!(:field_facets).with(name.to_s).and_return(facets)
126
- connection.stub!(:query).and_return(response)
281
+ connection.stub!(:select).and_return(
282
+ 'facet_counts' => {
283
+ 'facet_fields' => {
284
+ name.to_s => values.to_a.sort_by { |value, count| -count }.flatten
285
+ }
286
+ }
287
+ )
288
+ end
289
+
290
+ def stub_date_facet(name, gap, values)
291
+ connection.stub!(:select).and_return(
292
+ 'facet_counts' => {
293
+ 'facet_dates' => {
294
+ name.to_s => { 'gap' => "+#{gap}SECONDS" }.merge(values)
295
+ }
296
+ }
297
+ )
298
+ end
299
+
300
+ def stub_query_facet(values)
301
+ connection.stub!(:select).and_return(
302
+ 'facet_counts' => { 'facet_queries' => values }
303
+ )
127
304
  end
128
305
 
129
306
  def facet_values(result, field_name)
@@ -1,56 +1,111 @@
1
1
  require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
+ shared_examples_for 'all sessions' do
4
+ context '#index()' do
5
+ before :each do
6
+ @session.index(Post.new)
7
+ end
8
+
9
+ it 'should add document to connection' do
10
+ connection.should have(1).adds
11
+ end
12
+ end
13
+
14
+ context '#index!()' do
15
+ before :each do
16
+ @session.index!(Post.new)
17
+ end
18
+
19
+ it 'should add document to connection' do
20
+ connection.should have(1).adds
21
+ end
22
+
23
+ it 'should commit' do
24
+ connection.should have(1).commits
25
+ end
26
+ end
27
+
28
+ context '#commit()' do
29
+ before :each do
30
+ @session.commit
31
+ end
32
+
33
+ it 'should commit' do
34
+ connection.should have(1).commits
35
+ end
36
+ end
37
+
38
+ context '#search()' do
39
+ before :each do
40
+ @session.search(Post)
41
+ end
42
+
43
+ it 'should search' do
44
+ connection.should have(1).searches
45
+ end
46
+ end
47
+ end
48
+
3
49
  describe 'Session' do
4
- context 'using singleton session' do
50
+ before :each do
51
+ @connection_factory = Mock::ConnectionFactory.new
52
+ Sunspot::Session.connection_class = @connection_factory
53
+ end
54
+
55
+ after :each do
56
+ Sunspot::Session.connection_class = nil
57
+ Sunspot.reset!
58
+ end
59
+
60
+ context 'singleton session' do
5
61
  before :each do
6
62
  Sunspot.reset!
7
- connection.should_receive(:add).twice
8
- connection.should_receive(:commit).twice
9
- connection.should_receive(:query)
63
+ @session = Sunspot
10
64
  end
11
65
 
66
+ it_should_behave_like 'all sessions'
67
+
12
68
  it 'should open connection with defaults if nothing specified' do
13
- Solr::Connection.stub!(:new).with('http://localhost:8983/solr').and_return(connection)
14
- Sunspot.index(Post.new)
15
- Sunspot.index!(Post.new)
16
69
  Sunspot.commit
17
- Sunspot.search(Post)
70
+ connection.adapter.opts[:url].should == 'http://127.0.0.1:8983/solr'
18
71
  end
19
72
 
20
73
  it 'should open a connection with custom host' do
21
- Solr::Connection.stub!(:new).with('http://127.0.0.1:8981/solr').and_return(connection)
22
74
  Sunspot.config.solr.url = 'http://127.0.0.1:8981/solr'
23
- Sunspot.index(Post.new)
24
- Sunspot.index!(Post.new)
25
75
  Sunspot.commit
26
- Sunspot.search(Post)
76
+ connection.adapter.opts[:url].should == 'http://127.0.0.1:8981/solr'
77
+ end
78
+
79
+ it 'should use Net::HTTP adapter by default' do
80
+ Sunspot.commit
81
+ connection.adapter.connector.adapter_name.should == :net_http
82
+ end
83
+
84
+ it 'should use Net::HTTP adapter when specified' do
85
+ Sunspot.config.http_client = :curb
86
+ Sunspot.commit
87
+ connection.adapter.connector.adapter_name.should == :curb
27
88
  end
28
89
  end
29
90
 
30
- context 'using custom session' do
91
+ context 'custom session' do
31
92
  before :each do
32
- connection.should_receive(:add).twice
33
- connection.should_receive(:commit).twice
34
- connection.should_receive(:query)
93
+ @session = Sunspot::Session.new
35
94
  end
36
95
 
96
+ it_should_behave_like 'all sessions'
97
+
37
98
  it 'should open a connection with custom host' do
38
- Solr::Connection.stub!(:new).with('http://127.0.0.1:8982/solr').and_return(connection)
39
99
  session = Sunspot::Session.new do |config|
40
100
  config.solr.url = 'http://127.0.0.1:8982/solr'
41
101
  end
42
- session.index(Post.new)
43
- session.index!(Post.new)
44
102
  session.commit
45
- session.search(Post)
103
+ connection.adapter.opts[:url].should == 'http://127.0.0.1:8982/solr'
46
104
  end
47
105
  end
48
106
 
49
107
  context 'dirty sessions' do
50
108
  before :each do
51
- connection.stub!(:add)
52
- connection.stub!(:commit)
53
- Solr::Connection.stub!(:new).and_return(connection)
54
109
  @session = Sunspot::Session.new
55
110
  end
56
111
 
@@ -85,18 +140,18 @@ describe 'Session' do
85
140
  end
86
141
 
87
142
  it 'should not commit when commit_if_dirty called on clean session' do
88
- connection.should_not_receive(:commit)
89
143
  @session.commit_if_dirty
144
+ connection.should have(0).commits
90
145
  end
91
146
 
92
147
  it 'should commit when commit_if_dirty called on dirty session' do
93
- connection.should_receive(:commit)
94
148
  @session.index(Post.new)
95
149
  @session.commit_if_dirty
150
+ connection.should have(1).commits
96
151
  end
97
152
  end
98
153
 
99
154
  def connection
100
- @connection ||= mock('Connection').as_null_object
155
+ @connection_factory.instance
101
156
  end
102
157
  end
@@ -4,17 +4,15 @@ describe Sunspot do
4
4
  describe "reset!" do
5
5
  it "should reset current session" do
6
6
  old_session = Sunspot.send(:session)
7
- Sunspot.reset!
8
-
7
+ Sunspot.reset!(true)
9
8
  Sunspot.send(:session).should_not == old_session
10
9
  end
11
- it "should keep keep configuration" do
12
- Sunspot.config.solr.url = "http://localhost:9999/path/solr"
13
10
 
11
+ it "should keep keep configuration if specified" do
12
+ Sunspot.config.solr.url = "http://localhost:9999/path/solr"
14
13
  config_before_reset = Sunspot.config
15
- Sunspot.reset!
16
-
14
+ Sunspot.reset!(true)
17
15
  Sunspot.config.should == config_before_reset
18
16
  end
19
17
  end
20
- end
18
+ end
@@ -1,3 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
1
3
  describe 'search faceting' do
2
4
  def self.test_field_type(name, attribute, field, value1, value2)
3
5
  context "with field of type #{name}" do
@@ -36,4 +38,132 @@ describe 'search faceting' do
36
38
  test_field_type('Time', :published_at, :published_at, Time.mktime(2008, 02, 17, 17, 45, 04),
37
39
  Time.mktime(2008, 07, 02, 03, 56, 22))
38
40
  test_field_type('Boolean', :featured, :featured, true, false)
41
+
42
+ context 'facet options' do
43
+ before :all do
44
+ Sunspot.remove_all
45
+ facet_values = %w(zero one two three four)
46
+ facet_values.each_with_index do |value, i|
47
+ i.times { Sunspot.index(Post.new(:title => value, :blog_id => 1)) }
48
+ end
49
+ Sunspot.index(Post.new(:title => 'zero', :blog_id => 2))
50
+ Sunspot.commit
51
+ end
52
+
53
+ it 'should limit the number of facet rows' do
54
+ search = Sunspot.search(Post) do
55
+ facet :title, :limit => 3
56
+ end
57
+ search.facet(:title).should have(3).rows
58
+ end
59
+
60
+ it 'should not return zeros by default' do
61
+ search = Sunspot.search(Post) do
62
+ with :blog_id, 1
63
+ facet :title
64
+ end
65
+ search.facet(:title).rows.map { |row| row.value }.should_not include('zero')
66
+ end
67
+
68
+ it 'should return zeros when specified' do
69
+ search = Sunspot.search(Post) do
70
+ with :blog_id, 1
71
+ facet :title, :zeros => true
72
+ end
73
+ search.facet(:title).rows.map { |row| row.value }.should include('zero')
74
+ end
75
+
76
+ it 'should return a specified minimum count' do
77
+ search = Sunspot.search(Post) do
78
+ with :blog_id, 1
79
+ facet :title, :minimum_count => 2
80
+ end
81
+ search.facet(:title).rows.map { |row| row.value }.should == %w(four three two)
82
+ end
83
+
84
+ it 'should order facets lexically' do
85
+ search = Sunspot.search(Post) do
86
+ with :blog_id, 1
87
+ facet :title, :sort => :index
88
+ end
89
+ search.facet(:title).rows.map { |row| row.value }.should == %w(four one three two)
90
+ end
91
+
92
+ it 'should order facets by count' do
93
+ search = Sunspot.search(Post) do
94
+ with :blog_id, 1
95
+ facet :title, :sort => :count
96
+ end
97
+ search.facet(:title).rows.map { |row| row.value }.should == %w(four three two one)
98
+ end
99
+ end
100
+
101
+ context 'date facets' do
102
+ before :all do
103
+ Sunspot.remove_all
104
+ time = Time.utc(2009, 7, 8)
105
+ Sunspot.index!(
106
+ (0..2).map { |i| Post.new(:published_at => time + i*60*60*16) }
107
+ )
108
+ end
109
+
110
+ it 'should return time ranges' do
111
+ time = Time.utc(2009, 7, 8)
112
+ search = Sunspot.search(Post) do
113
+ facet :published_at, :time_range => time..(time + 60*60*24*2), :sort => :count
114
+ end
115
+ search.facet(:published_at).rows.first.value.should == (time..(time + 60*60*24))
116
+ search.facet(:published_at).rows.first.count.should == 2
117
+ search.facet(:published_at).rows.last.value.should == ((time + 60*60*24)..(time + 60*60*24*2))
118
+ search.facet(:published_at).rows.last.count.should == 1
119
+ end
120
+ end
121
+
122
+ context 'class facets' do
123
+ before :all do
124
+ Sunspot.remove_all
125
+ Sunspot.index!(Post.new, Post.new, Namespaced::Comment.new)
126
+ end
127
+
128
+ it 'should return classes' do
129
+ search = Sunspot.search(Post, Namespaced::Comment) do
130
+ facet(:class, :sort => :count)
131
+ end
132
+ search.facet(:class).rows.first.value.should == Post
133
+ search.facet(:class).rows.first.count.should == 2
134
+ search.facet(:class).rows.last.value.should == Namespaced::Comment
135
+ search.facet(:class).rows.last.count.should == 1
136
+ end
137
+ end
138
+
139
+ context 'query facets' do
140
+ before :all do
141
+ Sunspot.remove_all
142
+ Sunspot.index!(
143
+ [1.1, 1.2, 3.2, 3.4, 3.9, 4.1].map do |rating|
144
+ Post.new(:ratings_average => rating)
145
+ end
146
+ )
147
+ end
148
+
149
+ it 'should return specified facets' do
150
+ search = Sunspot.search(Post) do
151
+ facet :rating_range do
152
+ for rating in [1.0, 2.0, 3.0, 4.0]
153
+ range = rating..(rating + 1.0)
154
+ row range do
155
+ with :average_rating, rating..(rating + 1.0)
156
+ end
157
+ end
158
+ end
159
+ end
160
+ facet = search.facet(:rating_range)
161
+ facet.rows[0].value.should == (3.0..4.0)
162
+ facet.rows[0].count.should == 3
163
+ facet.rows[1].value.should == (1.0..2.0)
164
+ facet.rows[1].count.should == 2
165
+ facet.rows[2].value.should == (4.0..5.0)
166
+ facet.rows[2].count.should == 1
167
+ end
168
+ end
39
169
  end