gojee-sunspot 2.0.3 → 2.0.4

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 (178) hide show
  1. data/.gitignore +12 -0
  2. data/Gemfile +5 -0
  3. data/History.txt +252 -0
  4. data/LICENSE +18 -0
  5. data/Rakefile +13 -0
  6. data/TODO +13 -0
  7. data/lib/light_config.rb +40 -0
  8. data/lib/sunspot/adapters.rb +265 -0
  9. data/lib/sunspot/batcher.rb +62 -0
  10. data/lib/sunspot/class_set.rb +23 -0
  11. data/lib/sunspot/composite_setup.rb +202 -0
  12. data/lib/sunspot/configuration.rb +53 -0
  13. data/lib/sunspot/data_extractor.rb +50 -0
  14. data/lib/sunspot/dsl/adjustable.rb +47 -0
  15. data/lib/sunspot/dsl/field_group.rb +57 -0
  16. data/lib/sunspot/dsl/field_query.rb +327 -0
  17. data/lib/sunspot/dsl/fields.rb +103 -0
  18. data/lib/sunspot/dsl/fulltext.rb +243 -0
  19. data/lib/sunspot/dsl/function.rb +27 -0
  20. data/lib/sunspot/dsl/functional.rb +44 -0
  21. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  22. data/lib/sunspot/dsl/paginatable.rb +32 -0
  23. data/lib/sunspot/dsl/query_facet.rb +36 -0
  24. data/lib/sunspot/dsl/restriction.rb +25 -0
  25. data/lib/sunspot/dsl/restriction_with_near.rb +160 -0
  26. data/lib/sunspot/dsl/scope.rb +217 -0
  27. data/lib/sunspot/dsl/search.rb +30 -0
  28. data/lib/sunspot/dsl/standard_query.rb +123 -0
  29. data/lib/sunspot/dsl.rb +5 -0
  30. data/lib/sunspot/field.rb +193 -0
  31. data/lib/sunspot/field_factory.rb +129 -0
  32. data/lib/sunspot/indexer.rb +136 -0
  33. data/lib/sunspot/query/abstract_field_facet.rb +52 -0
  34. data/lib/sunspot/query/bbox.rb +15 -0
  35. data/lib/sunspot/query/boost_query.rb +24 -0
  36. data/lib/sunspot/query/common_query.rb +96 -0
  37. data/lib/sunspot/query/composite_fulltext.rb +36 -0
  38. data/lib/sunspot/query/connective.rb +206 -0
  39. data/lib/sunspot/query/date_field_facet.rb +14 -0
  40. data/lib/sunspot/query/dismax.rb +132 -0
  41. data/lib/sunspot/query/field_facet.rb +41 -0
  42. data/lib/sunspot/query/field_group.rb +36 -0
  43. data/lib/sunspot/query/filter.rb +38 -0
  44. data/lib/sunspot/query/function_query.rb +52 -0
  45. data/lib/sunspot/query/geo.rb +53 -0
  46. data/lib/sunspot/query/geofilt.rb +16 -0
  47. data/lib/sunspot/query/highlighting.rb +62 -0
  48. data/lib/sunspot/query/more_like_this.rb +61 -0
  49. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  50. data/lib/sunspot/query/pagination.rb +42 -0
  51. data/lib/sunspot/query/query_facet.rb +16 -0
  52. data/lib/sunspot/query/restriction.rb +262 -0
  53. data/lib/sunspot/query/scope.rb +9 -0
  54. data/lib/sunspot/query/sort.rb +109 -0
  55. data/lib/sunspot/query/sort_composite.rb +34 -0
  56. data/lib/sunspot/query/standard_query.rb +16 -0
  57. data/lib/sunspot/query/text_field_boost.rb +17 -0
  58. data/lib/sunspot/query.rb +11 -0
  59. data/lib/sunspot/schema.rb +151 -0
  60. data/lib/sunspot/search/abstract_search.rb +281 -0
  61. data/lib/sunspot/search/date_facet.rb +35 -0
  62. data/lib/sunspot/search/facet_row.rb +27 -0
  63. data/lib/sunspot/search/field_facet.rb +88 -0
  64. data/lib/sunspot/search/field_group.rb +32 -0
  65. data/lib/sunspot/search/group.rb +50 -0
  66. data/lib/sunspot/search/highlight.rb +38 -0
  67. data/lib/sunspot/search/hit.rb +150 -0
  68. data/lib/sunspot/search/hit_enumerable.rb +72 -0
  69. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  70. data/lib/sunspot/search/paginated_collection.rb +57 -0
  71. data/lib/sunspot/search/query_facet.rb +67 -0
  72. data/lib/sunspot/search/standard_search.rb +21 -0
  73. data/lib/sunspot/search.rb +9 -0
  74. data/lib/sunspot/session.rb +262 -0
  75. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  76. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  77. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  78. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  79. data/lib/sunspot/session_proxy/multicore_session_proxy.rb +67 -0
  80. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  81. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  82. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  83. data/lib/sunspot/session_proxy.rb +95 -0
  84. data/lib/sunspot/setup.rb +350 -0
  85. data/lib/sunspot/text_field_setup.rb +29 -0
  86. data/lib/sunspot/type.rb +393 -0
  87. data/lib/sunspot/util.rb +252 -0
  88. data/lib/sunspot/version.rb +3 -0
  89. data/lib/sunspot.rb +579 -0
  90. data/log/.gitignore +1 -0
  91. data/pkg/.gitignore +1 -0
  92. data/script/console +10 -0
  93. data/spec/api/adapters_spec.rb +33 -0
  94. data/spec/api/batcher_spec.rb +112 -0
  95. data/spec/api/binding_spec.rb +50 -0
  96. data/spec/api/class_set_spec.rb +24 -0
  97. data/spec/api/hit_enumerable_spec.rb +47 -0
  98. data/spec/api/indexer/attributes_spec.rb +149 -0
  99. data/spec/api/indexer/batch_spec.rb +72 -0
  100. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  101. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  102. data/spec/api/indexer/fulltext_spec.rb +43 -0
  103. data/spec/api/indexer/removal_spec.rb +53 -0
  104. data/spec/api/indexer/spec_helper.rb +1 -0
  105. data/spec/api/indexer_spec.rb +14 -0
  106. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  107. data/spec/api/query/connectives_examples.rb +189 -0
  108. data/spec/api/query/dsl_spec.rb +18 -0
  109. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  110. data/spec/api/query/faceting_examples.rb +397 -0
  111. data/spec/api/query/fulltext_examples.rb +313 -0
  112. data/spec/api/query/function_spec.rb +79 -0
  113. data/spec/api/query/geo_examples.rb +68 -0
  114. data/spec/api/query/group_spec.rb +32 -0
  115. data/spec/api/query/highlighting_examples.rb +245 -0
  116. data/spec/api/query/more_like_this_spec.rb +140 -0
  117. data/spec/api/query/ordering_pagination_examples.rb +116 -0
  118. data/spec/api/query/scope_examples.rb +275 -0
  119. data/spec/api/query/spatial_examples.rb +27 -0
  120. data/spec/api/query/spec_helper.rb +1 -0
  121. data/spec/api/query/standard_spec.rb +29 -0
  122. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  123. data/spec/api/query/types_spec.rb +20 -0
  124. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  125. data/spec/api/search/faceting_spec.rb +360 -0
  126. data/spec/api/search/highlighting_spec.rb +69 -0
  127. data/spec/api/search/hits_spec.rb +131 -0
  128. data/spec/api/search/paginated_collection_spec.rb +36 -0
  129. data/spec/api/search/results_spec.rb +72 -0
  130. data/spec/api/search/search_spec.rb +23 -0
  131. data/spec/api/search/spec_helper.rb +1 -0
  132. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  133. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  134. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  135. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  136. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  137. data/spec/api/session_proxy/spec_helper.rb +9 -0
  138. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
  139. data/spec/api/session_spec.rb +232 -0
  140. data/spec/api/spec_helper.rb +3 -0
  141. data/spec/api/sunspot_spec.rb +29 -0
  142. data/spec/ext.rb +11 -0
  143. data/spec/helpers/indexer_helper.rb +17 -0
  144. data/spec/helpers/integration_helper.rb +8 -0
  145. data/spec/helpers/mock_session_helper.rb +13 -0
  146. data/spec/helpers/query_helper.rb +26 -0
  147. data/spec/helpers/search_helper.rb +68 -0
  148. data/spec/integration/dynamic_fields_spec.rb +57 -0
  149. data/spec/integration/faceting_spec.rb +251 -0
  150. data/spec/integration/field_grouping_spec.rb +66 -0
  151. data/spec/integration/geospatial_spec.rb +85 -0
  152. data/spec/integration/highlighting_spec.rb +44 -0
  153. data/spec/integration/indexing_spec.rb +55 -0
  154. data/spec/integration/keyword_search_spec.rb +317 -0
  155. data/spec/integration/local_search_spec.rb +64 -0
  156. data/spec/integration/more_like_this_spec.rb +43 -0
  157. data/spec/integration/scoped_search_spec.rb +354 -0
  158. data/spec/integration/stored_fields_spec.rb +12 -0
  159. data/spec/integration/test_pagination.rb +43 -0
  160. data/spec/integration/unicode_spec.rb +15 -0
  161. data/spec/mocks/adapters.rb +32 -0
  162. data/spec/mocks/blog.rb +3 -0
  163. data/spec/mocks/comment.rb +21 -0
  164. data/spec/mocks/connection.rb +126 -0
  165. data/spec/mocks/mock_adapter.rb +30 -0
  166. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  167. data/spec/mocks/mock_record.rb +52 -0
  168. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  169. data/spec/mocks/photo.rb +11 -0
  170. data/spec/mocks/post.rb +86 -0
  171. data/spec/mocks/super_class.rb +2 -0
  172. data/spec/mocks/user.rb +13 -0
  173. data/spec/spec_helper.rb +40 -0
  174. data/sunspot.gemspec +42 -0
  175. data/tasks/rdoc.rake +27 -0
  176. data/tasks/schema.rake +19 -0
  177. data/tasks/todo.rake +4 -0
  178. metadata +261 -3
