sunspot 0.10.5 → 0.10.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/History.txt +13 -0
  2. data/README.rdoc +3 -3
  3. data/TODO +4 -5
  4. data/VERSION.yml +1 -1
  5. data/bin/sunspot-solr +18 -6
  6. data/lib/sunspot/dsl/field_query.rb +69 -18
  7. data/lib/sunspot/dsl/fields.rb +6 -5
  8. data/lib/sunspot/dsl/fulltext.rb +57 -1
  9. data/lib/sunspot/dsl/query.rb +30 -3
  10. data/lib/sunspot/dsl/query_facet.rb +8 -3
  11. data/lib/sunspot/dsl/search.rb +1 -1
  12. data/lib/sunspot/dsl.rb +1 -1
  13. data/lib/sunspot/field_factory.rb +6 -3
  14. data/lib/sunspot/query/abstract_field_facet.rb +43 -0
  15. data/lib/sunspot/query/date_field_facet.rb +14 -0
  16. data/lib/sunspot/query/dismax.rb +26 -7
  17. data/lib/sunspot/query/field_facet.rb +2 -122
  18. data/lib/sunspot/query/highlighting.rb +17 -5
  19. data/lib/sunspot/query/query.rb +12 -23
  20. data/lib/sunspot/query/query_facet.rb +4 -66
  21. data/lib/sunspot/query.rb +5 -1
  22. data/lib/sunspot/search/date_facet.rb +35 -0
  23. data/lib/sunspot/search/facet_row.rb +27 -0
  24. data/lib/sunspot/search/field_facet.rb +44 -0
  25. data/lib/sunspot/search/hit.rb +10 -6
  26. data/lib/sunspot/search/query_facet.rb +62 -0
  27. data/lib/sunspot/search.rb +22 -44
  28. data/lib/sunspot/setup.rb +22 -7
  29. data/lib/sunspot/type.rb +4 -0
  30. data/lib/sunspot/util.rb +8 -0
  31. data/lib/sunspot.rb +7 -6
  32. data/solr/solr/conf/solrconfig.xml +1 -2
  33. data/solr/solr/lib/locallucene.jar +0 -0
  34. data/solr/solr/lib/localsolr.jar +0 -0
  35. data/spec/api/indexer/attributes_spec.rb +5 -0
  36. data/spec/api/query/faceting_spec.rb +24 -0
  37. data/spec/api/query/fulltext_spec.rb +80 -1
  38. data/spec/api/query/highlighting_spec.rb +84 -6
  39. data/spec/api/search/faceting_spec.rb +45 -9
  40. data/spec/api/search/highlighting_spec.rb +2 -2
  41. data/spec/api/search/hits_spec.rb +5 -0
  42. data/spec/integration/faceting_spec.rb +19 -0
  43. data/spec/integration/keyword_search_spec.rb +101 -4
  44. data/spec/mocks/photo.rb +3 -0
  45. data/tasks/gemspec.rake +8 -2
  46. data/tasks/rcov.rake +2 -2
  47. metadata +9 -11
  48. data/lib/sunspot/facet.rb +0 -24
  49. data/lib/sunspot/facet_data.rb +0 -169
  50. data/lib/sunspot/facet_row.rb +0 -12
  51. data/lib/sunspot/instantiated_facet.rb +0 -39
  52. data/lib/sunspot/instantiated_facet_row.rb +0 -27
  53. data/lib/sunspot/query/fulltext_base_query.rb +0 -47
  54. data/lib/sunspot/query/query_facet_row.rb +0 -19
  55. data/lib/sunspot/query/query_field_facet.rb +0 -20
data/lib/sunspot/setup.rb CHANGED
@@ -10,6 +10,7 @@ module Sunspot
10
10
  @field_factories, @text_field_factories, @dynamic_field_factories,
11
11
  @field_factories_cache, @text_field_factories_cache,
12
12
  @dynamic_field_factories_cache = *Array.new(6) { Hash.new }
13
+ @stored_field_factories_cache = Hash.new { |h, k| h[k] = [] }
13
14
  @dsl = DSL::Fields.new(self)
