sunspot 2.0.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Appraisals +7 -0
  5. data/Gemfile +0 -2
  6. data/History.txt +10 -0
  7. data/lib/sunspot.rb +55 -17
  8. data/lib/sunspot/adapters.rb +68 -18
  9. data/lib/sunspot/batcher.rb +1 -1
  10. data/lib/sunspot/configuration.rb +4 -2
  11. data/lib/sunspot/data_extractor.rb +36 -6
  12. data/lib/sunspot/dsl.rb +4 -3
  13. data/lib/sunspot/dsl/adjustable.rb +2 -2
  14. data/lib/sunspot/dsl/field_query.rb +69 -16
  15. data/lib/sunspot/dsl/field_stats.rb +25 -0
  16. data/lib/sunspot/dsl/fields.rb +28 -8
  17. data/lib/sunspot/dsl/fulltext.rb +9 -1
  18. data/lib/sunspot/dsl/group.rb +118 -0
  19. data/lib/sunspot/dsl/paginatable.rb +4 -1
  20. data/lib/sunspot/dsl/scope.rb +19 -10
  21. data/lib/sunspot/dsl/search.rb +1 -1
  22. data/lib/sunspot/dsl/spellcheckable.rb +14 -0
  23. data/lib/sunspot/dsl/standard_query.rb +63 -35
  24. data/lib/sunspot/field.rb +76 -4
  25. data/lib/sunspot/field_factory.rb +60 -11
  26. data/lib/sunspot/indexer.rb +70 -18
  27. data/lib/sunspot/query.rb +5 -4
  28. data/lib/sunspot/query/abstract_field_facet.rb +0 -2
  29. data/lib/sunspot/query/abstract_fulltext.rb +76 -0
  30. data/lib/sunspot/query/abstract_json_field_facet.rb +70 -0
  31. data/lib/sunspot/query/bbox.rb +5 -1
  32. data/lib/sunspot/query/common_query.rb +31 -6
  33. data/lib/sunspot/query/composite_fulltext.rb +58 -8
  34. data/lib/sunspot/query/date_field_json_facet.rb +25 -0
  35. data/lib/sunspot/query/dismax.rb +25 -71
  36. data/lib/sunspot/query/field_json_facet.rb +19 -0
  37. data/lib/sunspot/query/field_list.rb +15 -0
  38. data/lib/sunspot/query/field_stats.rb +61 -0
  39. data/lib/sunspot/query/function_query.rb +1 -2
  40. data/lib/sunspot/query/geo.rb +1 -1
  41. data/lib/sunspot/query/geofilt.rb +8 -3
  42. data/lib/sunspot/query/group.rb +46 -0
  43. data/lib/sunspot/query/group_query.rb +17 -0
  44. data/lib/sunspot/query/join.rb +88 -0
  45. data/lib/sunspot/query/more_like_this.rb +1 -1
  46. data/lib/sunspot/query/pagination.rb +12 -4
  47. data/lib/sunspot/query/range_json_facet.rb +28 -0
  48. data/lib/sunspot/query/restriction.rb +99 -13
  49. data/lib/sunspot/query/sort.rb +41 -0
  50. data/lib/sunspot/query/sort_composite.rb +7 -0
  51. data/lib/sunspot/query/spellcheck.rb +19 -0
  52. data/lib/sunspot/query/standard_query.rb +24 -2
  53. data/lib/sunspot/query/text_field_boost.rb +1 -3
  54. data/lib/sunspot/schema.rb +12 -3
  55. data/lib/sunspot/search.rb +4 -2
  56. data/lib/sunspot/search/abstract_search.rb +93 -43
  57. data/lib/sunspot/search/cursor_paginated_collection.rb +32 -0
  58. data/lib/sunspot/search/field_facet.rb +4 -4
  59. data/lib/sunspot/search/field_json_facet.rb +33 -0
  60. data/lib/sunspot/search/field_stats.rb +21 -0
  61. data/lib/sunspot/search/hit.rb +6 -1
  62. data/lib/sunspot/search/hit_enumerable.rb +4 -1
  63. data/lib/sunspot/search/json_facet_row.rb +40 -0
  64. data/lib/sunspot/search/json_facet_stats.rb +23 -0
  65. data/lib/sunspot/search/paginated_collection.rb +1 -0
  66. data/lib/sunspot/search/query_group.rb +74 -0
  67. data/lib/sunspot/search/standard_search.rb +70 -3
  68. data/lib/sunspot/search/stats_facet.rb +25 -0
  69. data/lib/sunspot/search/stats_json_row.rb +82 -0
  70. data/lib/sunspot/search/stats_row.rb +68 -0
  71. data/lib/sunspot/session.rb +62 -37
  72. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +6 -4
  73. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +16 -8
  74. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +2 -2
  75. data/lib/sunspot/session_proxy/retry_5xx_session_proxy.rb +1 -1
  76. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +4 -2
  77. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +1 -1
  78. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +6 -4
  79. data/lib/sunspot/setup.rb +42 -0
  80. data/lib/sunspot/type.rb +20 -0
  81. data/lib/sunspot/util.rb +78 -14
  82. data/lib/sunspot/version.rb +1 -1
  83. data/spec/api/adapters_spec.rb +40 -15
  84. data/spec/api/batcher_spec.rb +15 -15
  85. data/spec/api/binding_spec.rb +3 -3
  86. data/spec/api/class_set_spec.rb +6 -6
  87. data/spec/api/data_extractor_spec.rb +39 -0
  88. data/spec/api/hit_enumerable_spec.rb +32 -9
  89. data/spec/api/indexer/attributes_spec.rb +35 -30
  90. data/spec/api/indexer/batch_spec.rb +8 -7
  91. data/spec/api/indexer/dynamic_fields_spec.rb +8 -8
  92. data/spec/api/indexer/fixed_fields_spec.rb +16 -11
  93. data/spec/api/indexer/fulltext_spec.rb +8 -8
  94. data/spec/api/indexer/removal_spec.rb +24 -14
  95. data/spec/api/indexer_spec.rb +2 -2
  96. data/spec/api/query/advanced_manipulation_examples.rb +3 -3
  97. data/spec/api/query/connectives_examples.rb +26 -14
  98. data/spec/api/query/dsl_spec.rb +24 -6
  99. data/spec/api/query/dynamic_fields_examples.rb +18 -18
  100. data/spec/api/query/faceting_examples.rb +80 -61
  101. data/spec/api/query/fulltext_examples.rb +194 -40
  102. data/spec/api/query/function_spec.rb +116 -13
  103. data/spec/api/query/geo_examples.rb +8 -12
  104. data/spec/api/query/group_spec.rb +27 -5
  105. data/spec/api/query/highlighting_examples.rb +26 -26
  106. data/spec/api/query/join_spec.rb +19 -0
  107. data/spec/api/query/more_like_this_spec.rb +40 -27
  108. data/spec/api/query/ordering_pagination_examples.rb +37 -23
  109. data/spec/api/query/scope_examples.rb +39 -39
  110. data/spec/api/query/spatial_examples.rb +3 -3
  111. data/spec/api/query/spellcheck_examples.rb +20 -0
  112. data/spec/api/query/standard_spec.rb +3 -1
  113. data/spec/api/query/stats_examples.rb +66 -0
  114. data/spec/api/query/text_field_scoping_examples.rb +5 -5
  115. data/spec/api/query/types_spec.rb +4 -4
  116. data/spec/api/search/cursor_paginated_collection_spec.rb +35 -0
  117. data/spec/api/search/dynamic_fields_spec.rb +4 -4
  118. data/spec/api/search/faceting_spec.rb +55 -52
  119. data/spec/api/search/highlighting_spec.rb +7 -7
  120. data/spec/api/search/hits_spec.rb +43 -29
  121. data/spec/api/search/paginated_collection_spec.rb +19 -18
  122. data/spec/api/search/results_spec.rb +13 -13
  123. data/spec/api/search/search_spec.rb +3 -3
  124. data/spec/api/search/stats_spec.rb +94 -0
  125. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +23 -16
  126. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +16 -4
  127. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +10 -6
  128. data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +11 -11
  129. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +15 -14
  130. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +3 -3
  131. data/spec/api/session_proxy/spec_helper.rb +1 -1
  132. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +40 -26
  133. data/spec/api/session_spec.rb +78 -38
  134. data/spec/api/sunspot_spec.rb +7 -4
  135. data/spec/helpers/integration_helper.rb +11 -1
  136. data/spec/helpers/query_helper.rb +1 -1
  137. data/spec/helpers/search_helper.rb +30 -0
  138. data/spec/integration/atomic_updates_spec.rb +58 -0
  139. data/spec/integration/dynamic_fields_spec.rb +31 -20
  140. data/spec/integration/faceting_spec.rb +252 -39
  141. data/spec/integration/field_grouping_spec.rb +47 -15
  142. data/spec/integration/field_lists_spec.rb +57 -0
  143. data/spec/integration/geospatial_spec.rb +34 -8
  144. data/spec/integration/highlighting_spec.rb +8 -8
  145. data/spec/integration/indexing_spec.rb +7 -6
  146. data/spec/integration/join_spec.rb +45 -0
  147. data/spec/integration/keyword_search_spec.rb +68 -38
  148. data/spec/integration/local_search_spec.rb +4 -4
  149. data/spec/integration/more_like_this_spec.rb +7 -7
  150. data/spec/integration/scoped_search_spec.rb +193 -74
  151. data/spec/integration/spellcheck_spec.rb +119 -0
  152. data/spec/integration/stats_spec.rb +88 -0
  153. data/spec/integration/stored_fields_spec.rb +1 -1
  154. data/spec/integration/test_pagination.rb +4 -4
  155. data/spec/integration/unicode_spec.rb +1 -1
  156. data/spec/mocks/adapters.rb +36 -0
  157. data/spec/mocks/connection.rb +5 -3
  158. data/spec/mocks/photo.rb +32 -1
  159. data/spec/mocks/post.rb +18 -3
  160. data/spec/spec_helper.rb +13 -8
  161. data/sunspot.gemspec +6 -4
  162. data/tasks/rdoc.rake +22 -14
  163. metadata +101 -44
  164. data/lib/sunspot/dsl/field_group.rb +0 -57
  165. data/lib/sunspot/query/field_group.rb +0 -37
