sunspot 0.9.8 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +40 -3
  3. data/TODO +10 -8
  4. data/VERSION.yml +2 -2
  5. data/bin/sunspot-configure-solr +22 -28
  6. data/bin/sunspot-solr +50 -29
  7. data/lib/sunspot/adapters.rb +1 -1
  8. data/lib/sunspot/composite_setup.rb +13 -15
  9. data/lib/sunspot/configuration.rb +14 -0
  10. data/lib/sunspot/data_extractor.rb +3 -0
  11. data/lib/sunspot/dsl/field_query.rb +33 -6
  12. data/lib/sunspot/dsl/fields.rb +14 -1
  13. data/lib/sunspot/dsl/fulltext.rb +168 -0
  14. data/lib/sunspot/dsl/query.rb +82 -5
  15. data/lib/sunspot/dsl/query_facet.rb +3 -3
  16. data/lib/sunspot/dsl/restriction.rb +7 -7
  17. data/lib/sunspot/dsl/scope.rb +17 -10
  18. data/lib/sunspot/dsl/search.rb +2 -2
  19. data/lib/sunspot/dsl.rb +2 -1
  20. data/lib/sunspot/facet.rb +9 -1
  21. data/lib/sunspot/facet_data.rb +56 -7
  22. data/lib/sunspot/facet_row.rb +2 -0
  23. data/lib/sunspot/field.rb +50 -26
  24. data/lib/sunspot/field_factory.rb +15 -0
  25. data/lib/sunspot/indexer.rb +6 -0
  26. data/lib/sunspot/instantiated_facet.rb +6 -9
  27. data/lib/sunspot/instantiated_facet_row.rb +7 -2
  28. data/lib/sunspot/query/boost_query.rb +20 -0
  29. data/lib/sunspot/query/connective.rb +98 -35
  30. data/lib/sunspot/query/dismax.rb +69 -0
  31. data/lib/sunspot/query/field_facet.rb +1 -22
  32. data/lib/sunspot/query/fulltext_base_query.rb +47 -0
  33. data/lib/sunspot/query/highlighting.rb +43 -0
  34. data/lib/sunspot/query/local.rb +24 -0
  35. data/lib/sunspot/query/pagination.rb +3 -4
  36. data/lib/sunspot/query/query.rb +93 -0
  37. data/lib/sunspot/query/query_facet.rb +14 -9
  38. data/lib/sunspot/query/query_facet_row.rb +3 -3
  39. data/lib/sunspot/query/query_field_facet.rb +10 -3
  40. data/lib/sunspot/query/restriction.rb +36 -15
  41. data/lib/sunspot/query/scope.rb +3 -159
  42. data/lib/sunspot/query/sort.rb +84 -15
  43. data/lib/sunspot/query/text_field_boost.rb +15 -0
  44. data/lib/sunspot/query.rb +2 -188
  45. data/lib/sunspot/schema.rb +7 -25
  46. data/lib/sunspot/search/highlight.rb +38 -0
  47. data/lib/sunspot/search/hit.rb +50 -3
  48. data/lib/sunspot/search.rb +51 -32
  49. data/lib/sunspot/session.rb +32 -12
  50. data/lib/sunspot/setup.rb +47 -10
  51. data/lib/sunspot/text_field_setup.rb +29 -0
  52. data/lib/sunspot/type.rb +4 -4
  53. data/lib/sunspot/util.rb +27 -1
  54. data/lib/sunspot.rb +8 -17
  55. data/solr/solr/conf/schema.xml +54 -40
  56. data/solr/solr/conf/solrconfig.xml +30 -0
  57. data/solr/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
  58. data/solr/solr/lib/gt2-referencing-2.3.1.jar +0 -0
  59. data/solr/solr/lib/jsr108-0.01.jar +0 -0
  60. data/solr/solr/lib/locallucene.jar +0 -0
  61. data/solr/solr/lib/localsolr.jar +0 -0
  62. data/spec/api/indexer/attributes_spec.rb +100 -0
  63. data/spec/api/indexer/batch_spec.rb +46 -0
  64. data/spec/api/indexer/dynamic_fields_spec.rb +33 -0
  65. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  66. data/spec/api/indexer/fulltext_spec.rb +43 -0
  67. data/spec/api/indexer/removal_spec.rb +46 -0
  68. data/spec/api/indexer/spec_helper.rb +1 -0
  69. data/spec/api/indexer_spec.rb +1 -308
  70. data/spec/api/query/connectives_spec.rb +162 -0
  71. data/spec/api/query/dsl_spec.rb +12 -0
  72. data/spec/api/query/dynamic_fields_spec.rb +149 -0
  73. data/spec/api/query/faceting_spec.rb +272 -0
  74. data/spec/api/query/fulltext_spec.rb +193 -0
  75. data/spec/api/query/highlighting_spec.rb +138 -0
  76. data/spec/api/query/local_spec.rb +54 -0
  77. data/spec/api/query/ordering_pagination_spec.rb +95 -0
  78. data/spec/api/query/scope_spec.rb +266 -0
  79. data/spec/api/query/spec_helper.rb +1 -0
  80. data/spec/api/query/text_field_scoping_spec.rb +30 -0
  81. data/spec/api/query/types_spec.rb +20 -0
  82. data/spec/api/search/dynamic_fields_spec.rb +27 -0
  83. data/spec/api/search/faceting_spec.rb +206 -0
  84. data/spec/api/search/highlighting_spec.rb +65 -0
  85. data/spec/api/search/hits_spec.rb +62 -0
  86. data/spec/api/search/results_spec.rb +52 -0
  87. data/spec/api/search/search_spec.rb +23 -0
  88. data/spec/api/search/spec_helper.rb +1 -0
  89. data/spec/api/spec_helper.rb +1 -1
  90. data/spec/helpers/indexer_helper.rb +29 -0
  91. data/spec/helpers/query_helper.rb +13 -0
  92. data/spec/helpers/search_helper.rb +78 -0
  93. data/spec/integration/faceting_spec.rb +1 -1
  94. data/spec/integration/highlighting_spec.rb +22 -0
  95. data/spec/integration/keyword_search_spec.rb +65 -0
  96. data/spec/integration/local_search_spec.rb +56 -0
  97. data/spec/integration/scoped_search_spec.rb +15 -1
  98. data/spec/integration/spec_helper.rb +3 -3
  99. data/spec/mocks/connection.rb +14 -1
  100. data/spec/mocks/photo.rb +1 -1
  101. data/spec/mocks/post.rb +5 -3
  102. data/spec/mocks/super_class.rb +2 -0
  103. data/spec/spec_helper.rb +13 -0
  104. data/tasks/gemspec.rake +18 -7
  105. data/tasks/schema.rake +1 -1
  106. data/tasks/spec.rake +1 -1
  107. data/templates/schema.xml.erb +36 -0
  108. metadata +117 -48
  109. data/lib/sunspot/query/base_query.rb +0 -90
  110. data/lib/sunspot/query/dynamic_query.rb +0 -69
  111. data/lib/sunspot/query/field_query.rb +0 -63
  112. data/spec/api/build_search_spec.rb +0 -1017
  113. data/spec/api/query_spec.rb +0 -153
  114. data/spec/api/search_retrieval_spec.rb +0 -362
  115. data/templates/schema.xml.haml +0 -24