14
15
  add_field_factory(:class, Type::ClassType)
15
16
  end
@@ -19,16 +20,16 @@ module Sunspot
19
20
  end
20
21
 
21
22
  #
22
- # Add field_factories for scope/ordering
23
- #
24
- # ==== Parameters
25
- #
26
- # field_factories<Array>:: Array of Sunspot::Field objects
23
+ # Add field factory for scope/ordering
27
24
  #
28
25
  def add_field_factory(name, type, options = {}, &block)
26
+ stored = options[:stored]
29
27
  field_factory = FieldFactory::Static.new(name, type, options, &block)
30
28
  @field_factories[field_factory.signature] = field_factory
31
29
  @field_factories_cache[field_factory.name] = field_factory
30
+ if stored
31
+ @stored_field_factories_cache[field_factory.name] << field_factory
32
+ end
32
33
  end
33
34
 
34
35
  #
@@ -39,9 +40,13 @@ module Sunspot
39
40
  # field_factories<Array>:: Array of Sunspot::Field objects
40
41
  #
41
42
  def add_text_field_factory(name, options = {}, &block)
43
+ stored = options[:stored]
42
44
  field_factory = FieldFactory::Static.new(name, Type::TextType, options, &block)
43
45
  @text_field_factories[name] = field_factory
44
46
  @text_field_factories_cache[field_factory.name] = field_factory
47
+ if stored
48
+ @stored_field_factories_cache[field_factory.name] << field_factory
49
+ end
45
50
  end
46
51
 
47
52
  #
@@ -62,8 +67,8 @@ module Sunspot
62
67
  # of documents during index, but does not actually generate fields (since
63
68
  # the field names used in search are static).
64
69
  #
65
- def set_coordinates_field(name)
66
- @coordinates_field_factory = FieldFactory::Coordinates.new(name)
70
+ def set_coordinates_field(name = nil, &block)
71
+ @coordinates_field_factory = FieldFactory::Coordinates.new(name, &block)
67
72
  end
68
73
 
69
74
  #
@@ -123,6 +128,16 @@ module Sunspot
123
128
  [text_field]
124
129
  end
125
130
 
131
+ #
132
+ # Return one or more stored fields (can be either attribute or text fields)
133
+ # for the given name.
134
+ #
135
+ def stored_fields(field_name)
136
+ @stored_field_factories_cache[field_name.to_sym].map do |field_factory|
137
+ field_factory.build
138
+ end
139
+ end
140
+
126
141
  #
127
142
  # Return the DynamicFieldFactory with the given base name
128
143
  #
data/lib/sunspot/type.rb CHANGED
@@ -33,6 +33,10 @@ module Sunspot
33
33
  def to_indexed(value) #:nodoc:
34
34
  value.to_s if value
35
35
  end
36
+
37
+ def cast(text)
38
+ text
39
+ end
36
40
  end
37
41
  end
38
42
 
data/lib/sunspot/util.rb CHANGED
@@ -87,6 +87,14 @@ module Sunspot
87
87
  end
88
88
  end
89
89
 
90
+ def extract_options_from(args)
91
+ if args.last.is_a?(Hash)
92
+ args.pop
93
+ else
94
+ {}
95
+ end
96
+ end
97
+
90
98
  #
91
99
  # Perform a deep merge of hashes, returning the result as a new hash.
92
100
  # See #deep_merge_into for rules used to merge the hashes
data/lib/sunspot.rb CHANGED
@@ -1,6 +1,8 @@
1
+ require 'set'
2
+ require 'time'
3
+ require 'date'
4
+ require 'enumerator'
1
5
  begin
2
- require 'time'
3
- require 'date'
4
6
  require 'rsolr'
5
7
  rescue LoadError
6
8
  require 'rubygems'
@@ -10,8 +12,7 @@ end
10
12
  require File.join(File.dirname(__FILE__), 'light_config')
11
13
 