@@ -3,28 +3,28 @@ shared_examples_for 'fulltext query' do
3
3
  search do
4
4
  keywords 'keyword search'
5
5
  end
6
- connection.should have_last_search_with(:q => 'keyword search')
6
+ expect(connection).to have_last_search_with(:q => 'keyword search')
7
7
  end
8
8
 
9
9
  it 'ignores keywords if empty' do
10
10
  search do
11
11
  keywords ''
12
12
  end
13
- connection.should_not have_last_search_with(:defType => 'dismax')
13
+ expect(connection).not_to have_last_search_with(:defType => 'edismax')
14
14
  end
15
15
 
16
16
  it 'ignores keywords if nil' do
17
17
  search do
18
18
  keywords nil
19
19
  end
20
- connection.should_not have_last_search_with(:defType => 'dismax')
20
+ expect(connection).not_to have_last_search_with(:defType => 'edismax')
21
21
  end
22
22
 
23
23
  it 'ignores keywords with only whitespace' do
24
24
  search do
25
25
  keywords " \t"
26
26
  end
27
- connection.should_not have_last_search_with(:defType => 'dismax')
27
+ expect(connection).not_to have_last_search_with(:defType => 'edismax')
28
28
  end
29
29
 
30
30
  it 'gracefully ignores keywords block if keywords ignored' do
