acts_as_solr 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. data/.gitignore +8 -0
  2. data/CHANGE_LOG +233 -0
  3. data/FORKED_CHANGES +3 -0
  4. data/LICENSE +19 -0
  5. data/README.markdown +94 -0
  6. data/README.rdoc +84 -0
  7. data/Rakefile +57 -0
  8. data/TESTING_THE_PLUGIN +25 -0
  9. data/VERSION +1 -0
  10. data/acts_as_solr.gemspec +237 -0
  11. data/config/solr.yml +15 -0
  12. data/config/solr_environment.rb +22 -0
  13. data/init.rb +21 -0
  14. data/install.rb +11 -0
  15. data/lib/acts_as_solr.rb +61 -0
  16. data/lib/acts_methods.rb +284 -0
  17. data/lib/class_methods.rb +239 -0
  18. data/lib/common_methods.rb +89 -0
  19. data/lib/deprecation.rb +61 -0
  20. data/lib/instance_methods.rb +181 -0
  21. data/lib/lazy_document.rb +18 -0
  22. data/lib/parser_methods.rb +230 -0
  23. data/lib/search_results.rb +69 -0
  24. data/lib/solr/connection.rb +191 -0
  25. data/lib/solr/document.rb +78 -0
  26. data/lib/solr/exception.rb +13 -0
  27. data/lib/solr/field.rb +39 -0
  28. data/lib/solr/importer/array_mapper.rb +26 -0
  29. data/lib/solr/importer/delimited_file_source.rb +38 -0
  30. data/lib/solr/importer/hpricot_mapper.rb +27 -0
  31. data/lib/solr/importer/mapper.rb +51 -0
  32. data/lib/solr/importer/solr_source.rb +43 -0
  33. data/lib/solr/importer/xpath_mapper.rb +35 -0
  34. data/lib/solr/importer.rb +19 -0
  35. data/lib/solr/indexer.rb +52 -0
  36. data/lib/solr/request/add_document.rb +63 -0
  37. data/lib/solr/request/base.rb +36 -0
  38. data/lib/solr/request/commit.rb +31 -0
  39. data/lib/solr/request/delete.rb +50 -0
  40. data/lib/solr/request/dismax.rb +46 -0
  41. data/lib/solr/request/index_info.rb +22 -0
  42. data/lib/solr/request/modify_document.rb +51 -0
  43. data/lib/solr/request/optimize.rb +21 -0
  44. data/lib/solr/request/ping.rb +36 -0
  45. data/lib/solr/request/select.rb +56 -0
  46. data/lib/solr/request/spellcheck.rb +30 -0
  47. data/lib/solr/request/standard.rb +402 -0
  48. data/lib/solr/request/update.rb +23 -0
  49. data/lib/solr/request.rb +26 -0
  50. data/lib/solr/response/add_document.rb +17 -0
  51. data/lib/solr/response/base.rb +42 -0
  52. data/lib/solr/response/commit.rb +17 -0
  53. data/lib/solr/response/delete.rb +13 -0
  54. data/lib/solr/response/dismax.rb +8 -0
  55. data/lib/solr/response/index_info.rb +26 -0
  56. data/lib/solr/response/modify_document.rb +17 -0
  57. data/lib/solr/response/optimize.rb +14 -0
  58. data/lib/solr/response/ping.rb +28 -0
  59. data/lib/solr/response/ruby.rb +42 -0
  60. data/lib/solr/response/select.rb +17 -0
  61. data/lib/solr/response/spellcheck.rb +20 -0
  62. data/lib/solr/response/standard.rb +64 -0
  63. data/lib/solr/response/xml.rb +42 -0
  64. data/lib/solr/response.rb +27 -0
  65. data/lib/solr/solrtasks.rb +27 -0
  66. data/lib/solr/util.rb +32 -0
  67. data/lib/solr/xml.rb +44 -0
  68. data/lib/solr.rb +21 -0
  69. data/lib/solr_fixtures.rb +13 -0
  70. data/lib/tasks/database.rake +18 -0
  71. data/lib/tasks/solr.rake +137 -0
  72. data/lib/tasks/test.rake +7 -0
  73. data/lib/will_paginate_support.rb +12 -0
  74. data/solr/CHANGES.txt +1207 -0
  75. data/solr/LICENSE.txt +712 -0
  76. data/solr/NOTICE.txt +90 -0
  77. data/solr/etc/jetty.xml +205 -0
  78. data/solr/etc/webdefault.xml +379 -0
  79. data/solr/lib/easymock.jar +0 -0
  80. data/solr/lib/jetty-6.1.3.jar +0 -0
  81. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  82. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  83. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  84. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  85. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  86. data/solr/lib/servlet-api-2.4.jar +0 -0
  87. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  88. data/solr/lib/xpp3-1.1.3.4.O.jar +0 -0
  89. data/solr/logs/.empty-dir-for-git +0 -0
  90. data/solr/solr/README.txt +52 -0
  91. data/solr/solr/bin/abc +176 -0
  92. data/solr/solr/bin/abo +176 -0
  93. data/solr/solr/bin/backup +108 -0
  94. data/solr/solr/bin/backupcleaner +142 -0
  95. data/solr/solr/bin/commit +128 -0
  96. data/solr/solr/bin/optimize +129 -0
  97. data/solr/solr/bin/readercycle +129 -0
  98. data/solr/solr/bin/rsyncd-disable +77 -0
  99. data/solr/solr/bin/rsyncd-enable +76 -0
  100. data/solr/solr/bin/rsyncd-start +145 -0
  101. data/solr/solr/bin/rsyncd-stop +105 -0
  102. data/solr/solr/bin/scripts-util +83 -0
  103. data/solr/solr/bin/snapcleaner +148 -0
  104. data/solr/solr/bin/snapinstaller +168 -0
  105. data/solr/solr/bin/snappuller +248 -0
  106. data/solr/solr/bin/snappuller-disable +77 -0
  107. data/solr/solr/bin/snappuller-enable +77 -0
  108. data/solr/solr/bin/snapshooter +109 -0
  109. data/solr/solr/conf/admin-extra.html +31 -0
  110. data/solr/solr/conf/protwords.txt +21 -0
  111. data/solr/solr/conf/schema.xml +126 -0
  112. data/solr/solr/conf/scripts.conf +24 -0
  113. data/solr/solr/conf/solrconfig.xml +458 -0
  114. data/solr/solr/conf/stopwords.txt +57 -0
  115. data/solr/solr/conf/synonyms.txt +31 -0
  116. data/solr/solr/conf/xslt/example.xsl +132 -0
  117. data/solr/solr/conf/xslt/example_atom.xsl +63 -0
  118. data/solr/solr/conf/xslt/example_rss.xsl +62 -0
  119. data/solr/start.jar +0 -0
  120. data/solr/tmp/.empty-dir-for-git +0 -0
  121. data/solr/webapps/solr.war +0 -0
  122. data/test/config/solr.yml +2 -0
  123. data/test/db/connections/mysql/connection.rb +10 -0
  124. data/test/db/connections/sqlite/connection.rb +8 -0
  125. data/test/db/migrate/001_create_books.rb +15 -0
  126. data/test/db/migrate/002_create_movies.rb +12 -0
  127. data/test/db/migrate/003_create_categories.rb +11 -0
  128. data/test/db/migrate/004_create_electronics.rb +16 -0
  129. data/test/db/migrate/005_create_authors.rb +12 -0
  130. data/test/db/migrate/006_create_postings.rb +9 -0
  131. data/test/db/migrate/007_create_posts.rb +13 -0
  132. data/test/db/migrate/008_create_gadgets.rb +11 -0
  133. data/test/fixtures/authors.yml +9 -0
  134. data/test/fixtures/books.yml +13 -0
  135. data/test/fixtures/categories.yml +7 -0
  136. data/test/fixtures/db_definitions/mysql.sql +41 -0
  137. data/test/fixtures/electronics.yml +49 -0
  138. data/test/fixtures/movies.yml +9 -0
  139. data/test/fixtures/postings.yml +10 -0
  140. data/test/functional/acts_as_solr_test.rb +413 -0
  141. data/test/functional/association_indexing_test.rb +37 -0
  142. data/test/functional/faceted_search_test.rb +163 -0
  143. data/test/functional/multi_solr_search_test.rb +51 -0
  144. data/test/models/author.rb +10 -0
  145. data/test/models/book.rb +10 -0
  146. data/test/models/category.rb +8 -0
  147. data/test/models/electronic.rb +21 -0
  148. data/test/models/gadget.rb +9 -0
  149. data/test/models/movie.rb +17 -0
  150. data/test/models/novel.rb +2 -0
  151. data/test/models/post.rb +3 -0
  152. data/test/models/posting.rb +11 -0
  153. data/test/test_helper.rb +51 -0
  154. data/test/unit/acts_methods_shoulda.rb +70 -0
  155. data/test/unit/class_methods_shoulda.rb +90 -0
  156. data/test/unit/common_methods_shoulda.rb +112 -0
  157. data/test/unit/instance_methods_shoulda.rb +326 -0
  158. data/test/unit/lazy_document_shoulda.rb +35 -0
  159. data/test/unit/parser_instance.rb +19 -0
  160. data/test/unit/parser_methods_shoulda.rb +279 -0
  161. data/test/unit/solr_instance.rb +46 -0
  162. data/test/unit/test_helper.rb +26 -0
  163. metadata +259 -0