12
14
  %w(util adapters configuration setup composite_setup text_field_setup field
13
- field_factory data_extractor indexer query search facet facet_row
14
- instantiated_facet instantiated_facet_row facet_data session type
15
+ field_factory data_extractor indexer query search session type
15
16
  dsl).each do |filename|
16
17
  require File.join(File.dirname(__FILE__), 'sunspot', filename)
17
18
  end
@@ -210,7 +211,7 @@ module Sunspot
210
211
  # ==== Parameters
211
212
  #
212
213
  # types<Class>...::
213
- # Zero, one, or more types to search for. If no types are passed, all
214
+ # One or more types to search for. If no types are passed, all
214
215
  # configured types will be searched for.
215
216
  #
216
217
  # ==== Returns
@@ -229,7 +230,7 @@ module Sunspot
229
230
  # ==== Parameters
230
231
  #
231
232
  # types<Class>...::
232
- # Zero, one, or more types to search for. If no types are passed, all
233
+ # One or more types to search for. If no types are passed, all
233
234
  # configured types will be searched.
234
235
  #
235
236
  # ==== Returns
@@ -682,9 +682,7 @@
682
682
  </queryResponseWriter>
683
683
 
684
684
 
685
- <!-- example of registering a query parser
686
685
  <queryParser name="lucene" class="org.apache.solr.search.LuceneQParserPlugin"/>
687
- -->
688
686
 
689
687
  <!-- example of registering a custom function parser
690
688
  <valueSourceParser name="myfunc" class="com.mycompany.MyValueSourceParser" />
@@ -715,6 +713,7 @@
715
713
  <str name='lngField'>long</str>
716
714
  </searchComponent>
717
715
  <requestHandler class='org.apache.solr.handler.component.SearchHandler' name='geo'>
716
+ <str name="defType">lucene</str>
718
717
  <arr name='components'>
719
718
  <str>localsolr</str>
720
719
  <str>facet</str>
Binary file
Binary file
@@ -66,6 +66,11 @@ describe 'indexing attribute fields', :type => :indexer do
66
66
  end
67
67
  end
68
68
 
69
+ it 'should index latitude and longitude from a block' do
70
+ session.index(Photo.new(:lat => 30, :lng => -60))
71
+ connection.should have_add_with(:lat => 30, :long => -60)
72
+ end
73
+
69
74
  it 'should correctly index an attribute field with block access' do
70
75
  session.index(post(:title => 'The Blog Post'))
71
76
  connection.should have_add_with(:sort_title_s => 'blog post')
@@ -1,3 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
1
3
  describe 'faceting' do
2
4
  describe 'on fields' do
3
5
  it 'does not turn faceting on if no facet requested' do
@@ -75,6 +77,28 @@ describe 'faceting' do
75
77
  end
76
78
  connection.should have_last_search_with(:"f.category_ids_im.facet.mincount" => 1)
77
79
  end
80
+
81
+ it 'sends a query facet for :any extra' do
82
+ session.search Post do
83
+ facet :category_ids, :extra => :any
84
+ end
85
+ connection.should have_last_search_with(:"facet.query" => "category_ids_im:[* TO *]")
86
+ end
87
+
88
+ it 'sends a query facet for :none extra' do
89
+ session.search Post do
90
+ facet :category_ids, :extra => :none
91
+ end
92
+ connection.should have_last_search_with(:"facet.query" => "-category_ids_im:[* TO *]")
93
+ end
94
+
95
+ it 'raises an ArgumentError if bogus extra is passed' do
96
+ lambda do
97
+ session.search Post do
98
+ facet :category_ids, :extra => :bogus
99
+ end
100
+ end.should raise_error(ArgumentError)
101
+ end
78
102
  end
79
103
 
80
104
  describe 'on time ranges' do
@@ -70,6 +70,15 @@ describe 'fulltext query', :type => :query do
70
70
  connection.searches.last[:qf].split(' ').sort.should == %w(body_texts title_text)
71
71
  end
72
72
 