@@ -37,14 +37,14 @@ shared_examples_for 'fulltext query' do
37
37
  search do
38
38
  keywords 'keyword search'
39
39
  end
40
- connection.should have_last_search_with(:defType => 'dismax')
40
+ expect(connection).to have_last_search_with(:defType => 'edismax')
41
41
  end
42
42
 
43
43
  it 'searches types in filter query if keywords used' do
44
44
  search do
45
45
  keywords 'keyword search'
46
46
  end
47
- connection.should have_last_search_with(:fq => ['type:Post'])
47
+ expect(connection).to have_last_search_with(:fq => ['type:Post'])
48
48
  end
49
49
 
50
50
  describe 'with multiple keyword components' do
@@ -56,20 +56,23 @@ shared_examples_for 'fulltext query' do
56
56
  end
57
57
 
58
58
  it 'puts specified keywords in subquery' do
59
- subqueries(:q).map { |subquery| subquery[:v] }.should ==
59
+ expect(subqueries(:q).map { |subquery| subquery[:v] }).to eq(
60
60
  ['first search', 'second search']
61
+ )
61
62
  end
62
63
 
63
64
  it 'puts specified dismax parameters in subquery' do
64
- subqueries(:q).first[:qf].should == 'title_text'
65
+ expect(subqueries(:q).first[:qf]).to eq('title_text')
65
66
  end