@@ -1,1017 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
-
3
- describe 'Search' do
4
- it 'should search by keywords from DSL' do
5
- session.search Post do
6
- keywords 'keyword search'
7
- end
8
- connection.should have_last_search_with(:q => 'keyword search')
9
- end
10
-
11
- it 'should search by keywords from options' do
12
- session.search Post, :keywords => 'keyword search'
13
- connection.should have_last_search_with(:q => 'keyword search')
14
- end
15
-
16
- it 'should set default query parser to dismax when keywords used' do
17
- session.search Post do
18
- keywords 'keyword search'
19
- end
20
- connection.should have_last_search_with(:defType => 'dismax')
21
- end
22
-
23
- it 'should search types in filter query if keywords used' do
24
- session.search Post do
25
- keywords 'keyword search'
26
- end
27
- connection.should have_last_search_with(:fq => 'type:Post')
28
- end
29
-
30
- it 'should search types in main query if keywords not used' do
31
- session.search Post
32
- connection.should have_last_search_with(:q => 'type:Post')
33
- end
34
-
35
- it 'should search type of subclass when superclass is configured' do
36
- session.search PhotoPost
37
- connection.should have_last_search_with(:q => 'type:PhotoPost')
38
- end
39
-
40
- it 'should search all text fields for searched class' do
41
- session.search Post do
42
- keywords 'keyword search'
43
- end
44
- connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_text title_text)
45
- end
46
-
47
- it 'should search only specified text fields when specified' do
48
- session.search Post do
49
- keywords 'keyword search', :fields => [:title, :body]
50
- end
51
- connection.searches.last[:qf].split(' ').sort.should == %w(body_text title_text)
52
- end
53
-
54
- it 'should request score when keywords used' do
55
- session.search Post, :keywords => 'keyword search'
56
- connection.should have_last_search_with(:fl => '* score')
57
- end
58
-
59
- it 'should not request score when keywords not used' do
60
- session.search Post
61
- connection.should_not have_last_search_with(:fl)
62
- end
63
-
64
- it 'should scope by exact match with a string from DSL' do
65
- session.search Post do
66
- with :title, 'My Pet Post'
67
- end
68
- connection.should have_last_search_with(:fq => ['title_ss:My\ Pet\ Post'])
69
- end
70
-
71
- it 'should scope by exact match with a string from options' do
72
- session.search Post, :conditions => { :title => 'My Pet Post' }
73
- connection.should have_last_search_with(:fq => ['title_ss:My\ Pet\ Post'])
74
- end
75
-
76
- it 'should ignore nonexistant fields in hash scope' do
77
- session.search Post, :conditions => { :bogus => 'Field' }
78
- connection.should_not have_last_search_with(:fq)
79
- end
80
-
81
- it 'should scope by exact match with time' do
82
- time = Time.parse('1983-07-08 05:00:00 -0400')
83
- session.search Post do
84
- with :published_at, time
85
- end
86
- connection.should have_last_search_with(
87
- :fq => ['published_at_d:1983\-07\-08T09\:00\:00Z']
88
- )
89
- end
90
-
91
- it 'should scope by exact match with date' do
92
- date = Date.new(1983, 7, 8)
93
- session.search Post do
94
- with :expire_date, date
95
- end
96
- connection.should have_last_search_with(
97
- :fq => ['expire_date_d:1983\-07\-08T00\:00\:00Z']
98
- )
99
- end
100
-
101
- it 'should scope by exact match with boolean' do
102
- session.search Post do
103
- with :featured, false
104
- end
105
- connection.should have_last_search_with(:fq => ['featured_b:false'])
106
- end
107
-
108
- it 'should scope by less than match with float' do
109
- session.search Post do
110
- with(:average_rating).less_than 3.0
111
- end
112
- connection.should have_last_search_with(:fq => ['average_rating_f:[* TO 3\.0]'])
113
- end
114
-
115
- it 'should scope by greater than match with float' do
116
- session.search Post do
117
- with(:average_rating).greater_than 3.0
118
- end
119
- connection.should have_last_search_with(:fq => ['average_rating_f:[3\.0 TO *]'])
120
- end
121
-
122
- it 'should scope by short-form between match with integers' do
123
- session.search Post do
124
- with :blog_id, 2..4
125
- end
126
- connection.should have_last_search_with(:fq => ['blog_id_i:[2 TO 4]'])
127
- end
128
-
129
- it 'should scope by between match with float' do
130
- session.search Post do
131
- with(:average_rating).between 2.0..4.0
132
- end
133
- connection.should have_last_search_with(:fq => ['average_rating_f:[2\.0 TO 4\.0]'])
134
- end
135
-
136
- it 'should scope by any match with integer using DSL' do
137
- session.search Post do
138
- with(:category_ids).any_of [2, 7, 12]
139
- end
140
- connection.should have_last_search_with(:fq => ['category_ids_im:(2 OR 7 OR 12)'])
141
- end
142
-
143
- it 'should scope by any match with integer using options' do
144
- session.search Post, :conditions => { :category_ids => [2, 7, 12] }
145
- connection.should have_last_search_with(:fq => ['category_ids_im:(2 OR 7 OR 12)'])
146
- end
147
-
148
- it 'should scope by short-form any-of match with integers' do
149
- session.search Post do
150
- with :category_ids, [2, 7, 12]
151
- end
152
- connection.should have_last_search_with(:fq => ['category_ids_im:(2 OR 7 OR 12)'])
153
- end
154
-
155
- it 'should scope by all match with integer' do
156
- session.search Post do
157
- with(:category_ids).all_of [2, 7, 12]
158
- end
159
- connection.should have_last_search_with(:fq => ['category_ids_im:(2 AND 7 AND 12)'])
160
- end
161
-
162
- it 'should scope by not equal match with string' do
163
- session.search Post do
164
- without :title, 'Bad Post'
165
- end
166
- connection.should have_last_search_with(:fq => ['-title_ss:Bad\ Post'])
167
- end
168
-
169
- it 'should scope by not less than match with float' do
170
- session.search Post do
171
- without(:average_rating).less_than 3.0
172
- end
173
- connection.should have_last_search_with(:fq => ['-average_rating_f:[* TO 3\.0]'])
174
- end
175
-
176
- it 'should scope by not greater than match with float' do
177
- session.search Post do
178
- without(:average_rating).greater_than 3.0
179
- end
180
- connection.should have_last_search_with(:fq => ['-average_rating_f:[3\.0 TO *]'])
181
- end
182
-
183
- it 'should scope by not between match with shorthand' do
184
- session.search Post do
185
- without(:blog_id, 2..4)
186
- end
187
- connection.should have_last_search_with(:fq => ['-blog_id_i:[2 TO 4]'])
188
- end
189
-
190
- it 'should scope by not between match with float' do
191
- session.search Post do
192
- without(:average_rating).between 2.0..4.0
193
- end
194
- connection.should have_last_search_with(:fq => ['-average_rating_f:[2\.0 TO 4\.0]'])
195
- end
196
-
197
- it 'should scope by not any match with integer' do
198
- session.search Post do
199
- without(:category_ids).any_of [2, 7, 12]
200
- end
201
- connection.should have_last_search_with(:fq => ['-category_ids_im:(2 OR 7 OR 12)'])
202
- end
203
-
204
-
205
- it 'should scope by not all match with integer' do
206
- session.search Post do
207
- without(:category_ids).all_of [2, 7, 12]
208
- end
209
- connection.should have_last_search_with(:fq => ['-category_ids_im:(2 AND 7 AND 12)'])
210
- end
211
-
212
- it 'should scope by empty field' do
213
- session.search Post do
214
- with :average_rating, nil
215
- end
216
- connection.should have_last_search_with(:fq => ['-average_rating_f:[* TO *]'])
217
- end
218
-
219
- it 'should scope by non-empty field' do
220
- session.search Post do
221
- without :average_rating, nil
222
- end
223
- connection.should have_last_search_with(:fq => ['average_rating_f:[* TO *]'])
224
- end
225
-
226
- it 'should exclude by object identity' do
227
- post = Post.new
228
- session.search Post do
229
- without post
230
- end
231
- connection.should have_last_search_with(:fq => ["-id:Post\\ #{post.id}"])
232
- end
233
-
234
- it 'should exclude multiple objects passed as varargs by object identity' do
235
- post1, post2 = Post.new, Post.new
236
- session.search Post do
237
- without post1, post2
238
- end
239
- connection.should have_last_search_with(
240
- :fq => ["-id:Post\\ #{post1.id}", "-id:Post\\ #{post2.id}"]
241
- )
242
- end
243
-
244
- it 'should exclude multiple objects passed as array by object identity' do
245
- posts = [Post.new, Post.new]
246
- session.search Post do
247
- without posts
248
- end
249
- connection.should have_last_search_with(
250
- :fq => ["-id:Post\\ #{posts.first.id}", "-id:Post\\ #{posts.last.id}"]
251
- )
252
- end
253
-
254
- it 'should create a disjunction between two restrictions' do
255
- session.search Post do
256
- any_of do
257
- with :category_ids, 1
258
- with :blog_id, 2
259
- end
260
- end
261
- connection.should have_last_search_with(
262
- :fq => '(category_ids_im:1 OR blog_id_i:2)'
263
- )
264
- end
265
-
266
- it 'should create a conjunction inside of a disjunction' do
267
- session.search Post do
268
- any_of do
269
- with :blog_id, 2
270
- all_of do
271
- with :category_ids, 1
272
- with(:average_rating).greater_than(3.0)
273
- end
274
- end
275
- end
276
- connection.should have_last_search_with(
277
- :fq => '(blog_id_i:2 OR (category_ids_im:1 AND average_rating_f:[3\.0 TO *]))'
278
- )
279
- end
280
-
281
- it 'should do nothing special if #all_of called from the top level' do
282
- session.search Post do
283
- all_of do
284
- with :blog_id, 2
285
- with :category_ids, 1
286
- end
287
- end
288
- connection.should have_last_search_with(
289
- :fq => ['blog_id_i:2', 'category_ids_im:1']
290
- )
291
- end
292
-
293
- it 'should create a disjunction with negated restrictions' do
294
- session.search Post do
295
- any_of do
296
- with :category_ids, 1
297
- without(:average_rating).greater_than(3.0)
298
- end
299
- end
300
- connection.should have_last_search_with(
301
- :fq => '-(-category_ids_im:1 AND average_rating_f:[3\.0 TO *])'
302
- )
303
- end
304
-
305
- it 'should create a disjunction with nested conjunction with negated restrictions' do
306
- session.search Post do
307
- any_of do
308
- with :category_ids, 1
309
- all_of do
310
- without(:average_rating).greater_than(3.0)
311
- with(:blog_id, 1)
312
- end
313
- end
314
- end
315
- connection.should have_last_search_with(
316
- :fq => '(category_ids_im:1 OR (-average_rating_f:[3\.0 TO *] AND blog_id_i:1))'
317
- )
318
- end
319
-
320
- it 'should create a disjunction with nested conjunction with nested disjunction with negated restriction' do
321
- session.search(Post) do
322
- any_of do
323
- with(:title, 'Yes')
324
- all_of do
325
- with(:blog_id, 1)
326
- any_of do
327
- with(:category_ids, 4)
328
- without(:average_rating, 2.0)
329
- end
330
- end
331
- end
332
- end
333
- connection.should have_last_search_with(
334
- :fq => '(title_ss:Yes OR (blog_id_i:1 AND -(-category_ids_im:4 AND average_rating_f:2\.0)))'
335
- )
336
- end
337
-
338
- it 'should create a disjunction with a negated restriction and a nested disjunction in a conjunction with a negated restriction' do
339
- session.search(Post) do
340
- any_of do
341
- without(:title, 'Yes')
342
- all_of do
343
- with(:blog_id, 1)
344
- any_of do
345
- with(:category_ids, 4)
346
- without(:average_rating, 2.0)
347
- end
348
- end
349
- end
350
- end
351
- connection.should have_last_search_with(
352
- :fq => '-(title_ss:Yes AND -(blog_id_i:1 AND -(-category_ids_im:4 AND average_rating_f:2\.0)))'
353
- )
354
- end
355
-
356
- #
357
- # This is important because if a disjunction could be nested in another
358
- # disjunction, then the inner disjunction could denormalize (and thus
359
- # become negated) after the outer disjunction denormalized (checking to
360
- # see if the inner one is negated). Since conjunctions never need to
361
- # denormalize, if a disjunction can only contain conjunctions or restrictions,
362
- # we can guarantee that the negation state of a disjunction's components will
363
- # not change when #to_params is called on them.
364
- #
365
- # Since disjunction is associative, this behavior has no effect on the actual
366
- # logical semantics of the disjunction.
367
- #
368
- it 'should create a single disjunction when disjunctions nested' do
369
- session.search(Post) do
370
- any_of do
371
- with(:title, 'Yes')
372
- any_of do
373
- with(:blog_id, 1)
374
- with(:category_ids, 4)
375
- end
376
- end
377
- end
378
- connection.should have_last_search_with(
379
- :fq => '(title_ss:Yes OR blog_id_i:1 OR category_ids_im:4)'
380
- )
381
- end
382
-
383
- it 'should create a disjunction with instance exclusion' do
384
- post = Post.new
385
- session.search Post do
386
- any_of do
387
- without(post)
388
- with(:category_ids, 1)
389
- end
390
- end
391
- connection.should have_last_search_with(
392
- :fq => "-(id:Post\\ #{post.id} AND -category_ids_im:1)"
393
- )
394
- end
395
-
396
- it 'should create a disjunction with empty restriction' do
397
- session.search Post do
398
- any_of do
399
- with(:average_rating, nil)
400
- with(:average_rating).greater_than(3.0)
401
- end
402
- end
403
- connection.should have_last_search_with(
404
- :fq => '-(average_rating_f:[* TO *] AND -average_rating_f:[3\.0 TO *])'
405
- )
406
- end
407
-
408
- it 'should restrict by dynamic string field with equality restriction' do
409
- session.search Post do
410
- dynamic :custom_string do
411
- with :test, 'string'
412
- end
413
- end
414
- connection.should have_last_search_with(:fq => ['custom_string\:test_s:string'])
415
- end
416
-
417
- it 'should restrict by dynamic integer field with less than restriction' do
418
- session.search Post do
419
- dynamic :custom_integer do
420
- with(:test).less_than(1)
421
- end
422
- end
423
- connection.should have_last_search_with(:fq => ['custom_integer\:test_i:[* TO 1]'])
424
- end
425
-
426
- it 'should restrict by dynamic float field with between restriction' do
427
- session.search Post do
428
- dynamic :custom_float do
429
- with(:test).between(2.2..3.3)
430
- end
431
- end
432
- connection.should have_last_search_with(:fq => ['custom_float\:test_fm:[2\.2 TO 3\.3]'])
433
- end
434
-
435
- it 'should restrict by dynamic time field with any of restriction' do
436
- session.search Post do
437
- dynamic :custom_time do
438
- with(:test).any_of([Time.parse('2009-02-10 14:00:00 UTC'),
439
- Time.parse('2009-02-13 18:00:00 UTC')])
440
- end
441
- end
442
- connection.should have_last_search_with(:fq => ['custom_time\:test_d:(2009\-02\-10T14\:00\:00Z OR 2009\-02\-13T18\:00\:00Z)'])
443
- end
444
-
445
- it 'should restrict by dynamic boolean field with equality restriction' do
446
- session.search Post do
447
- dynamic :custom_boolean do
448
- with :test, false
449
- end
450
- end
451
- connection.should have_last_search_with(:fq => ['custom_boolean\:test_b:false'])
452
- end
453
-
454
- it 'should negate a dynamic field restriction' do
455
- session.search Post do
456
- dynamic :custom_string do
457
- without :test, 'foo'
458
- end
459
- end
460
- connection.should have_last_search_with(:fq => ['-custom_string\:test_s:foo'])
461
- end
462
-
463
- it 'should search by a dynamic field inside a disjunction' do
464
- session.search Post do
465
- any_of do
466
- dynamic :custom_string do
467
- with :test, 'foo'
468
- end
469
- with :title, 'bar'
470
- end
471
- end
472
- connection.should have_last_search_with(
473
- :fq => '(custom_string\:test_s:foo OR title_ss:bar)'
474
- )
475
- end
476
-
477
- it 'should throw an UnrecognizedFieldError if an unknown dynamic field is searched by' do
478
- lambda do
479
- session.search Post do
480
- dynamic(:bogus) { with :some, 'value' }
481
- end
482
- end.should raise_error(Sunspot::UnrecognizedFieldError)
483
- end
484
-
485
- it 'should throw a NoMethodError if pagination is attempted in a dynamic query' do
486
- lambda do
487
- session.search Post do
488
- dynamic :custom_string do
489
- paginate 3, 10
490
- end
491
- end
492
- end.should raise_error(NoMethodError)
493
- end
494
-
495
- it 'should paginate using default per_page when page not provided' do
496
- session.search Post
497
- connection.should have_last_search_with(:rows => 30)
498
- end
499
-
500
- it 'should paginate using default per_page when page provided in DSL' do
501
- session.search Post do
502
- paginate :page => 2
503
- end
504
- connection.should have_last_search_with(:rows => 30, :start => 30)
505
- end
506
-
507
- it 'should paginate using default per_page when page provided in options' do
508
- session.search Post, :page => 2
509
- connection.should have_last_search_with(:rows => 30, :start => 30)
510
- end
511
-
512
- it 'should paginate using provided per_page in DSL' do
513
- session.search Post do
514
- paginate :page => 4, :per_page => 15
515
- end
516
- connection.should have_last_search_with(:rows => 15, :start => 45)
517
- end
518
-
519
- it 'should paginate using provided per_page in options' do
520
- session.search Post, :page => 4, :per_page => 15
521
- connection.should have_last_search_with(:rows => 15, :start => 45)
522
- end
523
-
524
- it 'should order in DSL' do
525
- session.search Post do
526
- order_by :average_rating, :desc
527
- end
528
- connection.should have_last_search_with(:sort => 'average_rating_f desc')
529
- end
530
-
531
- it 'should order in keywords' do
532
- session.search Post, :order => 'average_rating desc'
533
- connection.should have_last_search_with(:sort => 'average_rating_f desc')
534
- end
535
-
536
- it 'should order by multiple fields in DSL' do
537
- session.search Post do
538
- order_by :average_rating, :desc
539
- order_by :sort_title, :asc
540
- end
541
- connection.should have_last_search_with(:sort => 'average_rating_f desc, sort_title_s asc')
542
- end
543
-
544
- it 'should order by multiple fields in options' do
545
- session.search Post, :order => ['average_rating desc', 'sort_title asc']
546
- connection.should have_last_search_with(:sort => 'average_rating_f desc, sort_title_s asc')
547
- end
548
-
549
- it 'should order by a dynamic field' do
550
- session.search Post do
551
- dynamic :custom_integer do
552
- order_by :test, :desc
553
- end
554
- end
555
- connection.should have_last_search_with(:sort => 'custom_integer:test_i desc')
556
- end
557
-
558
- it 'should order by a dynamic field and static field, with given precedence' do
559
- session.search Post do
560
- dynamic :custom_integer do
561
- order_by :test, :desc
562
- end
563
- order_by :sort_title, :asc
564
- end
565
- connection.should have_last_search_with(:sort => 'custom_integer:test_i desc, sort_title_s asc')
566
- end
567
-
568
- it 'should order by random' do
569
- session.search Post do
570
- order_by_random
571
- end
572
- connection.searches.last[:sort].should =~ /^random_\d+ asc$/
573
- end
574
-
575
- it 'should throw an ArgumentError if a bogus order direction is given' do
576
- lambda do
577
- session.search Post do
578
- order_by :sort_title, :sideways
579
- end
580
- end.should raise_error(ArgumentError)
581
- end
582
-
583
- it 'should not allow ordering by multiple-value fields' do
584
- lambda do
585
- session.search Post do
586
- order_by :category_ids
587
- end
588
- end.should raise_error(ArgumentError)
589
- end
590
-
591
- it 'should not turn faceting on if no facet requested' do
592
- session.search(Post)
593
- connection.should_not have_last_search_with('facet')
594
- end
595
-
596
- it 'should turn faceting on if facet is requested' do
597
- session.search Post do
598
- facet :category_ids
599
- end
600
- connection.should have_last_search_with(:facet => 'true')
601
- end
602
-
603
- it 'should request single field facet' do
604
- session.search Post do
605
- facet :category_ids
606
- end
607
- connection.should have_last_search_with(:"facet.field" => %w(category_ids_im))
608
- end
609
-
610
- it 'should request multiple field facets' do
611
- session.search Post do
612
- facet :category_ids, :blog_id
613
- end
614
- connection.should have_last_search_with(:"facet.field" => %w(category_ids_im blog_id_i))
615
- end
616
-
617
- it 'should set facet sort by count' do
618
- session.search Post do
619
- facet :category_ids, :sort => :count
620
- end
621
- connection.should have_last_search_with(:"f.category_ids_im.facet.sort" => 'true')
622
- end
623
-
624
- it 'should set facet sort by index' do
625
- session.search Post do
626
- facet :category_ids, :sort => :index
627
- end
628
- connection.should have_last_search_with(:"f.category_ids_im.facet.sort" => 'false')
629
- end
630
-
631
- it 'should throw ArgumentError if bogus facet sort provided' do
632
- lambda do
633
- session.search Post do
634
- facet :category_ids, :sort => :sideways
635
- end
636
- end.should raise_error(ArgumentError)
637
- end
638
-
639
- it 'should set the facet limit' do
640
- session.search Post do
641
- facet :category_ids, :limit => 10
642
- end
643
- connection.should have_last_search_with(:"f.category_ids_im.facet.limit" => 10)
644
- end
645
-
646
- it 'should set the facet minimum count' do
647
- session.search Post do
648
- facet :category_ids, :minimum_count => 5
649
- end
650
- connection.should have_last_search_with(:"f.category_ids_im.facet.mincount" => 5)
651
- end
652
-
653
- it 'should set the facet minimum count to zero if zeros are allowed' do
654
- session.search Post do
655
- facet :category_ids, :zeros => true
656
- end
657
- connection.should have_last_search_with(:"f.category_ids_im.facet.mincount" => 0)
658
- end
659
-
660
- it 'should set the facet minimum count to one by default' do
661
- session.search Post do
662
- facet :category_ids
663
- end
664
- connection.should have_last_search_with(:"f.category_ids_im.facet.mincount" => 1)
665
- end
666
-
667
- describe 'with date faceting' do
668
- before :each do
669
- @time_range = (Time.parse('2009-06-01 00:00:00 -0400')..
670
- Time.parse('2009-07-01 00:00:00 -0400'))
671
- end
672
-
673
- it 'should not send date facet parameters if time range is not specified' do
674
- session.search Post do |query|
675
- query.facet :published_at
676
- end
677
- connection.should_not have_last_search_with(:"facet.date")
678
- end
679
-
680
- it 'should set the facet to a date facet' do
681
- session.search Post do |query|
682
- query.facet :published_at, :time_range => @time_range
683
- end
684
- connection.should have_last_search_with(:"facet.date" => ['published_at_d'])
685
- end
686
-
687
- it 'should set the facet start and end' do
688
- session.search Post do |query|
689
- query.facet :published_at, :time_range => @time_range
690
- end
691
- connection.should have_last_search_with(
692
- :"f.published_at_d.facet.date.start" => '2009-06-01T04:00:00Z',
693
- :"f.published_at_d.facet.date.end" => '2009-07-01T04:00:00Z'
694
- )
695
- end
696
-
697
- it 'should default the time interval to 1 day' do
698
- session.search Post do |query|
699
- query.facet :published_at, :time_range => @time_range
700
- end
701
- connection.should have_last_search_with(:"f.published_at_d.facet.date.gap" => "+86400SECONDS")
702
- end
703
-
704
- it 'should use custom time interval' do
705
- session.search Post do |query|
706
- query.facet :published_at, :time_range => @time_range, :time_interval => 3600
707
- end
708
- connection.should have_last_search_with(:"f.published_at_d.facet.date.gap" => "+3600SECONDS")
709
- end
710
-
711
- it 'should not allow date faceting on a non-date field' do
712
- lambda do
713
- session.search Post do |query|
714
- query.facet :blog_id, :time_range => @time_range
715
- end
716
- end.should raise_error(ArgumentError)
717
- end
718
- end
719
-
720
- describe 'with query faceting' do
721
- it 'should turn faceting on' do
722
- session.search Post do
723
- facet :foo do
724
- row :bar do
725
- with(:average_rating).between(4.0..5.0)
726
- end
727
- end
728
- end
729
- connection.should have_last_search_with(:facet => 'true')
730
- end
731
-
732
- it 'should facet by query' do
733
- session.search Post do
734
- facet :foo do
735
- row :bar do
736
- with(:average_rating).between(4.0..5.0)
737
- end
738
- end
739
- end
740
- connection.should have_last_search_with(:"facet.query" => 'average_rating_f:[4\.0 TO 5\.0]')
741
- end
742
-
743
- it 'should request multiple query facets' do
744
- session.search Post do
745
- facet :foo do
746
- row :bar do
747
- with(:average_rating).between(3.0..4.0)
748
- end
749
- row :baz do
750
- with(:average_rating).between(4.0..5.0)
751
- end
752
- end
753
- end
754
- connection.should have_last_search_with(
755
- :"facet.query" => [
756
- 'average_rating_f:[3\.0 TO 4\.0]',
757
- 'average_rating_f:[4\.0 TO 5\.0]'
758
- ]
759
- )
760
- end
761
-
762
- it 'should request query facet with multiple conditions' do
763
- session.search Post do
764
- facet :foo do
765
- row :bar do
766
- with(:category_ids, 1)
767
- with(:blog_id, 2)
768
- end
769
- end
770
- end
771
- connection.should have_last_search_with(
772
- :"facet.query" => '(category_ids_im:1 AND blog_id_i:2)'
773
- )
774
- end
775
-
776
- it 'should request query facet with disjunction' do
777
- session.search Post do
778
- facet :foo do
779
- row :bar do
780
- any_of do
781
- with(:category_ids, 1)
782
- with(:blog_id, 2)
783
- end
784
- end
785
- end
786
- end
787
- connection.should have_last_search_with(
788
- :"facet.query" => '(category_ids_im:1 OR blog_id_i:2)'
789
- )
790
- end
791
-
792
- it 'should request query facet with internal dynamic field' do
793
- session.search Post do
794
- facet :test do
795
- row 'foo' do
796
- dynamic :custom_string do
797
- with :test, 'foo'
798
- end
799
- end
800
- end
801
- end
802
- connection.should have_last_search_with(
803
- :"facet.query" => 'custom_string\:test_s:foo'
804
- )
805
- end
806
-
807
- it 'should request query facet with external dynamic field' do
808
- session.search Post do
809
- dynamic :custom_string do
810
- facet :test do
811
- row 'foo' do
812
- with :test, 'foo'
813
- end
814
- end
815
- end
816
- end
817
- connection.should have_last_search_with(
818
- :"facet.query" => 'custom_string\:test_s:foo'
819
- )
820
- end
821
-
822
- it 'should not allow 0 arguments to facet method with block' do
823
- lambda do
824
- session.search Post do
825
- facet do
826
- end
827
- end
828
- end.should raise_error(ArgumentError)
829
- end
830
-
831
- it 'should not allow more than 1 argument to facet method with block' do
832
- lambda do
833
- session.search Post do
834
- facet :foo, :bar do
835
- end
836
- end
837
- end.should raise_error(ArgumentError)
838
- end
839
- end
840
-
841
- it 'builds query facets when passed :only argument to field facet declaration' do
842
- session.search Post do
843
- facet :category_ids, :only => [1, 3]
844
- end
845
- connection.should have_last_search_with(
846
- :"facet.query" => ['category_ids_im:1', 'category_ids_im:3']
847
- )
848
- end
849
-
850
- it 'converts limited query facet values to the correct type' do
851
- session.search Post do
852
- facet :published_at, :only => [Time.utc(2009, 8, 28, 15, 33), Time.utc(2008,8, 28, 15, 33)]
853
- end
854
- connection.should have_last_search_with(
855
- :"facet.query" => [
856
- 'published_at_d:2009\-08\-28T15\:33\:00Z',
857
- 'published_at_d:2008\-08\-28T15\:33\:00Z'
858
- ]
859
- )
860
- end
861
-
862
- it 'should allow faceting by dynamic string field' do
863
- session.search Post do
864
- dynamic :custom_string do
865
- facet :test
866
- end
867
- end
868
- connection.should have_last_search_with(:"facet.field" => %w(custom_string:test_s))
869
- end
870
-
871
- it 'should properly escape namespaced type names' do
872
- session.search(Namespaced::Comment)
873
- connection.should have_last_search_with(:q => 'type:Namespaced\:\:Comment')
874
- end
875
-
876
- it 'should build search for multiple types' do
877
- session.search(Post, Namespaced::Comment)
878
- connection.should have_last_search_with(:q => 'type:(Post OR Namespaced\:\:Comment)')
879
- end
880
-
881
- it 'should allow search on fields common to all types with DSL' do
882
- time = Time.parse('1983-07-08 05:00:00 -0400')
883
- session.search Post, Namespaced::Comment do
884
- with :published_at, time
885
- end
886
- connection.should have_last_search_with(:fq => ['published_at_d:1983\-07\-08T09\:00\:00Z'])
887
- end
888
-
889
- it 'should allow search on fields common to all types with conditions' do
890
- time = Time.parse('1983-07-08 05:00:00 -0400')
891
- session.search Post, Namespaced::Comment, :conditions => { :published_at => time }
892
- connection.should have_last_search_with(:fq => ['published_at_d:1983\-07\-08T09\:00\:00Z'])
893
- end
894
-
895
- it 'should allow search on dynamic fields common to all types' do
896
- session.search Post, Namespaced::Comment do
897
- dynamic :custom_string do
898
- with(:test, 'test')
899
- end
900
- end
901
- connection.should have_last_search_with(:fq => ['custom_string\\:test_s:test'])
902
- end
903
-
904
- it 'should combine all text fields' do
905
- session.search Post, Namespaced::Comment do
906
- keywords 'keywords'
907
- end
908
- connection.searches.last[:qf].split(' ').sort.should ==
909
- %w(author_name_text backwards_title_text body_text title_text)
910
- end
911
-
912
- it 'should allow specification of a text field that only exists in one type' do
913
- session.search Post, Namespaced::Comment do
914
- keywords 'keywords', :fields => :author_name
915
- end
916
- connection.searches.last[:qf].should == 'author_name_text'
917
- end
918
-
919
- it 'should raise Sunspot::UnrecognizedFieldError if search scoped to field not common to all types' do
920
- lambda do
921
- session.search Post, Namespaced::Comment do
922
- with :blog_id, 1
923
- end
924
- end.should raise_error(Sunspot::UnrecognizedFieldError)
925
- end
926
-
927
- it 'should raise Sunspot::UnrecognizedFieldError if search scoped to field configured differently between types' do
928
- lambda do
929
- session.search Post, Namespaced::Comment do
930
- with :average_rating, 2.2 # this is a float in Post but an integer in Comment
931
- end
932
- end.should raise_error(Sunspot::UnrecognizedFieldError)
933
- end
934
-
935
- it 'should raise Sunspot::UnrecognizedFieldError if a text field that does not exist for any type is specified' do
936
- lambda do
937
- session.search Post, Namespaced::Comment do
938
- keywords 'fulltext', :fields => :bogus
939
- end
940
- end.should raise_error(Sunspot::UnrecognizedFieldError)
941
- end
942
-
943
- it 'should ignore condition if field is not common to all types' do
944
- session.search Post, Namespaced::Comment, :conditions => { :blog_id => 1 }
945
- connection.should_not have_last_search_with(:fq)
946
- end
947
-
948
- it 'should allow building search using block argument rather than instance_eval' do
949
- @blog_id = 1
950
- session.search Post do |query|
951
- query.with(:blog_id, @blog_id)
952
- end
953
- connection.should have_last_search_with(:fq => ['blog_id_i:1'])
954
- end
955
-
956
- it 'should raise Sunspot::UnrecognizedFieldError for nonexistant fields in block scope' do
957
- lambda do
958
- session.search Post do
959
- with :bogus, 'Field'
960
- end
961
- end.should raise_error(Sunspot::UnrecognizedFieldError)
962
- end
963
-
964
- it 'should raise Sunspot::UnrecognizedFieldError for nonexistant fields in keywords' do
965
- lambda do
966
- session.search Post do
967
- keywords 'text', :fields => :bogus
968
- end
969
- end.should raise_error(Sunspot::UnrecognizedFieldError)
970
- end
971
-
972
- it 'should raise NoMethodError if bogus operator referenced' do
973
- lambda do
974
- session.search Post do
975
- with(:category_ids).resembling :bogus_condition
976
- end
977
- end.should raise_error(NoMethodError)
978
- end
979
-
980
- it 'should raise ArgumentError if no :page argument given to paginate' do
981
- lambda do
982
- session.search Post do
983
- paginate
984
- end
985
- end.should raise_error(ArgumentError)
986
- end
987
-
988
- it 'should raise ArgumentError if bogus argument given to paginate' do
989
- lambda do
990
- session.search Post do
991
- paginate :page => 4, :ugly => :puppy
992
- end
993
- end.should raise_error(ArgumentError)
994
- end
995
-
996
- it 'should raise ArgumentError if more than two arguments passed to scope method' do
997
- lambda do
998
- session.search Post do
999
- with(:category_ids, 4, 5)
1000
- end
1001
- end.should raise_error(ArgumentError)
1002
- end
1003
-
1004
- private
1005
-
1006
- def config
1007
- @config ||= Sunspot::Configuration.build
1008
- end
1009
-
1010
- def connection
1011
- @connection ||= Mock::Connection.new
1012
- end
1013
-
1014
- def session
1015
- @session ||= Sunspot::Session.new(config, connection)
1016
- end
1017
- end