73
+ it 'excludes text fields when instructed' do
74
+ session.search Post do
75
+ keywords 'keyword search' do
76
+ exclude_fields :backwards_title
77
+ end
78
+ end
79
+ connection.searches.last[:qf].split(' ').sort.should == %w(body_texts title_text)
80
+ end
81
+
73
82
  it 'assigns boost to fields when specified' do
74
83
  session.search Post do
75
84
  keywords 'keyword search' do
@@ -124,6 +133,15 @@ describe 'fulltext query', :type => :query do
124
133
  connection.should have_last_search_with(:pf => 'title_text^1.5')
125
134
  end
126
135
 
136
+ it 'sets phrase slop from DSL' do
137
+ session.search Post do
138
+ keywords 'great pizza' do
139
+ phrase_slop 2
140
+ end
141
+ end
142
+ connection.should have_last_search_with(:ps => 2)
143
+ end
144
+
127
145
  it 'sets boost for certain fields without restricting fields' do
128
146
  session.search Post do
129
147
  keywords 'great pizza' do
@@ -173,7 +191,68 @@ describe 'fulltext query', :type => :query do
173
191
  end
174
192
  end
175
193
  end
176
- connection.should have_last_search_with(:bq => 'average_rating_f:[2\.0 TO *]^2.0')
194
+ connection.should have_last_search_with(:bq => ['average_rating_f:[2\.0 TO *]^2.0'])
195
+ end
196
+
197
+ it 'creates multiple boost queries' do
198
+ session.search Post do
199
+ keywords 'great pizza' do
200
+ boost(2.0) do
201
+ with(:average_rating).greater_than(2.0)
202
+ end
203
+ boost(1.5) do
204
+ with(:featured, true)
205
+ end
206
+ end
207
+ end
208
+ connection.should have_last_search_with(
209
+ :bq => [
210
+ 'average_rating_f:[2\.0 TO *]^2.0',
211
+ 'featured_b:true^1.5'
212
+ ]
213
+ )
214
+ end
215
+
216
+ it 'sends minimum match parameter from options' do
217
+ session.search Post do
218
+ keywords 'great pizza', :minimum_match => 2
219
+ end
220
+ connection.should have_last_search_with(:mm => 2)
221
+ end
222
+
223
+ it 'sends minimum match parameter from DSL' do
224
+ session.search Post do
225
+ keywords('great pizza') { minimum_match(2) }
226
+ end
227
+ connection.should have_last_search_with(:mm => 2)
228
+ end
229
+
230
+ it 'sends tiebreaker parameter from options' do
231
+ session.search Post do
232
+ keywords 'great pizza', :tie => 0.1
233
+ end
234
+ connection.should have_last_search_with(:tie => 0.1)
235
+ end
236
+
237
+ it 'sends tiebreaker parameter from DSL' do
238
+ session.search Post do
239
+ keywords('great pizza') { tie(0.1) }
240
+ end
241
+ connection.should have_last_search_with(:tie => 0.1)
242
+ end
243
+
244
+ it 'sends query phrase slop from options' do
245
+ session.search Post do
246
+ keywords 'great pizza', :query_phrase_slop => 2
247
+ end
248
+ connection.should have_last_search_with(:qs => 2)
249
+ end
250
+
251
+ it 'sends query phrase slop from DSL' do
252
+ session.search Post do
253
+ keywords('great pizza') { query_phrase_slop(2) }
254
+ end
255
+ connection.should have_last_search_with(:qs => 2)
177
256
  end
178
257
 
179
258
  it 'allows specification of a text field that only exists in one type' do
@@ -77,15 +77,24 @@ describe 'highlighted fulltext queries', :type => :query do
77
77
  )
78
78
  end
79
79
 
80
- it 'should set options and hihglight fields' do
80
+ it 'should set highlight fields from DSL' do
81
81
  session.search(Post) do
82
82
  keywords 'test' do
83
- highlight :title, :max_snippets => 3
83
+ highlight :title
84
84
  end
85
85
  end