66
67
 
67
68
  it 'puts default dismax parameters in subquery' do
68
- subqueries(:q).last[:qf].split(' ').sort.should == %w(backwards_title_text body_textsv tags_textv title_text)
69
+ expect(subqueries(:q).last[:qf].split(' ').sort).to(
70
+ eq(%w(backwards_title_text body_textsv tags_textv text_array_text title_text))
71
+ )
69
72
  end
70
73
 
71
74
  it 'puts field list in main query' do
72
- connection.should have_last_search_with(:fl => '* score')
75
+ expect(connection).to have_last_search_with(:fl => '* score')
73
76
  end
74
77
  end
75
78
 
@@ -77,21 +80,25 @@ shared_examples_for 'fulltext query' do
77
80
  search = search do
78
81
  keywords 'keyword search'
79
82
  end
80
- connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_textsv tags_textv title_text)
83
+ expect(connection.searches.last[:qf].split(' ').sort).to(
84
+ eq(%w(backwards_title_text body_textsv tags_textv text_array_text title_text))
85
+ )
81
86
  end
82
87
 
83
88
  it 'searches both stored and unstored text fields' do
84
89
  search Post, Namespaced::Comment do
85
90
  keywords 'keyword search'
86
91
  end
87
- connection.searches.last[:qf].split(' ').sort.should == %w(author_name_text backwards_title_text body_text body_textsv tags_textv title_text)
92
+ expect(connection.searches.last[:qf].split(' ').sort).to(
93
+ eq(%w(author_name_text backwards_title_text body_text body_textsv tags_textv text_array_text title_text))
94
+ )
88
95
  end
89
96
 
90
97
  it 'searches only specified text fields when specified' do
91
98
  search do
92
99
  keywords 'keyword search', :fields => [:title, :body]
93
100
  end
94
- connection.searches.last[:qf].split(' ').sort.should == %w(body_textsv title_text)
101
+ expect(connection.searches.last[:qf].split(' ').sort).to eq(%w(body_textsv title_text))
95
102
  end
96
103
 
97
104
  it 'excludes text fields when instructed' do
@@ -100,7 +107,7 @@ shared_examples_for 'fulltext query' do
100
107
  exclude_fields :backwards_title, :body_mlt
101
108
  end
102
109
  end
103
- connection.searches.last[:qf].split(' ').sort.should == %w(body_textsv tags_textv title_text)
110
+ expect(connection.searches.last[:qf].split(' ').sort).to eq(%w(body_textsv tags_textv text_array_text title_text))
104
111
  end