@@ -0,0 +1,354 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'scoped_search' do
4
+ def self.test_field_type(name, attribute, field, *values)
5
+ clazz =
6
+ if values.first.is_a?(Class)
7
+ values.shift
8
+ else
9
+ Post
10
+ end
11
+ raise(ArgumentError, 'Please supply five values') unless values.length == 5
12
+
13
+ context "with field of type #{name}" do
14
+ before :all do
15
+ Sunspot.remove_all
16
+ @objects = values.map do |value|
17
+ object = clazz.new(attribute => value)
18
+ Sunspot.index(object)
19
+ object
20
+ end
21
+ Sunspot.commit
22
+ end
23
+
24
+ it 'should filter by exact match' do
25
+ Sunspot.search(clazz) { with(field, values[2]) }.results.should == [@objects[2]]
26
+ end
27
+
28
+ it 'should reject by inexact match' do
29
+ results = Sunspot.search(clazz) { without(field, values[2]) }.results
30
+ [0, 1, 3, 4].each { |i| results.should include(@objects[i]) }
31
+ results.should_not include(@objects[2])
32
+ end
33
+
34
+ it 'should filter by less than' do
35
+ results = Sunspot.search(clazz) { with(field).less_than values[2] }.results
36
+ (0..2).each { |i| results.should include(@objects[i]) }
37
+ (3..4).each { |i| results.should_not include(@objects[i]) }
38
+ end
39
+
40
+ it 'should reject by less than' do
41
+ results = Sunspot.search(clazz) { without(field).less_than values[2] }.results
42
+ (0..2).each { |i| results.should_not include(@objects[i]) }
43
+ (3..4).each { |i| results.should include(@objects[i]) }
44
+ end
45
+
46
+ it 'should filter by greater than' do
47
+ results = Sunspot.search(clazz) { with(field).greater_than values[2] }.results
48
+ (2..4).each { |i| results.should include(@objects[i]) }
49
+ (0..1).each { |i| results.should_not include(@objects[i]) }
50
+ end
51
+
52
+ it 'should reject by greater than' do
53
+ results = Sunspot.search(clazz) { without(field).greater_than values[2] }.results
54
+ (2..4).each { |i| results.should_not include(@objects[i]) }
55
+ (0..1).each { |i| results.should include(@objects[i]) }
56
+ end
57
+
58
+ it 'should filter by between' do
59
+ results = Sunspot.search(clazz) { with(field).between(values[1]..values[3]) }.results
60
+ (1..3).each { |i| results.should include(@objects[i]) }
61
+ [0, 4].each { |i| results.should_not include(@objects[i]) }
62
+ end
63
+
64
+ it 'should reject by between' do
65
+ results = Sunspot.search(clazz) { without(field).between(values[1]..values[3]) }.results
66
+ (1..3).each { |i| results.should_not include(@objects[i]) }
67
+ [0, 4].each { |i| results.should include(@objects[i]) }
68
+ end
69
+
70
+ it 'should filter by any of' do
71
+ results = Sunspot.search(clazz) { with(field).any_of(values.values_at(1, 3)) }.results
72
+ [1, 3].each { |i| results.should include(@objects[i]) }
73
+ [0, 2, 4].each { |i| results.should_not include(@objects[i]) }
74
+ end
75
+
76
+ it 'should reject by any of' do
77
+ results = Sunspot.search(clazz) { without(field).any_of(values.values_at(1, 3)) }.results
78
+ [1, 3].each { |i| results.should_not include(@objects[i]) }
79
+ [0, 2, 4].each { |i| results.should include(@objects[i]) }
80
+ end
81
+
82
+ it 'should order by field ascending' do
83
+ results = Sunspot.search(clazz) { order_by field, :asc }.results
84
+ results.should == @objects
85
+ end
86
+
87
+ it 'should order by field descending' do
88
+ results = Sunspot.search(clazz) { order_by field, :desc }.results
89
+ results.should == @objects.reverse
90
+ end
91
+ end
92
+ end
93
+
94
+ test_field_type 'String', :title, :title, 'apple pie', 'banana split', 'cherry tart', 'date pastry', 'eggplant a la mode'
95
+ test_field_type 'Integer', :blog_id, :blog_id, -2, 0, 3, 12, 20
96
+ test_field_type 'Long', :hash, :hash, Namespaced::Comment, 2**29, 2**30, 2**31, 2**32, 2**33
97
+ test_field_type 'Float', :ratings_average, :average_rating, -2.5, 0.0, 3.2, 3.5, 16.0
98
+ test_field_type 'Double', :average_rating, :average_rating, Namespaced::Comment, -2.5, 0.0, 3.2, 3.5, 16.0
99
+ test_field_type 'Time', :published_at, :published_at, *(['1970-01-01 00:00:00 UTC', '1983-07-08 04:00:00 UTC', '1983-07-08 02:00:00 -0500',
100
+ '2005-11-05 10:00:00 UTC', Time.now.to_s].map { |t| Time.parse(t) })
101
+ test_field_type 'Trie Integer', :size, :size, Photo, -2, 0, 3, 12, 20
102
+ test_field_type 'Trie Float', :average_rating, :average_rating, Photo, -2.5, 0.0, 3.2, 3.5, 16.0
103
+ test_field_type 'Trie Time', :created_at, :created_at, Photo, *(['1970-01-01 00:00:00 UTC', '1983-07-08 04:00:00 UTC', '1983-07-08 02:00:00 -0500',
104
+ '2005-11-05 10:00:00 UTC', Time.now.to_s].map { |t| Time.parse(t) })
105
+
106
+ describe 'Boolean field type' do
107
+ before :all do
108
+ Sunspot.remove_all
109
+ @posts = [Post.new(:featured => true), Post.new(:featured => false), Post.new]
110
+ Sunspot.index!(@posts)
111
+ end
112
+
113
+ it 'should filter by exact match for true' do
114
+ Sunspot.search(Post) { with(:featured, true) }.results.should == [@posts[0]]
115
+ end
116
+
117
+ it 'should filter for exact match for false' do
118
+ Sunspot.search(Post) { with(:featured, false) }.results.should == [@posts[1]]
119
+ end
120
+ end
121
+
122
+ describe 'reserved words' do
123
+ %w(AND OR NOT TO).each do |word|
124
+ it "should successfully search for #{word.inspect}" do
125
+ Sunspot.index!(post = Post.new(:title => word))
126
+ Sunspot.search(Post) { with(:title, word) }.results.should == [post]
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'passing nil value to equal' do
132
+ before :all do
133
+ Sunspot.remove_all
134
+ @posts = [Post.new(:title => 'apple'), Post.new]
135
+ Sunspot.index!(@posts)
136
+ end
137
+
138
+ it 'should filter results without value for field' do
139
+ Sunspot.search(Post) { with(:title, nil) }.results.should == [@posts[1]]
140
+ end
141
+
142
+ it 'should exclude results without value for field' do
143
+ Sunspot.search(Post) { without(:title, nil) }.results.should == [@posts[0]]
144
+ end
145
+ end
146
+
147
+ describe 'prefix searching' do
148
+ before :each do
149
+ Sunspot.remove_all
150
+ @posts = ['test', 'test post', 'some test', 'bogus'].map do |title|
151
+ Post.new(:title => title)
152
+ end
153
+ Sunspot.index!(@posts)
154
+ end
155
+
156
+ it 'should return results whose prefix matches' do
157
+ Sunspot.search(Post) { with(:title).starting_with('test') }.results.should == @posts[0..1]
158
+ end
159
+ end
160
+
161
+ describe 'inclusion by identity' do
162
+ before do
163
+ @posts = (1..5).map do |i|
164
+ post = Post.new
165
+ Sunspot.index(post)
166
+ post
167
+ end
168
+ Sunspot.commit
169
+ end
170
+
171
+ it 'should only return included object' do
172
+ included_post = @posts.shift
173
+ Sunspot.search(Post) { with(included_post) }.results.should include(included_post)
174
+ end
175
+
176
+ it 'should not return objects not included' do
177
+ included_post = @posts.shift
178
+ for excluded_post in @posts
179
+ Sunspot.search(Post) { with(included_post) }.results.should_not include(excluded_post)
180
+ end
181
+ end
182
+
183
+ it 'should return included objects' do
184
+ included_posts = [@posts.shift, @posts.shift]
185
+ for included_post in included_posts
186
+ Sunspot.search(Post) { with(included_posts) }.results.should include(included_post)
187
+ end
188
+ end
189
+ end
190
+
191
+ describe 'exclusion by identity' do
192
+ before do
193
+ @posts = (1..5).map do |i|
194
+ post = Post.new
195
+ Sunspot.index(post)
196
+ post
197
+ end
198
+ Sunspot.commit
199
+ end
200
+
201
+ it 'should not return excluded object' do
202
+ excluded_post = @posts.shift
203
+ Sunspot.search(Post) { without(excluded_post) }.results.should_not include(excluded_post)
204
+ end
205
+
206
+ it 'should return objects not excluded' do
207
+ excluded_post = @posts.shift
208
+ for included_post in @posts
209
+ Sunspot.search(Post) { without(excluded_post) }.results.should include(included_post)
210
+ end
211
+ end
212
+
213
+ it 'should not return excluded objects' do
214
+ excluded_posts = [@posts.shift, @posts.shift]
215
+ for excluded_post in excluded_posts
216
+ Sunspot.search(Post) { without(excluded_posts) }.results.should_not include(excluded_post)
217
+ end
218
+ end
219
+ end
220
+
221
+ describe 'connectives' do
222
+ before :each do
223
+ Sunspot.remove_all
224
+ end
225
+
226
+ it 'should return results that match any restriction in a disjunction' do
227
+ posts = (1..3).map { |i| Post.new(:blog_id => i)}
228
+ Sunspot.index!(posts)
229
+ Sunspot.search(Post) do
230
+ any_of do
231
+ with(:blog_id, 1)
232
+ with(:blog_id, 2)
233
+ end
234
+ end.results.should == posts[0..1]
235
+ end
236
+
237
+ it 'should return results that match a nested conjunction in a disjunction' do
238
+ posts = [
239
+ Post.new(:title => 'No', :blog_id => 1),
240
+ Post.new(:title => 'Yes', :blog_id => 2),
241
+ Post.new(:title => 'Yes', :blog_id => 3),
242
+ Post.new(:title => 'No', :blog_id => 2)
243
+ ]
244
+ Sunspot.index!(posts)
245
+ Sunspot.search(Post) do
246
+ any_of do
247
+ with(:blog_id, 1)
248
+ all_of do
249
+ with(:blog_id, 2)
250
+ with(:title, 'Yes')
251
+ end
252
+ end
253
+ end.results.should == posts[0..1]
254
+ end
255
+
256
+ it 'should return results that match a conjunction with a negated restriction' do
257
+ posts = [
258
+ Post.new(:title => 'No', :blog_id => 1),
259
+ Post.new(:title => 'Yes', :blog_id => 2),
260
+ Post.new(:title => 'No', :blog_id => 2)
261
+ ]
262
+ Sunspot.index!(posts)
263
+ search = Sunspot.search(Post) do
264
+ any_of do
265
+ with(:blog_id, 1)
266
+ without(:title, 'No')
267
+ end
268
+ end
269
+ search.results.should == posts[0..1]
270
+ end
271
+
272
+ it 'should return results that match a conjunction with a disjunction with a conjunction with a negated restriction' do
273
+ posts = [
274
+ Post.new(:title => 'Yes', :ratings_average => 2.0),
275
+ Post.new(:blog_id => 1, :category_ids => [4], :ratings_average => 2.0),
276
+ Post.new(:blog_id => 1),
277
+ Post.new(:blog_id => 2),
278
+ Post.new(:blog_id => 1, :ratings_average => 2.0)
279
+ ]
280
+ Sunspot.index!(posts)
281
+ search = Sunspot.search(Post) do
282
+ any_of do
283
+ with(:title, 'Yes')
284
+ all_of do
285
+ with(:blog_id, 1)
286
+ any_of do
287
+ with(:category_ids, 4)
288
+ without(:average_rating, 2.0)
289
+ end
290
+ end
291
+ end
292
+ end
293
+ search.results.should == posts[0..2]
294
+ end
295
+
296
+ it 'should return results that match a disjunction with a negated restriction and a nested disjunction in a conjunction with a negated restriction' do
297
+ posts = [
298
+ Post.new,
299
+ Post.new(:title => 'Yes', :blog_id => 1, :category_ids => [4], :ratings_average => 2.0),
300
+ Post.new(:title => 'Yes', :blog_id => 1),
301
+ Post.new(:title => 'Yes'),
302
+ Post.new(:title => 'Yes', :category_ids => [4], :ratings_average => 2.0),
303
+ Post.new(:title => 'Yes', :blog_id => 1, :ratings_average => 2.0)
304
+ ]
305
+ Sunspot.index!(posts)
306
+ search = Sunspot.search(Post) do
307
+ any_of do
308
+ without(:title, 'Yes')
309
+ all_of do
310
+ with(:blog_id, 1)
311
+ any_of do
312
+ with(:category_ids, 4)
313
+ without(:average_rating, 2.0)
314
+ end
315
+ end
316
+ end
317
+ end
318
+ search.results.should == posts[0..2]
319
+ end
320
+ end
321
+
322
+ describe 'multiple column ordering' do
323
+ before do
324
+ Sunspot.remove_all
325
+ @posts = [
326
+ Post.new(:ratings_average => 2, :title => 'banana'),
327
+ Post.new(:ratings_average => 2, :title => 'eggplant'),
328
+ Post.new(:ratings_average => 1, :title => 'apple')
329
+ ].each { |post| Sunspot.index(post) }
330
+ Sunspot.commit
331
+ end
332
+
333
+ it 'should order with precedence given' do
334
+ search = Sunspot.search(Post) do
335
+ order_by :average_rating, :desc
336
+ order_by :sort_title, :asc
337
+ end
338
+ search.results.should == @posts
339
+ end
340
+ end
341
+
342
+ describe 'ordering by random' do
343
+ it 'should order randomly (run this test again if it fails)' do
344
+ Sunspot.remove_all
345
+ Sunspot.index!(Array.new(100) { Post.new })
346
+ result_sets = Array.new(2) do
347
+ Sunspot.search(Post) { order_by_random }.results.map do |result|
348
+ result.id
349
+ end
350
+ end
351
+ result_sets[0].should_not == result_sets[1]
352
+ end
353
+ end
354
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'stored fields' do
4
+ before :all do
5
+ Sunspot.remove_all
6
+ Sunspot.index!(Post.new(:title => 'A Title'))
7
+ end
8
+
9
+ it 'should return stored fields' do
10
+ Sunspot.search(Post).hits.first.stored(:title).should == 'A Title'
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'pagination' do
4
+ before :all do
5
+ Sunspot.remove_all
6
+ @posts = (0..19).map do |i|
7
+ Post.new(:blog_id => i)
8
+ end
9
+ Sunspot.index!(*@posts)
10
+ end
11
+
12
+ it 'should return all by default' do
13
+ results = Sunspot.search(Post) { order_by :blog_id }.results
14
+ results.should == @posts
15
+ end
16
+
17
+ it 'should return first page of 10' do
18
+ results = Sunspot.search(Post) do
19
+ order_by :blog_id
20
+ paginate :page => 1, :per_page => 10
21
+ end.results
22
+ results.should == @posts[0,10]
23
+ end
24
+
25
+ it 'should return second page of 10' do
26
+ results = Sunspot.search(Post) do
27
+ order_by :blog_id
28
+ paginate :page => 2, :per_page => 10
29
+ end.results
30
+ results.should == @posts[10,10]
31
+ end
32
+
33
+ it 'should return pages with offsets' do
34
+ results = Sunspot.search(Post) do
35
+ order_by :blog_id
36
+ paginate :page => 2, :per_page => 5, :offset => 3
37
+ end.results
38
+
39
+ # page 1 is 3, 4, 5, 6, 7
40
+ # page 2 is 8, 9, 10, 11, 12
41
+ results.should == @posts[8,5]
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
3
+
4
+ describe "unicode characters" do
5
+ before :each do
6
+ Sunspot.remove_all
7
+
8
+ @post = Post.new(:title => "Híghgrøøvé")
9
+ Sunspot.index!(@post)
10
+ end
11
+
12
+ it "correctly retrieves the string as UTF-8" do
13
+ Sunspot.search(Post).hits.first.stored(:title).should == "Híghgrøøvé"
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ class AbstractModel
2
+ end
3
+
4
+ class Model < AbstractModel
5
+ end
6
+
7
+ class AbstractModelInstanceAdapter < Sunspot::Adapters::InstanceAdapter
8
+ end
9
+
10
+ class AbstractModelDataAccessor < Sunspot::Adapters::DataAccessor
11
+ end
12
+
13
+ Sunspot::Adapters::InstanceAdapter.register(AbstractModelInstanceAdapter, AbstractModel)
14
+ Sunspot::Adapters::DataAccessor.register(AbstractModelDataAccessor, AbstractModel)
15
+
16
+
17
+ module MixInModel
18
+ end
19
+
20
+ class MixModel
21
+ include MixInModel
22
+ end
23
+
24
+ class MixInModelInstanceAdapter < Sunspot::Adapters::InstanceAdapter
25
+ end
26
+
27
+ class MixInModelDataAccessor < Sunspot::Adapters::DataAccessor
28
+ end
29
+
30
+ Sunspot::Adapters::InstanceAdapter.register(MixInModelInstanceAdapter, MixInModel)
31
+ Sunspot::Adapters::DataAccessor.register(MixInModelDataAccessor, MixInModel)
32
+
@@ -0,0 +1,3 @@
1
+ class Blog < MockRecord
2
+ attr_accessor :name
3
+ end
@@ -0,0 +1,21 @@
1
+ module Namespaced
2
+ class Comment < MockRecord
3
+ attr_reader :id
4
+ attr_accessor :author_name, :published_at, :body, :average_rating, :boost,
5
+ :hash
6
+
7
+ def custom_float
8
+ @custom_float ||= {}
9
+ end
10
+ end
11
+ end
12
+
13
+ Sunspot.setup(Namespaced::Comment) do
14
+ text :body, :author_name
15
+ string :author_name
16
+ time :published_at, :trie => true
17
+ long :hash
18
+ double :average_rating
19
+ dynamic_float :custom_float, :multiple => true
20
+ boost :boost
21
+ end
@@ -0,0 +1,126 @@
1
+ module Mock
2
+ class ConnectionFactory
3
+ def connect(opts)
4
+ if @instance
5
+ raise('Factory can only create an instance once!')
6
+ else
7
+ @instance = Connection.new(opts)
8
+ end
9
+ end
10
+
11
+ def new(url = nil)
12
+ if @instance
13
+ raise('Factory can only create an instance once!')
14
+ else
15
+ @instance = Connection.new(url)
16
+ end
17
+ end
18
+
19
+ def instance
20
+ @instance ||= Connection.new
21
+ end
22
+ end
23
+
24
+ class Connection
25
+ attr_reader :adds, :commits, :optims, :searches, :message, :opts, :deletes_by_query
26
+ attr_accessor :response
27
+ attr_writer :expected_handler
28
+ undef_method :select # annoyingly defined on Object
29
+
30
+ def initialize(opts = {})
31
+ @opts = opts
32
+ @message = OpenStruct.new
33
+ @adds, @deletes, @deletes_by_query, @commits, @optims, @searches = Array.new(6) { [] }
34
+ @expected_handler = :select
35
+ end
36
+
37
+ def add(documents)
38
+ @adds << Array(documents)
39
+ end
40
+
41
+ def delete_by_id(ids)
42
+ @deletes << Array(ids)
43
+ end
44
+
45
+ def delete_by_query(query)
46
+ @deletes_by_query << query
47
+ end
48
+
49
+ def commit
50
+ @commits << Time.now
51
+ end
52
+
53
+ def optimize
54
+ @optims << Time.now
55
+ end
56
+
57
+ def post(path, params)
58
+ unless path == "#{@expected_handler}"
59
+ raise ArgumentError, "Expected request to #{@expected_handler} request handler"
60
+ end
61
+ @searches << @last_search = params[:data]
62
+ @response || {}
63
+ end
64
+
65
+ def method_missing(method, *args, &block)
66
+ get("#{method}", *args)
67
+ end
68
+
69
+ def has_add_with?(*documents)
70
+ @adds.any? do |add|
71
+ documents.all? do |document|
72
+ add.any? do |added|
73
+ if document.is_a?(Hash)
74
+ document.all? do |field, value|
75
+ added.fields_by_name(field).map do |field|
76
+ field.value.to_s
77
+ end == Array(value).map { |v| v.to_s }
78
+ end
79
+ else
80
+ !added.fields_by_name(document).empty?
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def has_delete?(*ids)
88
+ @deletes.any? do |delete|
89
+ delete & ids == ids
90
+ end
91
+ end
92
+
93
+ def has_delete_by_query?(query)
94
+ @deletes_by_query.include?(query)
95
+ end
96
+
97
+ def has_last_search_with?(params)
98
+ with?(@last_search, params) if @last_search
99
+ end
100
+
101
+ def has_last_search_including?(key, *values)
102
+ return unless @last_search
103
+ if @last_search.has_key?(key)
104
+ if @last_search[key].is_a?(Array)
105
+ (@last_search[key] & values).length == values.length
106
+ elsif values.length == 1
107
+ @last_search[key] == values.first
108
+ end
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def with?(request, params)
115
+ if params.respond_to?(:all?)
116
+ params.all? do |key, value|
117
+ if request.has_key?(key)
118
+ request[key] == value
119
+ end
120
+ end
121
+ else
122
+ request.has_key?(params)
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), 'post')
2
+
3
+ module MockAdapter
4
+ class InstanceAdapter < Sunspot::Adapters::InstanceAdapter
5
+ def id
6
+ @instance.id
7
+ end
8
+ end
9
+
10
+ class DataAccessor < Sunspot::Adapters::DataAccessor
11
+ def load(id)
12
+ @clazz.get(id.to_i)
13
+ end
14
+
15
+ def load_all(ids)
16
+ all = @clazz.get_all(ids.map { |id| id.to_i })
17
+ if @custom_title
18
+ all.each { |item| item.title = @custom_title }
19
+ end
20
+ all
21
+ end
22
+
23
+ def custom_title=(custom_title)
24
+ @custom_title = custom_title
25
+ end
26
+ end
27
+ end
28
+
29
+ Sunspot::Adapters::DataAccessor.register(MockAdapter::DataAccessor, MockRecord)
30
+ Sunspot::Adapters::InstanceAdapter.register(MockAdapter::InstanceAdapter, MockRecord)
@@ -0,0 +1,24 @@
1
+ class MockClassShardingSessionProxy < Sunspot::SessionProxy::ClassShardingSessionProxy
2
+ attr_reader :post_session, :photo_session
3
+
4
+ def initialize(search_session)
5
+ super
6
+ @post_session, @photo_session = Sunspot::Session.new, Sunspot::Session.new
7
+ @post_session.config.solr.url = 'http://posts.solr.local/solr'
8
+ @photo_session.config.solr.url = 'http://photos.solr.local/solr'
9
+ @sessions = {
10
+ Post => @post_session,
11
+ Photo => @photo_session
12
+ }
13
+ end
14
+
15
+ def session_for_class(clazz)
16
+ @sessions[clazz]
17
+ end
18
+
19
+ def all_sessions
20
+ @sessions.values.sort do |lsession, rsession|
21
+ lsession.config.solr.url <=> rsession.config.solr.url
22
+ end
23
+ end
24
+ end