@@ -0,0 +1,413 @@
1
+ # encoding: utf-8
2
+ require "#{File.dirname(File.expand_path(__FILE__))}/../test_helper"
3
+
4
+ class ActsAsSolrTest < Test::Unit::TestCase
5
+
6
+ fixtures :books, :movies, :electronics, :postings, :authors
7
+
8
+ # Inserting new data into Solr and making sure it's getting indexed
9
+ def test_insert_new_data
10
+ assert_equal 2, Book.count_by_solr('ruby OR splinter OR bob')
11
+ b = Book.create(:name => "Fuze in action", :author => "Bob Bobber", :category_id => 1)
12
+ assert b.valid?
13
+ assert_equal 3, Book.count_by_solr('ruby OR splinter OR bob')
14
+ end
15
+
16
+ # Check the type column stored in the index isn't stemmed by SOLR. If it is stemmed,
17
+ # then both Post and Posting will be stored as type:Post, so a query for Posts will
18
+ # return Postings and vice versa
19
+
20
+ def test_insert_new_data_doesnt_stem_type
21
+ assert_equal 0, Post.count_by_solr('aardvark')
22
+ p = Posting.new :name => 'aardvark', :description => "An interesting animal"
23
+ p.guid = '12AB'
24
+ p.save!
25
+ assert_equal 0, Post.count_by_solr('aardvark')
26
+ end
27
+
28
+ def test_type_determined_from_database_if_not_explicitly_set
29
+ assert Post.configuration[:solr_fields][:posted_at][:type] == :date
30
+ end
31
+
32
+ def test_search_includes_subclasses
33
+ Novel.create! :name => 'Wuthering Heights', :author => 'Emily Bronte'
34
+ Book.create! :name => 'Jane Eyre', :author => 'Charlotte Bronte'
35
+ assert_equal 1, Novel.find_by_solr('Bronte').total_hits
36
+ assert_equal 2, Book.find_by_solr('Bronte').total_hits
37
+ end
38
+
39
+ # Testing basic solr search:
40
+ # Model.find_by_solr 'term'
41
+ # Note that you're able to mix free-search with fields and boolean operators
42
+ def test_find_by_solr_ruby
43
+ ['ruby', 'dummy', 'name:ruby', 'name:dummy', 'name:ruby AND author:peter',
44
+ 'author:peter AND ruby', 'peter dummy'].each do |term|
45
+ records = Book.find_by_solr term
46
+ assert_equal 1, records.total
47
+ assert_equal "Peter McPeterson", records.docs.first.author
48
+ assert_equal "Ruby for Dummies", records.docs.first.name
49
+ assert_equal ({"id" => 2,
50
+ "category_id" => 2,
51
+ "name" => "Ruby for Dummies",
52
+ "author" => "Peter McPeterson", "published_on" => (Date.today - 2.years), "type" => nil}), records.docs.first.attributes
53
+ end
54
+ end
55
+
56
+ # Testing basic solr search:
57
+ # Model.find_by_solr 'term'
58
+ # Note that you're able to mix free-search with fields and boolean operators
59
+ def test_find_by_solr_splinter
60
+ ['splinter', 'name:splinter', 'name:splinter AND author:clancy',
61
+ 'author:clancy AND splinter', 'cell tom'].each do |term|
62
+ records = Book.find_by_solr term
63
+ assert_equal 1, records.total
64
+ assert_equal "Splinter Cell", records.docs.first.name
65
+ assert_equal "Tom Clancy", records.docs.first.author
66
+ assert_equal ({"id" => 1, "category_id" => 1, "name" => "Splinter Cell",
67
+ "author" => "Tom Clancy", "published_on" => (Date.today - 1.year), "type" => nil}), records.docs.first.attributes
68
+ end
69
+ end
70
+
71
+ # Testing basic solr search:
72
+ # Model.find_by_solr 'term'
73
+ # Note that you're able to mix free-search with fields and boolean operators
74
+ def test_find_by_solr_ruby_or_splinter
75
+ ['ruby OR splinter', 'ruby OR author:tom', 'name:cell OR author:peter', 'dummy OR cell'].each do |term|
76
+ records = Book.find_by_solr term
77
+ assert_equal 2, records.total
78
+ end
79
+ end
80
+
81
+ # Testing search in indexed field methods:
82
+ #
83
+ # class Movie < ActiveRecord::Base
84
+ # acts_as_solr :fields => [:name, :description, :current_time]
85
+ #
86
+ # def current_time
87
+ # Time.now.to_s
88
+ # end
89
+ #
90
+ # end
91
+ #
92
+ # The method current_time above gets indexed as being part of the
93
+ # Movie model and it's available for search as well
94
+ def test_find_with_dynamic_fields
95
+ date = Time.now.strftime('%b %d %Y')
96
+ ["dynamite AND #{date}", "description:goofy AND #{date}", "goofy napoleon #{date}",
97
+ "goofiness #{date}"].each do |term|
98
+ records = Movie.find_by_solr term
99
+ assert_equal 1, records.total
100
+ assert_equal ({"id" => 1, "name" => "Napoleon Dynamite",
101
+ "description" => "Cool movie about a goofy guy"}), records.docs.first.attributes
102
+ end
103
+ end
104
+
105
+ # Testing basic solr search that returns just the ids instead of the objects:
106
+ # Model.find_id_by_solr 'term'
107
+ # Note that you're able to mix free-search with fields and boolean operators
108
+ def test_find_id_by_solr_ruby
109
+ ['ruby', 'dummy', 'name:ruby', 'name:dummy', 'name:ruby AND author:peter',
110
+ 'author:peter AND ruby'].each do |term|
111
+ records = Book.find_id_by_solr term
112
+ assert_equal 1, records.docs.size
113
+ assert_equal [2], records.docs
114
+ end
115
+ end
116
+
117
+ # Testing basic solr search that returns just the ids instead of the objects:
118
+ # Model.find_id_by_solr 'term'
119
+ # Note that you're able to mix free-search with fields and boolean operators
120
+ def test_find_id_by_solr_splinter
121
+ ['splinter', 'name:splinter', 'name:splinter AND author:clancy',
122
+ 'author:clancy AND splinter'].each do |term|
123
+ records = Book.find_id_by_solr term
124
+ assert_equal 1, records.docs.size
125
+ assert_equal [1], records.docs
126
+ end
127
+ end
128
+
129
+ # Testing basic solr search that returns just the ids instead of the objects:
130
+ # Model.find_id_by_solr 'term'
131
+ # Note that you're able to mix free-search with fields and boolean operators
132
+ def test_find_id_by_solr_ruby_or_splinter
133
+ ['ruby OR splinter', 'ruby OR author:tom', 'name:cell OR author:peter',
134
+ 'dummy OR cell'].each do |term|
135
+ records = Book.find_id_by_solr term
136
+ assert_equal 2, records.docs.size
137
+ assert_equal [1,2], records.docs
138
+ end
139
+ end
140
+
141
+ # Testing basic solr search that returns the total number of records found:
142
+ # Model.find_count_by_solr 'term'
143
+ # Note that you're able to mix free-search with fields and boolean operators
144
+ def test_count_by_solr
145
+ ['ruby', 'dummy', 'name:ruby', 'name:dummy', 'name:ruby AND author:peter',
146
+ 'author:peter AND ruby'].each do |term|
147
+ assert_equal 1, Book.count_by_solr(term), "there should only be 1 result for search: #{term}"
148
+ end
149
+ end
150
+
151
+ # Testing basic solr search that returns the total number of records found:
152
+ # Model.find_count_by_solr 'term'
153
+ # Note that you're able to mix free-search with fields and boolean operators
154
+ def test_count_by_solr_splinter
155
+ ['splinter', 'name:splinter', 'name:splinter AND author:clancy',
156
+ 'author:clancy AND splinter', 'author:clancy cell'].each do |term|
157
+ assert_equal 1, Book.count_by_solr(term)
158
+ end
159
+ end
160
+
161
+ # Testing basic solr search that returns the total number of records found:
162
+ # Model.find_count_by_solr 'term'
163
+ # Note that you're able to mix free-search with fields and boolean operators
164
+ def test_count_by_solr_ruby_or_splinter
165
+ ['ruby OR splinter', 'ruby OR author:tom', 'name:cell OR author:peter', 'dummy OR cell'].each do |term|
166
+ assert_equal 2, Book.count_by_solr(term)
167
+ end
168
+ end
169
+
170
+ # Testing basic solr search with additional options:
171
+ # Model.find_count_by_solr 'term', :limit => 10, :offset => 0
172
+ def test_find_with_options
173
+ [1,2].each do |count|
174
+ records = Book.find_by_solr 'ruby OR splinter', :limit => count
175
+ assert_equal count, records.docs.size
176
+ end
177
+ end
178
+
179
+ # Testing self.rebuild_solr_index
180
+ # - It makes sure the index is rebuilt after a data has been lost
181
+ def test_rebuild_solr_index
182
+ assert_equal 1, Book.count_by_solr('splinter')
183
+
184
+ Book.find(:first).solr_destroy
185
+ assert_equal 0, Book.count_by_solr('splinter')
186
+
187
+ Book.rebuild_solr_index
188
+ assert_equal 1, Book.count_by_solr('splinter')
189
+ end
190
+
191
+ # Testing instance methods:
192
+ # - solr_save
193
+ # - solr_destroy
194
+ def test_solr_save_and_solr_destroy
195
+ assert_equal 1, Book.count_by_solr('splinter')
196
+
197
+ Book.find(:first).solr_destroy
198
+ assert_equal 0, Book.count_by_solr('splinter')
199
+
200
+ Book.find(:first).solr_save
201
+ assert_equal 1, Book.count_by_solr('splinter')
202
+ end
203
+
204
+ # Testing the order of results
205
+ def test_find_returns_records_in_order
206
+ records = Book.find_by_solr 'ruby^5 OR splinter'
207
+ # we boosted ruby so ruby should come first
208
+
209
+ assert_equal 2, records.total
210
+ assert_equal 'Ruby for Dummies', records.docs.first.name
211
+ assert_equal 'Splinter Cell', records.docs.last.name
212
+ end
213
+
214
+ # Testing solr search with optional :order argument
215
+ def _test_with_order_option
216
+ records = Movie.find_by_solr 'office^5 OR goofiness'
217
+ assert_equal 'Hypnotized dude loves fishing but not working', records.docs.first.description
218
+ assert_equal 'Cool movie about a goofy guy', records.docs.last.description
219
+
220
+ records = Movie.find_by_solr 'office^5 OR goofiness', :order => 'description asc'
221
+ assert_equal 'Cool movie about a goofy guy', records.docs.first.description
222
+ assert_equal 'Hypnotized dude loves fishing but not working', records.docs.last.description
223
+ end
224
+
225
+ # Testing search with omitted :field_types should
226
+ # return the same result set as if when we use it
227
+ def test_omit_field_types_in_search
228
+ records = Electronic.find_by_solr "price:[200 TO 599.99]"
229
+ assert_match(/599/, records.docs.first.price)
230
+ assert_match(/319/, records.docs.last.price)
231
+
232
+ records = Electronic.find_by_solr "price:[200 TO 599.99]", :order => 'price asc'
233
+ assert_match(/319/, records.docs.first.price)
234
+ assert_match(/599/, records.docs.last.price)
235
+
236
+ end
237
+
238
+ # Test to make sure the result returned when no matches
239
+ # are found has the same structure when there are results
240
+ def test_returns_no_matches
241
+ records = Book.find_by_solr 'rubyist'
242
+ assert_equal [], records.docs
243
+ assert_equal 0, records.total
244
+
245
+ records = Book.find_id_by_solr 'rubyist'
246
+ assert_equal [], records.docs
247
+ assert_equal 0, records.total
248
+
249
+ records = Book.find_by_solr 'rubyist', :facets => {}
250
+ assert_equal [], records.docs
251
+ assert_equal 0, records.total
252
+ assert_equal({"facet_fields"=>[]}, records.facets)
253
+ end
254
+
255
+
256
+ # Testing the :exclude_fields option when set in the
257
+ # model to make sure it doesn't get indexed
258
+ def test_exclude_fields_option
259
+ records = Electronic.find_by_solr 'audiobooks OR latency'
260
+ assert records.docs.empty?
261
+ assert_equal 0, records.total
262
+
263
+ assert_nothing_raised{
264
+ records = Electronic.find_by_solr 'features:audiobooks'
265
+ assert records.docs.empty?
266
+ assert_equal 0, records.total
267
+ }
268
+ end
269
+
270
+ # Testing the :auto_commit option set to false in the model
271
+ # should not send the commit command to Solr
272
+ def test_auto_commit_turned_off
273
+ assert_equal 0, Author.count_by_solr('raymond chandler')
274
+
275
+ original_count = Author.count
276
+ Author.create(:name => 'Raymond Chandler', :biography => 'Writes noirish detective stories')
277
+
278
+ assert_equal original_count + 1, Author.count
279
+ assert_equal 0, Author.count_by_solr('raymond chandler')
280
+ end
281
+
282
+ # Testing models that use a different key as the primary key
283
+ def test_search_on_model_with_string_id_field
284
+ records = Posting.find_by_solr 'first^5 OR second'
285
+ assert_equal 2, records.total
286
+ assert_equal 'ABC-123', records.docs.first.guid
287
+ assert_equal 'DEF-456', records.docs.last.guid
288
+ end
289
+
290
+ # Making sure the result set is ordered correctly even on
291
+ # models that use a different key as the primary key
292
+ def test_records_in_order_on_model_with_string_id_field
293
+ records = Posting.find_by_solr 'first OR second^5'
294
+ assert_equal 2, records.total
295
+ assert_equal 'DEF-456', records.docs.first.guid
296
+ assert_equal 'ABC-123', records.docs.last.guid
297
+ end
298
+
299
+ # Making sure the records are added when passing a batch size
300
+ # to rebuild_solr_index
301
+ def test_using_rebuild_solr_index_with_batch
302
+ assert_equal 2, Movie.count_by_solr('office OR napoleon')
303
+ Movie.find(:all).each(&:solr_destroy)
304
+ assert_equal 0, Movie.count_by_solr('office OR napoleon')
305
+
306
+ Movie.rebuild_solr_index 100
307
+ assert_equal 2, Movie.count_by_solr('office OR napoleon')
308
+ end
309
+
310
+ # Making sure find_by_solr with scores actually return the scores
311
+ # for each individual record
312
+ def test_find_by_solr_with_score
313
+ books = Book.find_by_solr 'ruby^10 OR splinter', :scores => true
314
+ assert_equal 2, books.total
315
+ assert_equal 0.41763234, books.max_score
316
+
317
+ books.records.each { |book| assert_not_nil book.solr_score }
318
+ assert_equal 0.41763234, books.docs.first.solr_score
319
+ assert_equal 0.14354616, books.docs.last.solr_score
320
+ end
321
+
322
+ # Making sure nothing breaks when html entities are inside
323
+ # the content to be indexed; and on the search as well.
324
+ def test_index_and_search_with_html_entities
325
+ description = "
326
+ inverted exclamation mark &iexcl; &#161;
327
+ ¤ currency &curren; &#164;
328
+ ¢ cent &cent; &#162;
329
+ £ pound &pound; &#163;
330
+ ¥ yen &yen; &#165;
331
+ ¦ broken vertical bar &brvbar; &#166;
332
+ § section &sect; &#167;
333
+ ¨ spacing diaeresis &uml; &#168;
334
+ © copyright &copy; &#169;
335
+ ª feminine ordinal indicator &ordf; &#170;
336
+ « angle quotation mark (left) &laquo; &#171;
337
+ ¬ negation &not; &#172;
338
+ ­ soft hyphen &shy; &#173;
339
+ ® registered trademark &reg; &#174;
340
+ ™ trademark &trade; &#8482;
341
+ ¯ spacing macron &macr; &#175;
342
+ ° degree &deg; &#176;
343
+ ± plus-or-minus &plusmn; &#177;
344
+ ² superscript 2 &sup2; &#178;
345
+ ³ superscript 3 &sup3; &#179;
346
+ ´ spacing acute &acute; &#180;
347
+ µ micro &micro; &#181;
348
+ ¶ paragraph &para; &#182;
349
+ · middle dot &middot; &#183;
350
+ ¸ spacing cedilla &cedil; &#184;
351
+ ¹ superscript 1 &sup1; &#185;
352
+ º masculine ordinal indicator &ordm; &#186;
353
+ » angle quotation mark (right) &raquo; &#187;
354
+ ¼ fraction 1/4 &frac14; &#188;
355
+ ½ fraction 1/2 &frac12; &#189;
356
+ ¾ fraction 3/4 &frac34; &#190;
357
+ ¿ inverted question mark &iquest; &#191;
358
+ × multiplication &times; &#215;
359
+ ÷ division &divide; &#247
360
+ &hearts; &diams; &clubs; &spades;"
361
+
362
+ author = Author.create(:name => "Test in Action&trade; - Copyright &copy; Bob", :biography => description)
363
+ assert author.valid?
364
+ author.solr_commit
365
+
366
+ author = Author.find_by_solr 'trademark &copy &#190 &iexcl &#163'
367
+ assert_equal 1, author.total
368
+ end
369
+
370
+ def test_operator_search_option
371
+ assert_nothing_raised {
372
+ books = Movie.find_by_solr "office napoleon", :operator => :or
373
+ assert_equal 2, books.total
374
+
375
+ books = Movie.find_by_solr "office napoleon", :operator => :and
376
+ assert_equal 0, books.total
377
+ }
378
+
379
+ assert_raise RuntimeError do
380
+ Movie.find_by_solr "office napoleon", :operator => :bad
381
+ end
382
+ end
383
+
384
+ # Making sure find_by_solr with scores actually return the scores
385
+ # for each individual record and orders them accordingly
386
+ def test_find_by_solr_order_by_score
387
+ books = Book.find_by_solr 'ruby^10 OR splinter', {:scores => true, :order => 'score asc' }
388
+ assert (books.docs.collect(&:solr_score).compact.size == books.docs.size), "Each book should have a score"
389
+ assert_equal 0.41763234, books.docs.last.solr_score
390
+
391
+ books = Book.find_by_solr 'ruby^10 OR splinter', {:scores => true, :order => 'score desc' }
392
+ assert_equal 0.41763234, books.docs.first.solr_score
393
+ assert_equal 0.14354616, books.docs.last.solr_score
394
+ end
395
+
396
+ # Search based on fields with the :date format
397
+ def test_indexed_date_field_format
398
+ movies = Movie.find_by_solr 'time_on_xml:[NOW-1DAY TO NOW]'
399
+ assert_equal 2, movies.total
400
+ end
401
+
402
+ def test_query_time_is_returned
403
+ results = Book.find_by_solr('ruby')
404
+ assert_not_nil(results.query_time)
405
+ assert_equal(results.query_time.class,Fixnum)
406
+ end
407
+
408
+ def test_should_not_index_the_record_when_offline_proc_returns_true
409
+ Gadget.search_disabled = true
410
+ gadget = Gadget.create(:name => "flipvideo mino")
411
+ assert_equal 0, Gadget.find_id_by_solr('flipvideo').total
412
+ end
413
+ end
@@ -0,0 +1,37 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+
3
+ class AssociationIndexingTest < Test::Unit::TestCase
4
+
5
+ fixtures :categories, :books
6
+
7
+ # Testing the association indexing with has_many:
8
+ #
9
+ # class Category < ActiveRecord::Base
10
+ # has_many :books
11
+ # acts_as_solr :include => [:books]
12
+ # end
13
+ #
14
+ # Note that some of the search terms below are from the 'books'
15
+ # table, but get indexed as being a part of Category
16
+ def test_search_on_fields_in_has_many_association
17
+ ['thriller', 'novel', 'splinter', 'clancy', 'tom clancy thriller'].each do |term|
18
+ assert_equal 1, Category.count_by_solr(term), "expected one result: #{term}"
19
+ end
20
+ end
21
+
22
+ # Testing the association indexing with belongs_to:
23
+ #
24
+ # class Book < ActiveRecord::Base
25
+ # belongs_to :category
26
+ # acts_as_solr :include => [:category]
27
+ # end
28
+ #
29
+ # Note that some of the search terms below are from the 'categories'
30
+ # table, but get indexed as being a part of Book
31
+ def test_search_on_fields_in_belongs_to_association
32
+ ['splinter', 'clancy', 'tom clancy thriller', 'splinter novel'].each do |term|
33
+ assert_equal 1, Book.count_by_solr(term), "expected one result: #{term}"
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,163 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+
3
+ class FacetedSearchTest < Test::Unit::TestCase
4
+
5
+ fixtures :electronics
6
+
7
+ # The tests below are for faceted search, but make sure you setup
8
+ # the fields on your model you'd like to index as a facet field:
9
+ #
10
+ # class Electronic < ActiveRecord::Base
11
+ # acts_as_solr :facets => [:category, :manufacturer]
12
+ # end
13
+ #
14
+ # A basic faceted search using just one facet field
15
+ def test_faceted_search_basic
16
+ records = Electronic.find_by_solr "memory", :facets => {:fields =>[:category]}
17
+ assert_equal 4, records.docs.size
18
+ assert_match /Apple 60 GB Memory iPod/, records.docs.first.name
19
+ assert_equal({"category_facet" => {"Electronics" => 1,
20
+ "Memory" => 2,
21
+ "Hard Drive" => 1}},
22
+ records.facets['facet_fields'])
23
+ end
24
+
25
+ # Making sure the empty result returned what we expected
26
+ def test_faceted_search_no_matches
27
+ records = Electronic.find_by_solr "not found", :facets => { :fields => [:category]}
28
+ assert_equal [], records.docs
29
+ assert_equal [], records.facets['facet_fields']
30
+ end
31
+
32
+ # A basic faceted search using multiple facet fields
33
+ def test_faceted_search_multiple_fields
34
+ records = Electronic.find_by_solr "memory", :facets => {:fields =>[:category, :manufacturer]}
35
+ assert_equal 4, records.docs.size
36
+ assert_equal({"category_facet" => {"Electronics" => 1,
37
+ "Memory" => 2,
38
+ "Hard Drive" => 1},
39
+ "manufacturer_facet" => {"Dell, Inc" => 0,
40
+ "Samsung Electronics Co. Ltd." => 1,
41
+ "Corsair Microsystems Inc." => 1,
42
+ "A-DATA Technology Inc." => 1,
43
+ "Apple Computer Inc." => 1}}, records.facets['facet_fields'])
44
+ end
45
+
46
+ # A basic faceted search using facet queries to get counts.
47
+ # Here are the facets search query meaning:
48
+ # "price:[* TO 200]" - Price up to 200
49
+ # "price:[200 TO 500]" - Price from 200 to 500
50
+ # "price:[500 TO *]" - Price higher than 500
51
+ def test_facet_search_with_query
52
+ records = Electronic.find_by_solr "memory", :facets => {:query => ["price:[* TO 200.00]",
53
+ "price:[200.00 TO 500.00]",
54
+ "price:[500.00 TO *]"]}
55
+ assert_equal 4, records.docs.size
56
+ assert_equal({"facet_queries" => {"price_rf:[* TO 200.00]"=>2,
57
+ "price_rf:[200.00 TO 500.00]"=>1,
58
+ "price_rf:[500.00 TO *]"=>1},
59
+ "facet_fields" => {}, "facet_dates" => {}}, records.facets)
60
+ end
61
+
62
+ # Faceted search specifying the query and fields
63
+ def test_facet_search_with_query_and_field
64
+ records = Electronic.find_by_solr "memory", :facets => {:query => ["price:[* TO 200.00]",
65
+ "price:[200.00 TO 500.00]",
66
+ "price:[500.00 TO *]"],
67
+ :fields => [:category, :manufacturer]}
68
+
69
+ q = records.facets["facet_queries"]
70
+ assert_equal 2, q["price_rf:[* TO 200.00]"]
71
+ assert_equal 1, q["price_rf:[500.00 TO *]"]
72
+ assert_equal 1, q["price_rf:[200.00 TO 500.00]"]
73
+
74
+ f = records.facets["facet_fields"]
75
+ assert_equal 1, f["category_facet"]["Electronics"]
76
+ assert_equal 2, f["category_facet"]["Memory"]
77
+ assert_equal 1, f["category_facet"]["Hard Drive"]
78
+ assert_equal 1, f["manufacturer_facet"]["Samsung Electronics Co. Ltd."]
79
+ assert_equal 1, f["manufacturer_facet"]["Corsair Microsystems Inc."]
80
+ assert_equal 1, f["manufacturer_facet"]["A-DATA Technology Inc."]
81
+ assert_equal 1, f["manufacturer_facet"]["Apple Computer Inc."]
82
+ end
83
+
84
+ # Faceted searches with :sort and :zeros options turned on/off
85
+ def test_faceted_search_using_zero_and_sort
86
+ records = Electronic.find_by_solr "memory", :facets => {:fields =>[:category]}
87
+ assert_equal({"category_facet"=>{"Electronics"=>1, "Memory"=>2, "Hard Drive"=>1}}, records.facets['facet_fields'])
88
+
89
+ records = Electronic.find_by_solr "memory", :facets => {:sort => true, :fields =>[:category]}
90
+ assert_equal({"category_facet"=>{"Memory"=>2, "Electronics"=>1, "Hard Drive"=>1}}, records.facets['facet_fields'])
91
+
92
+ records = Electronic.find_by_solr "memory", :facets => {:fields =>[:manufacturer]}
93
+ assert_equal({"manufacturer_facet" => {"Dell, Inc" => 0,
94
+ "Samsung Electronics Co. Ltd." => 1,
95
+ "Corsair Microsystems Inc." => 1,
96
+ "A-DATA Technology Inc." => 1,
97
+ "Apple Computer Inc." => 1}}, records.facets['facet_fields'])
98
+
99
+ records = Electronic.find_by_solr "memory", :facets => {:zeros => false, :fields =>[:manufacturer]}
100
+ assert_equal({"manufacturer_facet" => {"Samsung Electronics Co. Ltd." => 1,
101
+ "Corsair Microsystems Inc." => 1,
102
+ "A-DATA Technology Inc." => 1,
103
+ "Apple Computer Inc." => 1}}, records.facets['facet_fields'])
104
+ end
105
+
106
+ # Faceted search with 'drill-down' option being passed.
107
+ # The :browse option receives the argument in the format:
108
+ # "facet_field:term". You can drill-down to as many
109
+ # facet fields as you like
110
+ def test_faceted_search_with_drill_down
111
+ records = Electronic.find_by_solr "memory", :facets => {:fields =>[:category]}
112
+ assert_equal 4, records.docs.size
113
+ assert_equal({"category_facet"=>{"Electronics"=>1, "Memory"=>2, "Hard Drive"=>1}}, records.facets['facet_fields'])
114
+
115
+ records = Electronic.find_by_solr "memory", :facets => {:fields =>[:category],
116
+ :browse => "category:Memory",
117
+ :zeros => false}
118
+ assert_equal 2, records.docs.size
119
+ assert_equal({"category_facet"=>{"Memory"=>2}}, records.facets['facet_fields'])
120
+ end
121
+
122
+ def test_faceted_search_with_dates
123
+ records = Electronic.find_by_solr "memory", :facets => {:dates => {:fields => [:created_at, :updated_at],
124
+ :start => (Date.today - 7.years).strftime("%Y-%m-%dT%H:%M:%SZ"), :end => Date.today.strftime("%Y-%m-%dT%H:%M:%SZ"), :gap => '+1YEAR', :other => :all}}
125
+
126
+ assert_equal 4, records.docs.size
127
+
128
+ assert_equal 0, records.facets["facet_dates"]["created_at_d"]["after"]
129
+ assert_equal 1, records.facets["facet_dates"]["created_at_d"]["before"]
130
+ assert_equal 3, records.facets["facet_dates"]["created_at_d"]["between"]
131
+
132
+ assert_equal 0, records.facets["facet_dates"]["updated_at_d"]["after"]
133
+ assert_equal 0, records.facets["facet_dates"]["updated_at_d"]["before"]
134
+ assert_equal 4, records.facets["facet_dates"]["updated_at_d"]["between"]
135
+ end
136
+
137
+ def test_faceted_search_with_dates_filter
138
+ records = Electronic.find_by_solr "memory", :facets => {:dates => {:filter => ["updated_at:[#{(Date.today - 3.months).strftime("%Y-%m-%dT%H:%M:%SZ")} TO NOW-1MONTH/DAY]"]}}
139
+
140
+ assert_equal 2, records.docs.size
141
+
142
+ records.docs.each { |r|
143
+ assert r.updated_at >= (Date.today - 3.month)
144
+ assert r.updated_at <= (Date.today - 1.month)
145
+ }
146
+ end
147
+
148
+ def test_faceted_search_with_dates_filter_and_facets
149
+ # this is a very contrived example but gives us data to validate
150
+ records = Electronic.find_by_solr "memory", :facets => {:dates => {:filter => ["updated_at:[#{(Date.today - 3.months).strftime("%Y-%m-%dT%H:%M:%SZ")} TO NOW-1MONTH/DAY]"],
151
+ :fields => [:created_at, :updated_at], :start => 'NOW-2MONTHS/DAY', :end => 'NOW-1MONTH/DAY', :gap => '+1MONTH', :other => :all}}
152
+
153
+ assert_equal 2, records.docs.size
154
+
155
+ assert_equal 0, records.facets["facet_dates"]["created_at_d"]["after"]
156
+ assert_equal 2, records.facets["facet_dates"]["created_at_d"]["before"]
157
+ assert_equal 0, records.facets["facet_dates"]["created_at_d"]["between"]
158
+
159
+ assert_equal 0, records.facets["facet_dates"]["updated_at_d"]["after"]
160
+ assert_equal 1, records.facets["facet_dates"]["updated_at_d"]["before"]
161
+ assert_equal 1, records.facets["facet_dates"]["updated_at_d"]["between"]
162
+ end
163
+ end
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+
3
+ class ActsAsSolrTest < Test::Unit::TestCase
4
+
5
+ fixtures :books, :movies
6
+
7
+ # Testing the multi_solr_search with the returning results being objects
8
+ def test_multi_solr_search_return_objects
9
+ records = Book.multi_solr_search "Napoleon OR Tom", :models => [Movie], :results_format => :objects
10
+ assert_equal 2, records.total
11
+ assert_equal Movie, records.docs.first.class
12
+ assert_equal Book, records.docs.last.class
13
+ end
14
+
15
+ # Testing the multi_solr_search with the returning results being ids
16
+ def test_multi_solr_search_return_ids
17
+ records = Book.multi_solr_search "Napoleon OR Tom", :models => [Movie], :results_format => :ids
18
+ assert_equal 2, records.total
19
+ assert records.docs.include?({"id" => "Movie:1"})
20
+ assert records.docs.include?({"id" => "Book:1"})
21
+ end
22
+
23
+ # Testing the multi_solr_search with multiple models
24
+ def test_multi_solr_search_multiple_models
25
+ records = Book.multi_solr_search "Napoleon OR Tom OR Thriller", :models => [Movie, Category], :results_format => :ids
26
+ assert_equal 4, records.total
27
+ [{"id" => "Category:1"}, {"id" =>"Book:1"}, {"id" => "Movie:1"}, {"id" =>"Book:3"}].each do |result|
28
+ assert records.docs.include?(result)
29
+ end
30
+ end
31
+
32
+ # Testing empty result set format
33
+ def test_returns_no_matches
34
+ records = Book.multi_solr_search "not found", :models => [Movie, Category]
35
+ assert_equal [], records.docs
36
+ assert_equal 0, records.total
37
+ end
38
+
39
+ def test_search_on_empty_string_does_not_return_nil
40
+ records = Book.multi_solr_search('', :models => [Movie, Category])
41
+ assert_not_nil records
42
+ assert_equal [], records.docs
43
+ assert_equal 0, records.total
44
+ end
45
+
46
+ def test_search_with_score_should_set_score
47
+ records = Book.multi_solr_search "Napoleon OR Tom", :models => [Movie], :results_format => :objects, :scores => true
48
+ assert_equal 1.0112731, records.docs.first.solr_score
49
+ assert_equal 0.6723396, records.docs.last.solr_score
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ # Table fields for 'movies'
2
+ # - id
3
+ # - name
4
+ # - biography
5
+
6
+ class Author < ActiveRecord::Base
7
+
8
+ acts_as_solr :auto_commit => false
9
+
10
+ end