105
112
 
106
113
  it 'assigns boost to fields when specified' do
@@ -109,7 +116,7 @@ shared_examples_for 'fulltext query' do
109
116
  fields :title => 2.0, :body => 0.75
110
117
  end
111
118
  end
112
- connection.searches.last[:qf].split(' ').sort.should == %w(body_textsv^0.75 title_text^2.0)
119
+ expect(connection.searches.last[:qf].split(' ').sort).to eq(%w(body_textsv^0.75 title_text^2.0))
113
120
  end
114
121
 
115
122
  it 'allows assignment of boosted and unboosted fields' do
@@ -124,19 +131,19 @@ shared_examples_for 'fulltext query' do
124
131
  search Post, Namespaced::Comment do
125
132
  keywords 'keyword search', :fields => [:body]
126
133
  end
127
- connection.searches.last[:qf].split(' ').sort.should == %w(body_text body_textsv)
134
+ expect(connection.searches.last[:qf].split(' ').sort).to eq(%w(body_text body_textsv))
128
135
  end
129
136
 
130
137
  it 'requests score when keywords used' do
131
138
  search do
132
139
  keywords 'keyword search'
133
140
  end
134
- connection.should have_last_search_with(:fl => '* score')
141
+ expect(connection).to have_last_search_with(:fl => '* score')
135
142
  end
136
143
 
137
144
  it 'does not request score when keywords not used' do
138
145
  search Post
139
- connection.should_not have_last_search_with(:fl)
146
+ expect(connection).not_to have_last_search_with(:fl)
140
147
  end
141
148
 
142
149
  it 'sets phrase fields' do
@@ -145,7 +152,7 @@ shared_examples_for 'fulltext query' do
145
152
  phrase_fields :title => 2.0
146
153
  end
147
154
  end
148
- connection.should have_last_search_with(:pf => 'title_text^2.0')
155
+ expect(connection).to have_last_search_with(:pf => 'title_text^2.0')
149
156
  end
150
157
 
151
158
  it 'sets phrase fields with boost' do
@@ -154,7 +161,7 @@ shared_examples_for 'fulltext query' do
154
161
  phrase_fields :title => 1.5
155
162
  end
156
163
  end
157
- connection.should have_last_search_with(:pf => 'title_text^1.5')
164
+ expect(connection).to have_last_search_with(:pf => 'title_text^1.5')
158
165
  end
159
166
 
160
167
  it 'sets phrase slop from DSL' do
@@ -163,7 +170,7 @@ shared_examples_for 'fulltext query' do
163
170
  phrase_slop 2
164
171
  end
165
172
  end
166
- connection.should have_last_search_with(:ps => 2)
173
+ expect(connection).to have_last_search_with(:ps => 2)
167
174
  end
168
175
 
169
176
  it 'sets boost for certain fields without restricting fields' do
@@ -172,7 +179,9 @@ shared_examples_for 'fulltext query' do
172
179
  boost_fields :title => 1.5
173
180
  end
174
181
  end
175
- connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_textsv tags_textv title_text^1.5)
182
+ expect(connection.searches.last[:qf].split(' ').sort).to(
183
+ eq(%w(backwards_title_text body_textsv tags_textv text_array_text title_text^1.5))
184
+ )
176
185
  end
177
186
 
178
187
  it 'ignores boost fields that do not apply' do
@@ -181,21 +190,24 @@ shared_examples_for 'fulltext query' do
181
190
  boost_fields :bogus => 1.2, :title => 1.5
182
191
  end
183
192
  end
184
- connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_textsv tags_textv title_text^1.5)
193
+ expect(connection.searches.last[:qf].split(' ').sort).to(
194
+ eq(%w(backwards_title_text body_textsv tags_textv text_array_text title_text^1.5))
195
+ )
185
196
  end
186
197
 
187
198
  it 'sets default boost with default fields' do
188
199
  search Photo do
