sunspot 0.10.5 → 0.10.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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