86
86
  connection.should have_last_search_with(
87
- :"hl.fl" => %w(title_text),
88
- :"hl.snippets" => 3
87
+ :"hl.fl" => %w(title_text)
88
+ )
89
+ end
90
+
91
+ it 'should not set formatting params specific to fields if fields specified' do
92
+ session.search(Post) do
93
+ keywords 'test', :highlight => :body
94
+ end
95
+ connection.should have_last_search_with(
96
+ :"hl.simple.pre" => '@@@hl@@@',
97
+ :"hl.simple.post" => '@@@endhl@@@'
89
98
  )
90
99
  end
91
100
 
@@ -100,6 +109,18 @@ describe 'highlighted fulltext queries', :type => :query do
100
109
  )
101
110
  end
102
111
 
112
+ it 'should set max snippets specific to highlight fields' do
113
+ session.search(Post) do
114
+ keywords 'test' do
115
+ highlight :title, :max_snippets => 3
116
+ end
117
+ end
118
+ connection.should have_last_search_with(
119
+ :"hl.fl" => %w(title_text),
120
+ :"f.title_text.hl.snippets" => 3
121
+ )
122
+ end
123
+
103
124
  it 'should set the maximum size' do
104
125
  session.search(Post) do
105
126
  keywords 'text' do
@@ -111,7 +132,18 @@ describe 'highlighted fulltext queries', :type => :query do
111
132
  )
112
133
  end
113
134
 
114
- it 'enables merging of continuous fragments' do
135
+ it 'should set the maximum size for specific fields' do
136
+ session.search(Post) do
137
+ keywords 'text' do
138
+ highlight :title, :fragment_size => 200
139
+ end
140
+ end
141
+ connection.should have_last_search_with(
142
+ :"f.title_text.hl.fragsize" => 200
143
+ )
144
+ end
145
+
146
+ it 'enables merging of contiguous fragments' do
115
147
  session.search(Post) do
116
148
  keywords 'test' do
117
149
  highlight :merge_contiguous_fragments => true
@@ -122,7 +154,18 @@ describe 'highlighted fulltext queries', :type => :query do
122
154
  )
123
155
  end
124
156
 
125
- it 'enables use of phrase highlighter' do #TODO figure out what the hell this means
157
+ it 'enables merging of contiguous fragments for specific fields' do
158
+ session.search(Post) do
159
+ keywords 'test' do
160
+ highlight :title, :merge_contiguous_fragments => true
161
+ end
162
+ end
163
+ connection.should have_last_search_with(
164
+ :"f.title_text.hl.mergeContiguous" => 'true'
165
+ )
166
+ end
167
+
168
+ it 'enables use of phrase highlighter' do
126
169
  session.search(Post) do
127
170
  keywords 'test' do
128
171
  highlight :phrase_highlighter => true
@@ -133,6 +176,17 @@ describe 'highlighted fulltext queries', :type => :query do
133
176
  )
134
177
  end
135
178
 
179
+ it 'enables use of phrase highlighter for specific fields' do
180
+ session.search(Post) do
181
+ keywords 'test' do
182
+ highlight :title, :phrase_highlighter => true
183
+ end
184
+ end
185
+ connection.should have_last_search_with(
186
+ :"f.title_text.hl.usePhraseHighlighter" => 'true'
187
+ )
188
+ end
189
+
136
190
  it 'requires field match if requested' do
137
191
  session.search(Post) do
138
192
  keywords 'test' do
@@ -144,4 +198,28 @@ describe 'highlighted fulltext queries', :type => :query do
144
198
  )
145
199
  end
146
200
 
201
+ it 'requires field match for specified field if requested' do
202
+ session.search(Post) do
203
+ keywords 'test' do
204
+ highlight :title, :phrase_highlighter => true, :require_field_match => true
205
+ end
206
+ end
207
+ connection.should have_last_search_with(
208
+ :"f.title_text.hl.requireFieldMatch" => 'true'
209
+ )
210
+ end
211
+
212
+ it 'sets field specific params for different fields if different params given' do
213
+ session.search(Post) do
214
+ keywords 'test' do
215
+ highlight :title, :max_snippets => 2
216
+ highlight :body, :max_snippets => 1
217
+ end
218
+ end
219
+ connection.should have_last_search_with(
220
+ :"hl.fl" => %w(title_text body_texts),
221
+ :"f.title_text.hl.snippets" => 2,
222
+ :"f.body_texts.hl.snippets" => 1
223
+ )
224
+ end
147
225
  end