189
200
  keywords 'great pizza'
190
201
  end
191
- connection.should have_last_search_with(:qf => 'caption_text^1.5')
202
+ # Hashes in 1.8 aren't ordered
203
+ expect(connection.searches.last[:qf].split(" ").sort.join(" ")).to eq 'caption_text^1.5 description_text'
192
204
  end
193
205
 
194
206
  it 'sets default boost with fields specified in options' do
195
207
  search Photo do
196
208
  keywords 'great pizza', :fields => [:caption]
197
209
  end
198
- connection.should have_last_search_with(:qf => 'caption_text^1.5')
210
+ expect(connection).to have_last_search_with(:qf => 'caption_text^1.5')
199
211
  end
200
212
 
201
213
  it 'sets default boost with fields specified in DSL' do
@@ -204,7 +216,7 @@ shared_examples_for 'fulltext query' do
204
216
  fields :caption
205
217
  end
206
218
  end
207
- connection.should have_last_search_with(:qf => 'caption_text^1.5')
219
+ expect(connection).to have_last_search_with(:qf => 'caption_text^1.5')
208
220
  end
209
221
 
210
222
  it 'overrides default boost when specified in DSL' do
@@ -213,7 +225,7 @@ shared_examples_for 'fulltext query' do
213
225
  fields :caption => 2.0
214
226
  end
215
227
  end
216
- connection.should have_last_search_with(:qf => 'caption_text^2.0')
228
+ expect(connection).to have_last_search_with(:qf => 'caption_text^2.0')
217
229
  end
218
230
 
219
231
  it 'creates boost query' do
@@ -224,7 +236,7 @@ shared_examples_for 'fulltext query' do
224
236
  end
225
237
  end
226
238
  end
227
- connection.should have_last_search_with(:bq => ['average_rating_ft:{2\.0 TO *}^2.0'])
239
+ expect(connection).to have_last_search_with(:bq => ['average_rating_ft:{2\.0 TO *}^2.0'])
228
240
  end
229
241
 
230
242
  it 'creates multiple boost queries' do
@@ -238,7 +250,7 @@ shared_examples_for 'fulltext query' do
238
250
  end
239
251
  end
240
252
  end
