sunspot 2.0.0 → 2.5.0

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 (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