@@ -81,6 +81,26 @@ describe 'faceting', :type => :search do
81
81
  facet_values(result, :class).should == [Post, Namespaced::Comment]
82
82
  end
83
83
 
84
+ it 'should return special :any facet' do
85
+ stub_query_facet(
86
+ 'category_ids_im:[* TO *]' => 3
87
+ )
88
+ search = session.search(Post) { facet(:category_ids, :extra => :any) }
89
+ row = search.facet(:category_ids).rows.first
90
+ row.value.should == :any
91
+ row.count.should == 3
92
+ end
93
+
94
+ it 'should return special :none facet' do
95
+ stub_query_facet(
96
+ '-category_ids_im:[* TO *]' => 3
97
+ )
98
+ search = session.search(Post) { facet(:category_ids, :extra => :none) }
99
+ row = search.facet(:category_ids).rows.first
100
+ row.value.should == :none
101
+ row.count.should == 3
102
+ end
103
+
84
104
  it 'should return date range facet' do
85
105
  stub_date_facet(:published_at_d, 60*60*24, '2009-07-08T04:00:00Z' => 2, '2009-07-07T04:00:00Z' => 1)
86
106
  start_time = Time.utc(2009, 7, 7, 4)
@@ -91,6 +111,16 @@ describe 'faceting', :type => :search do
91
111
  facet.rows.last.value.should == ((start_time+24*60*60)..end_time)
92
112
  end
93
113
 
114
+ it 'should return date range facet sorted by count' do
115
+ stub_date_facet(:published_at_d, 60*60*24, '2009-07-08T04:00:00Z' => 2, '2009-07-07T04:00:00Z' => 1)
116
+ start_time = Time.utc(2009, 7, 7, 4)
117
+ end_time = start_time + 2*24*60*60
118
+ result = session.search(Post) { facet(:published_at, :time_range => start_time..end_time, :sort => :count) }
119
+ facet = result.facet(:published_at)
120
+ facet.rows.first.value.should == ((start_time+24*60*60)..end_time)
121
+ facet.rows.last.value.should == (start_time..(start_time+24*60*60))
122
+ end
123
+
94
124
  it 'returns query facet' do