241
- connection.should have_last_search_with(
253
+ expect(connection).to have_last_search_with(
242
254
  :bq => [
243
255
  'average_rating_ft:{2\.0 TO *}^2.0',
244
256
  'featured_bs:true^1.5'
@@ -250,64 +262,206 @@ shared_examples_for 'fulltext query' do
250
262
  search do
251
263
  keywords 'great pizza', :minimum_match => 2
252
264
  end
253
- connection.should have_last_search_with(:mm => 2)
265
+ expect(connection).to have_last_search_with(:mm => 2)
254
266
  end
255
267
 
256
268
  it 'sends minimum match parameter from DSL' do
257
269
  search do
258
270
  keywords('great pizza') { minimum_match(2) }
259
271
  end
260
- connection.should have_last_search_with(:mm => 2)
272
+ expect(connection).to have_last_search_with(:mm => 2)
261
273
  end
262
274
 
263
275
  it 'sends tiebreaker parameter from options' do
264
276
  search do
265
277
  keywords 'great pizza', :tie => 0.1
266
278
  end
267
- connection.should have_last_search_with(:tie => 0.1)
279
+ expect(connection).to have_last_search_with(:tie => 0.1)
268
280
  end
269
281
 
270
282
  it 'sends tiebreaker parameter from DSL' do
271
283
  search do
272
284
  keywords('great pizza') { tie(0.1) }
273
285
  end
274
- connection.should have_last_search_with(:tie => 0.1)
286
+ expect(connection).to have_last_search_with(:tie => 0.1)
275
287
  end
276
288
 
277
289
  it 'sends query phrase slop from options' do
278
290
  search do
279
291
  keywords 'great pizza', :query_phrase_slop => 2
280
292
  end
281
- connection.should have_last_search_with(:qs => 2)
293
+ expect(connection).to have_last_search_with(:qs => 2)
282
294
  end
283
295
 
284
296
  it 'sends query phrase slop from DSL' do
285
297
  search do
286
298
  keywords('great pizza') { query_phrase_slop(2) }
287
299
  end
288
- connection.should have_last_search_with(:qs => 2)
300
+ expect(connection).to have_last_search_with(:qs => 2)
289
301
  end
290
302
 
291
303
  it 'allows specification of a text field that only exists in one type' do
292
304
  search Post, Namespaced::Comment do
293
305
  keywords 'keywords', :fields => :author_name
294
306
  end
295
- connection.searches.last[:qf].should == 'author_name_text'
307
+ expect(connection.searches.last[:qf]).to eq('author_name_text')
296
308
  end
297
309
 
298
310
  it 'raises Sunspot::UnrecognizedFieldError for nonexistant fields in keywords' do
299
- lambda do
311
+ expect do
300
312
  search do
301
313
  keywords :text, :fields => :bogus
302
314
  end
303
- end.should raise_error(Sunspot::UnrecognizedFieldError)
315
+ end.to raise_error(Sunspot::UnrecognizedFieldError)
304
316
  end
305
317
 
306
318
  it 'raises Sunspot::UnrecognizedFieldError if a text field that does not exist for any type is specified' do
307
- lambda do
319
+ expect do
308
320
  search Post, Namespaced::Comment do
309
321
  keywords 'fulltext', :fields => :bogus
310
322
  end
311
- end.should raise_error(Sunspot::UnrecognizedFieldError)
323
+ end.to raise_error(Sunspot::UnrecognizedFieldError)
324
+ end
325
+
326
+ describe 'connective examples' do
327
+ it 'creates a disjunction between two subqueries' do
328
+ search Post do
329
+ any do
330
+ fulltext 'keywords1', :fields => :title
331
+ fulltext 'keyword2', :fields => :body
332
+ end
333
+ end
334
+
335
+ expect(connection.searches.last[:q]).to eq "(_query_:\"{!edismax qf='title_text'}keywords1\" OR _query_:\"{!edismax qf='body_textsv'}keyword2\")"
336
+ end
337
+
338
+ it 'creates a conjunction inside of a disjunction' do
339
+ search do
340
+ any do
341
+ fulltext 'keywords1', :fields => :body
342
+
343
+ all do
344
+ fulltext 'keyword2', :fields => :body
345
+ fulltext 'keyword3', :fields => :body
346
+ end
347
+ end
348
+ end
349
+
350
+ expect(connection.searches.last[:q]).to eq "(_query_:\"{!edismax qf='body_textsv'}keywords1\" OR (_query_:\"{!edismax qf='body_textsv'}keyword2\" AND _query_:\"{!edismax qf='body_textsv'}keyword3\"))"
351
+ end
352
+
353
+ it 'does nothing special if #all/#any called from the top level or called multiple times' do
354
+ search Post do
355
+ all do
356
+ fulltext 'keywords1', :fields => :title
357
+ fulltext 'keyword2', :fields => :body
358
+ end
359
+ end
360
+
361
+ expect(connection.searches.last[:q]).to eq "(_query_:\"{!edismax qf='title_text'}keywords1\" AND _query_:\"{!edismax qf='body_textsv'}keyword2\")"
362
+ end
363
+
364
+ it 'does nothing special if #all/#any are mixed and called multiple times' do
365
+ search Post do
366
+ all do
367
+ any do
368
+ all do
369
+ fulltext 'keywords1', :fields => :title
370
+ fulltext 'keyword2', :fields => :body
371
+ end
372
+ end
373
+ end
374
+ end
375
+
376
+ expect(connection.searches.last[:q]).to eq "(_query_:\"{!edismax qf='title_text'}keywords1\" AND _query_:\"{!edismax qf='body_textsv'}keyword2\")"
377
+
378
+ search Post do
379
+ any do
380
+ all do
381
+ any do
382
+ fulltext 'keywords1', :fields => :title
383
+ fulltext 'keyword2', :fields => :body
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+ expect(connection.searches.last[:q]).to eq "(_query_:\"{!edismax qf='title_text'}keywords1\" OR _query_:\"{!edismax qf='body_textsv'}keyword2\")"
390
+ end
391
+
392
+ it "does not add empty parentheses" do
393
+ search Post do
394
+ any do
395
+ all do
396
+ end
397
+
398
+ any do
399
+ fulltext 'keywords1', :fields => :title
400
+ all do
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ expect(connection.searches.last[:q]).to eq "_query_:\"{!edismax qf='title_text'}keywords1\""
407
+ end
408
+ end
409
+
410
+ describe "joins" do
411
+ it "should search by join" do
412
+ srch = search PhotoContainer do
413
+ any do
414
+ fulltext 'keyword1', :fields => :caption
415
+ fulltext 'keyword2', :fields => :description
416
+ end
417
+ end
418
+
419
+ obj_id = find_ob_id(srch)
420
+ q_name = "qPhoto#{obj_id}"
421
+
422
+ expect(connection.searches.last[:q]).to eq "(_query_:\"{!join from=photo_container_id_i to=id_i v=$#{q_name}}\" OR _query_:\"{!edismax qf='description_text^1.2'}keyword2\")"
423
+ expect(connection.searches.last[q_name]).to eq "_query_:\"{!field f=type}Photo\"+_query_:\"{!edismax qf='caption_text'}keyword1\""
424
+ end
425
+
426
+ it "should be able to resolve name conflicts with the :prefix option" do
427
+ srch = search PhotoContainer do
428
+ any do
429
+ fulltext 'keyword1', :fields => :description
430
+ fulltext 'keyword2', :fields => :photo_description
431
+ end
432
+ end
433
+
434
+ obj_id = find_ob_id(srch)
435
+ q_name = "qPhoto#{obj_id}"
436
+
437
+ expect(connection.searches.last[:q]).to eq "(_query_:\"{!edismax qf='description_text^1.2'}keyword1\" OR _query_:\"{!join from=photo_container_id_i to=id_i v=$#{q_name}}\")"
438
+ expect(connection.searches.last[q_name]).to eq "_query_:\"{!field f=type}Photo\"+_query_:\"{!edismax qf='description_text'}keyword2\""
439
+ end
440
+
441
+ it "should recognize fields when adding from DSL, e.g. when calling boost_fields" do
442
+ srch = search PhotoContainer do
443
+ any do
444
+ fulltext 'keyword1', :fields => [:photo_description, :description] do
445
+ boost_fields(:photo_description => 1.3, :description => 1.5)
446
+ end
447
+ end
448
+ end
449
+
450
+ obj_id = find_ob_id(srch)
451
+ q_name = "qPhoto#{obj_id}"
452
+
453
+ expect(connection.searches.last[:q]).to eq "(_query_:\"{!edismax qf='description_text^1.5'}keyword1\" OR _query_:\"{!join from=photo_container_id_i to=id_i v=$#{q_name}}\")"
454
+ expect(connection.searches.last[q_name]).to eq "_query_:\"{!field f=type}Photo\"+_query_:\"{!edismax qf='description_text^1.3'}keyword1\""
455
+ end
456
+
457
+ private
458
+
459
+ def find_ob_id(search)
460
+ search.query.
461
+ instance_variable_get("@components").find { |c| c.is_a?(Sunspot::Query::Conjunction) }.
462
+ instance_variable_get("@components").find { |c| c.is_a?(Sunspot::Query::Disjunction) }.
463
+ instance_variable_get("@components").find { |c| c.is_a?(Sunspot::Query::Join) }.
464
+ object_id
465
+ end
312
466
  end
313
467
  end