jbasdf-muck-solr 0.4.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 (156) hide show
  1. data/CHANGE_LOG +239 -0
  2. data/LICENSE +19 -0
  3. data/README.markdown +118 -0
  4. data/README.rdoc +107 -0
  5. data/Rakefile +99 -0
  6. data/TESTING_THE_PLUGIN +25 -0
  7. data/VERSION.yml +4 -0
  8. data/config/solr.yml +15 -0
  9. data/config/solr_environment.rb +32 -0
  10. data/lib/acts_as_solr/acts_methods.rb +352 -0
  11. data/lib/acts_as_solr/class_methods.rb +236 -0
  12. data/lib/acts_as_solr/common_methods.rb +89 -0
  13. data/lib/acts_as_solr/deprecation.rb +61 -0
  14. data/lib/acts_as_solr/instance_methods.rb +165 -0
  15. data/lib/acts_as_solr/lazy_document.rb +18 -0
  16. data/lib/acts_as_solr/parser_methods.rb +203 -0
  17. data/lib/acts_as_solr/search_results.rb +68 -0
  18. data/lib/acts_as_solr/solr_fixtures.rb +13 -0
  19. data/lib/acts_as_solr/tasks/database.rake +16 -0
  20. data/lib/acts_as_solr/tasks/solr.rake +135 -0
  21. data/lib/acts_as_solr/tasks/test.rake +5 -0
  22. data/lib/acts_as_solr/tasks.rb +10 -0
  23. data/lib/acts_as_solr.rb +65 -0
  24. data/lib/solr/connection.rb +177 -0
  25. data/lib/solr/document.rb +75 -0
  26. data/lib/solr/exception.rb +13 -0
  27. data/lib/solr/field.rb +36 -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 +41 -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 +58 -0
  37. data/lib/solr/request/base.rb +36 -0
  38. data/lib/solr/request/commit.rb +29 -0
  39. data/lib/solr/request/delete.rb +48 -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 +46 -0
  43. data/lib/solr/request/optimize.rb +19 -0
  44. data/lib/solr/request/ping.rb +36 -0
  45. data/lib/solr/request/select.rb +54 -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 +15 -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 +26 -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 +60 -0
  63. data/lib/solr/response/xml.rb +39 -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 +26 -0
  69. data/solr/CHANGES.txt +1207 -0
  70. data/solr/LICENSE.txt +712 -0
  71. data/solr/NOTICE.txt +90 -0
  72. data/solr/etc/jetty.xml +205 -0
  73. data/solr/etc/webdefault.xml +379 -0
  74. data/solr/lib/easymock.jar +0 -0
  75. data/solr/lib/jetty-6.1.3.jar +0 -0
  76. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  77. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  78. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  79. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  80. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  81. data/solr/lib/servlet-api-2.4.jar +0 -0
  82. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  83. data/solr/lib/xpp3-1.1.3.4.O.jar +0 -0
  84. data/solr/solr/README.txt +52 -0
  85. data/solr/solr/bin/abc +176 -0
  86. data/solr/solr/bin/abo +176 -0
  87. data/solr/solr/bin/backup +108 -0
  88. data/solr/solr/bin/backupcleaner +142 -0
  89. data/solr/solr/bin/commit +128 -0
  90. data/solr/solr/bin/optimize +129 -0
  91. data/solr/solr/bin/readercycle +129 -0
  92. data/solr/solr/bin/rsyncd-disable +77 -0
  93. data/solr/solr/bin/rsyncd-enable +76 -0
  94. data/solr/solr/bin/rsyncd-start +145 -0
  95. data/solr/solr/bin/rsyncd-stop +105 -0
  96. data/solr/solr/bin/scripts-util +83 -0
  97. data/solr/solr/bin/snapcleaner +148 -0
  98. data/solr/solr/bin/snapinstaller +168 -0
  99. data/solr/solr/bin/snappuller +248 -0
  100. data/solr/solr/bin/snappuller-disable +77 -0
  101. data/solr/solr/bin/snappuller-enable +77 -0
  102. data/solr/solr/bin/snapshooter +109 -0
  103. data/solr/solr/conf/admin-extra.html +31 -0
  104. data/solr/solr/conf/protwords.txt +21 -0
  105. data/solr/solr/conf/schema.xml +126 -0
  106. data/solr/solr/conf/scripts.conf +24 -0
  107. data/solr/solr/conf/solrconfig.xml +458 -0
  108. data/solr/solr/conf/stopwords.txt +57 -0
  109. data/solr/solr/conf/synonyms.txt +31 -0
  110. data/solr/solr/conf/xslt/example.xsl +132 -0
  111. data/solr/solr/conf/xslt/example_atom.xsl +63 -0
  112. data/solr/solr/conf/xslt/example_rss.xsl +62 -0
  113. data/solr/start.jar +0 -0
  114. data/solr/webapps/solr.war +0 -0
  115. data/test/config/solr.yml +2 -0
  116. data/test/db/connections/mysql/connection.rb +10 -0
  117. data/test/db/connections/sqlite/connection.rb +8 -0
  118. data/test/db/migrate/001_create_books.rb +15 -0
  119. data/test/db/migrate/002_create_movies.rb +12 -0
  120. data/test/db/migrate/003_create_categories.rb +11 -0
  121. data/test/db/migrate/004_create_electronics.rb +16 -0
  122. data/test/db/migrate/005_create_authors.rb +12 -0
  123. data/test/db/migrate/006_create_postings.rb +9 -0
  124. data/test/db/migrate/007_create_posts.rb +13 -0
  125. data/test/db/migrate/008_create_gadgets.rb +11 -0
  126. data/test/fixtures/authors.yml +9 -0
  127. data/test/fixtures/books.yml +13 -0
  128. data/test/fixtures/categories.yml +7 -0
  129. data/test/fixtures/db_definitions/mysql.sql +41 -0
  130. data/test/fixtures/electronics.yml +49 -0
  131. data/test/fixtures/movies.yml +9 -0
  132. data/test/fixtures/postings.yml +10 -0
  133. data/test/functional/acts_as_solr_test.rb +413 -0
  134. data/test/functional/association_indexing_test.rb +37 -0
  135. data/test/functional/faceted_search_test.rb +163 -0
  136. data/test/functional/multi_solr_search_test.rb +57 -0
  137. data/test/models/author.rb +10 -0
  138. data/test/models/book.rb +10 -0
  139. data/test/models/category.rb +8 -0
  140. data/test/models/electronic.rb +25 -0
  141. data/test/models/gadget.rb +9 -0
  142. data/test/models/movie.rb +17 -0
  143. data/test/models/novel.rb +2 -0
  144. data/test/models/post.rb +3 -0
  145. data/test/models/posting.rb +11 -0
  146. data/test/test_helper.rb +54 -0
  147. data/test/unit/acts_methods_shoulda.rb +68 -0
  148. data/test/unit/class_methods_shoulda.rb +85 -0
  149. data/test/unit/common_methods_shoulda.rb +111 -0
  150. data/test/unit/instance_methods_shoulda.rb +318 -0
  151. data/test/unit/lazy_document_shoulda.rb +34 -0
  152. data/test/unit/parser_instance.rb +19 -0
  153. data/test/unit/parser_methods_shoulda.rb +268 -0
  154. data/test/unit/solr_instance.rb +49 -0
  155. data/test/unit/test_helper.rb +24 -0
  156. metadata +241 -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 (books.max_score >= 0.3 && books.max_score <= 0.6)