95
125
  stub_query_facet(
96
126
  'average_rating_f:[3\.0 TO 5\.0]' => 3,
@@ -118,22 +148,28 @@ describe 'faceting', :type => :search do
118
148
  session.search(Post) do
119
149
  facet :average_rating, options do
120
150
  row(1) { with(:average_rating, 1.0..2.0) }
121
- row(2) { with(:average_rating, 2.0..3.0) }
122
151
  row(3) { with(:average_rating, 3.0..4.0) }
152
+ row(2) { with(:average_rating, 2.0..3.0) }
153
+ row(4) { with(:average_rating, 4.0..5.0) }
123
154
  end
124
155
  end.facet(:average_rating).rows.map { |row| row.value }
125
156
  end
126
157
 
127
158
  before :each do
128
159
  stub_query_facet(
129
- 'average_rating_f:[1\.0 TO 2\.0]' => 1,
130
- 'average_rating_f:[2\.0 TO 3\.0]' => 2,
131
- 'average_rating_f:[3\.0 TO 4\.0]' => 0
160
+ 'average_rating_f:[1\.0 TO 2\.0]' => 2,
161
+ 'average_rating_f:[2\.0 TO 3\.0]' => 3,
162
+ 'average_rating_f:[3\.0 TO 4\.0]' => 1,
163
+ 'average_rating_f:[4\.0 TO 5\.0]' => 0
132
164
  )
133
165
  end
134
166
 
135
- it 'sorts lexically by default if no limit is given' do
136
- facet_values_from_options.should == [1, 2]
167
+ it 'sorts in order of specification if no limit is given' do
168
+ facet_values_from_options.should == [1, 3, 2]
169
+ end
170
+
171
+ it 'sorts lexically if lexical option is specified' do
172
+ facet_values_from_options(:sort => :index).should == [1, 2, 3]
137
173
  end
138
174
 
139
175
  it 'sorts by count by default if limit is given' do
@@ -141,7 +177,7 @@ describe 'faceting', :type => :search do
141
177
  end
142
178
 
143
179
  it 'sorts by count if count option is specified' do
144
- facet_values_from_options(:sort => :count).should == [2, 1]
180
+ facet_values_from_options(:sort => :count).should == [2, 1, 3]
145
181
  end
146
182
 
147
183
  it 'sorts lexically if lexical option is specified even if limit is given' do
@@ -153,11 +189,11 @@ describe 'faceting', :type => :search do
153
189
  end
154
190
 
155
191
  it 'allows zero count if specified' do
156
- facet_values_from_options(:zeros => true).should == [1, 2, 3]
192
+ facet_values_from_options(:zeros => true).should == [1, 3, 2, 4]
157
193
  end
158
194
 
159
195
  it 'sets minimum count' do
160
- facet_values_from_options(:minimum_count => 2).should == [2]
196
+ facet_values_from_options(:minimum_count => 2).should == [1, 2]
161
197
  end
162
198
  end
163
199
 
@@ -21,8 +21,8 @@ describe 'search with highlighting results', :type => :search do
21
21
  @search.hits.last.should have(2).highlights(:body)
22
22
  end
23
23
 
24
- it 'returns nil if a given field does not have a highlight' do
25
- @search.hits.first.highlights(:body).should be_nil
24
+ it 'returns an empty array if a given field does not have a highlight' do
25
+ @search.hits.first.highlights(:body).should == []
26
26
  end
27
27
 
28
28
  it 'should format hits with <em> by default' do
@@ -44,6 +44,11 @@ describe 'hits', :type => :search do
44
44
  session.search(Post, Namespaced::Comment).hits.first.stored(:title).should == 'Title'
45
45
  end
46
46
 
47
+ it 'should return stored text fields' do
48
+ stub_full_results('instance' => Post.new, 'body_texts' => 'Body')
49
+ session.search(Post, Namespaced::Comment).hits.first.stored(:body).should == 'Body'
50
+ end
51
+
47
52
  it 'should typecast stored field values in hits' do
48
53
  time = Time.utc(2008, 7, 8, 2, 45)
49
54
  stub_full_results('instance' => Post.new, 'last_indexed_at_ds' => time.xmlschema)
@@ -46,6 +46,7 @@ describe 'search faceting' do
46
46
  facet_values.each_with_index do |value, i|
47
47
  i.times { Sunspot.index(Post.new(:title => value, :blog_id => 1)) }
48
48
  end
49
+ Sunspot.index(Post.new(:blog_id => 1))
49
50
  Sunspot.index(Post.new(:title => 'zero', :blog_id => 2))
50
51
  Sunspot.commit
51
52
  end
@@ -96,6 +97,24 @@ describe 'search faceting' do
96
97
  end
97
98
  search.facet(:title).rows.map { |row| row.value }.should == %w(four three two one)
98
99
  end
100
+
101
+ it 'should return :all facet' do
102
+ search = Sunspot.search(Post) do
103
+ with :blog_id, 1
104
+ facet :title, :extra => :any
105
+ end
106
+ search.facet(:title).rows.first.value.should == :any
107
+ search.facet(:title).rows.first.count.should == 10
108
+ end
109
+
110
+ it 'should return :none facet' do
111
+ search = Sunspot.search(Post) do
112
+ with :blog_id, 1
113
+ facet :title, :extra => :none
114
+ end
115
+ search.facet(:title).rows.first.value.should == :none
116
+ search.facet(:title).rows.first.count.should == 1
117
+ end
99
118
  end
100
119
 
101
120
  context 'date facets' do