316
+
317
+ books.records.each { |book| assert_not_nil book.solr_score }
318
+ assert (books.docs.first.solr_score >= 0.3 && books.docs.first.solr_score <= 0.6)
319
+ assert (books.docs.last.solr_score >= 0.1 && books.docs.last.solr_score <= 0.2)
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 (books.docs.last.solr_score >= 0.3 && books.docs.last.solr_score <= 0.6)
390
+
391
+ books = Book.find_by_solr 'ruby^10 OR splinter', {:scores => true, :order => 'score desc' }
392
+ assert (books.docs.first.solr_score >= 0.3 && books.docs.first.solr_score <= 0.6)
393
+ assert (books.docs.last.solr_score >= 0.1 && books.docs.last.solr_score <= 0.2)
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,57 @@
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
+ classes = records.docs.map {|d| d.class}
12
+ assert classes.include?(Book)
13
+ assert classes.include?(Movie)
14
+ end
15
+
16
+ # Testing the multi_solr_search with the returning results being ids
17
+ def test_multi_solr_search_return_ids
18
+ records = Book.multi_solr_search "Napoleon OR Tom", :models => [Movie], :results_format => :ids
19
+ assert_equal 2, records.total
20
+ assert records.docs.include?({"id" => "Movie:1"})
21
+ assert records.docs.include?({"id" => "Book:1"})
22
+ end
23
+
24
+ # Testing the multi_solr_search with multiple models
25
+ def test_multi_solr_search_multiple_models
26
+ # TODO: Generalize me
27
+ ActsAsSolr::Post.execute(Solr::Request::Delete.new(:query => 'type_s:Author AND NOT id:"Author:1" AND NOT id:"Author:2"'))
28
+ ActsAsSolr::Post.execute(Solr::Request::Delete.new(:query => 'type_s:Book AND NOT id:"Book:1" AND NOT id:"Book:2"'))
29
+ ActsAsSolr::Post.execute(Solr::Request::Commit.new)
30
+
31
+ records = Book.multi_solr_search "Napoleon OR Tom OR Thriller", :models => [Movie, Category], :results_format => :ids
32
+ assert_equal 3, records.total
33
+ assert records.docs.include?({"id" => "Category:1"})
34
+ assert records.docs.include?({"id" =>"Book:1"})
35
+ assert records.docs.include?({"id" => "Movie:1"})
36
+ end
37
+
38
+ # Testing empty result set format
39
+ def test_returns_no_matches
40
+ records = Book.multi_solr_search "not found", :models => [Movie, Category]
41
+ assert_equal [], records.docs
42
+ assert_equal 0, records.total
43
+ end
44
+
45
+ def test_search_on_empty_string_does_not_return_nil
46
+ records = Book.multi_solr_search('', :models => [Movie, Category])
47
+ assert_not_nil records
48
+ assert_equal [], records.docs
49
+ assert_equal 0, records.total
50
+ end
51
+
52
+ def test_search_with_score_should_set_score
53
+ records = Book.multi_solr_search "Napoleon OR Tom", :models => [Movie], :results_format => :objects, :scores => true
54
+ assert records.docs.first.solr_score.is_a?(Float)
55
+ assert records.docs.last.solr_score.is_a?(Float)
56
+ end
57
+ 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