sunspot_solr 2.0.0.pre.120720 → 2.0.0.pre.120924

Sign up to get free protection for your applications and to get access to all the features.
Files changed (293) hide show
  1. data/.gitignore +11 -0
  2. data/.travis.yml +35 -0
  3. data/README.md +863 -0
  4. data/Rakefile +37 -0
  5. data/ci/travis.sh +67 -0
  6. data/sunspot/.gitignore +13 -0
  7. data/sunspot/Gemfile +5 -0
  8. data/sunspot/History.txt +258 -0
  9. data/sunspot/LICENSE +18 -0
  10. data/sunspot/Rakefile +13 -0
  11. data/sunspot/TODO +13 -0
  12. data/sunspot/lib/light_config.rb +40 -0
  13. data/sunspot/lib/sunspot.rb +579 -0
  14. data/sunspot/lib/sunspot/adapters.rb +349 -0
  15. data/sunspot/lib/sunspot/batcher.rb +62 -0
  16. data/sunspot/lib/sunspot/class_set.rb +23 -0
  17. data/sunspot/lib/sunspot/composite_setup.rb +202 -0
  18. data/sunspot/lib/sunspot/configuration.rb +53 -0
  19. data/sunspot/lib/sunspot/data_extractor.rb +50 -0
  20. data/sunspot/lib/sunspot/dsl.rb +5 -0
  21. data/sunspot/lib/sunspot/dsl/adjustable.rb +47 -0
  22. data/sunspot/lib/sunspot/dsl/field_group.rb +57 -0
  23. data/sunspot/lib/sunspot/dsl/field_query.rb +345 -0
  24. data/sunspot/lib/sunspot/dsl/fields.rb +103 -0
  25. data/sunspot/lib/sunspot/dsl/fulltext.rb +243 -0
  26. data/sunspot/lib/sunspot/dsl/function.rb +27 -0
  27. data/sunspot/lib/sunspot/dsl/functional.rb +44 -0
  28. data/sunspot/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  29. data/sunspot/lib/sunspot/dsl/paginatable.rb +32 -0
  30. data/sunspot/lib/sunspot/dsl/query_facet.rb +36 -0
  31. data/sunspot/lib/sunspot/dsl/restriction.rb +25 -0
  32. data/sunspot/lib/sunspot/dsl/restriction_with_near.rb +160 -0
  33. data/sunspot/lib/sunspot/dsl/scope.rb +214 -0
  34. data/sunspot/lib/sunspot/dsl/search.rb +30 -0
  35. data/sunspot/lib/sunspot/dsl/standard_query.rb +122 -0
  36. data/sunspot/lib/sunspot/field.rb +193 -0
  37. data/sunspot/lib/sunspot/field_factory.rb +129 -0
  38. data/sunspot/lib/sunspot/indexer.rb +136 -0
  39. data/sunspot/lib/sunspot/query.rb +11 -0
  40. data/sunspot/lib/sunspot/query/abstract_field_facet.rb +55 -0
  41. data/sunspot/lib/sunspot/query/bbox.rb +15 -0
  42. data/sunspot/lib/sunspot/query/boost_query.rb +24 -0
  43. data/sunspot/lib/sunspot/query/common_query.rb +96 -0
  44. data/sunspot/lib/sunspot/query/composite_fulltext.rb +36 -0
  45. data/sunspot/lib/sunspot/query/connective.rb +206 -0
  46. data/sunspot/lib/sunspot/query/date_field_facet.rb +14 -0
  47. data/sunspot/lib/sunspot/query/dismax.rb +132 -0
  48. data/sunspot/lib/sunspot/query/field_facet.rb +41 -0
  49. data/sunspot/lib/sunspot/query/field_group.rb +37 -0
  50. data/sunspot/lib/sunspot/query/filter.rb +38 -0
  51. data/sunspot/lib/sunspot/query/function_query.rb +52 -0
  52. data/sunspot/lib/sunspot/query/geo.rb +53 -0
  53. data/sunspot/lib/sunspot/query/geofilt.rb +16 -0
  54. data/sunspot/lib/sunspot/query/highlighting.rb +62 -0
  55. data/sunspot/lib/sunspot/query/more_like_this.rb +61 -0
  56. data/sunspot/lib/sunspot/query/more_like_this_query.rb +12 -0
  57. data/sunspot/lib/sunspot/query/pagination.rb +42 -0
  58. data/sunspot/lib/sunspot/query/query_facet.rb +53 -0
  59. data/sunspot/lib/sunspot/query/range_facet.rb +15 -0
  60. data/sunspot/lib/sunspot/query/restriction.rb +308 -0
  61. data/sunspot/lib/sunspot/query/scope.rb +9 -0
  62. data/sunspot/lib/sunspot/query/sort.rb +109 -0
  63. data/sunspot/lib/sunspot/query/sort_composite.rb +34 -0
  64. data/sunspot/lib/sunspot/query/standard_query.rb +16 -0
  65. data/sunspot/lib/sunspot/query/text_field_boost.rb +17 -0
  66. data/sunspot/lib/sunspot/schema.rb +151 -0
  67. data/sunspot/lib/sunspot/search.rb +9 -0
  68. data/sunspot/lib/sunspot/search/abstract_search.rb +286 -0
  69. data/sunspot/lib/sunspot/search/date_facet.rb +35 -0
  70. data/sunspot/lib/sunspot/search/facet_row.rb +27 -0
  71. data/sunspot/lib/sunspot/search/field_facet.rb +88 -0
  72. data/sunspot/lib/sunspot/search/field_group.rb +70 -0
  73. data/sunspot/lib/sunspot/search/group.rb +54 -0
  74. data/sunspot/lib/sunspot/search/highlight.rb +38 -0
  75. data/sunspot/lib/sunspot/search/hit.rb +150 -0
  76. data/sunspot/lib/sunspot/search/hit_enumerable.rb +68 -0
  77. data/sunspot/lib/sunspot/search/more_like_this_search.rb +31 -0
  78. data/sunspot/lib/sunspot/search/paginated_collection.rb +57 -0
  79. data/sunspot/lib/sunspot/search/query_facet.rb +67 -0
  80. data/sunspot/lib/sunspot/search/range_facet.rb +37 -0
  81. data/sunspot/lib/sunspot/search/standard_search.rb +21 -0
  82. data/sunspot/lib/sunspot/session.rb +262 -0
  83. data/sunspot/lib/sunspot/session_proxy.rb +95 -0
  84. data/sunspot/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  85. data/sunspot/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  86. data/sunspot/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  87. data/sunspot/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  88. data/sunspot/lib/sunspot/session_proxy/retry_5xx_session_proxy.rb +67 -0
  89. data/sunspot/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  90. data/sunspot/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  91. data/sunspot/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  92. data/sunspot/lib/sunspot/setup.rb +350 -0
  93. data/sunspot/lib/sunspot/text_field_setup.rb +29 -0
  94. data/sunspot/lib/sunspot/type.rb +393 -0
  95. data/sunspot/lib/sunspot/util.rb +252 -0
  96. data/sunspot/lib/sunspot/version.rb +3 -0
  97. data/sunspot/script/console +10 -0
  98. data/sunspot/spec/api/adapters_spec.rb +68 -0
  99. data/sunspot/spec/api/batcher_spec.rb +112 -0
  100. data/sunspot/spec/api/binding_spec.rb +50 -0
  101. data/sunspot/spec/api/class_set_spec.rb +24 -0
  102. data/sunspot/spec/api/hit_enumerable_spec.rb +47 -0
  103. data/sunspot/spec/api/indexer/attributes_spec.rb +149 -0
  104. data/sunspot/spec/api/indexer/batch_spec.rb +72 -0
  105. data/sunspot/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  106. data/sunspot/spec/api/indexer/fixed_fields_spec.rb +57 -0
  107. data/sunspot/spec/api/indexer/fulltext_spec.rb +43 -0
  108. data/sunspot/spec/api/indexer/removal_spec.rb +53 -0
  109. data/sunspot/spec/api/indexer/spec_helper.rb +1 -0
  110. data/sunspot/spec/api/indexer_spec.rb +14 -0
  111. data/sunspot/spec/api/query/advanced_manipulation_examples.rb +35 -0
  112. data/sunspot/spec/api/query/connectives_examples.rb +201 -0
  113. data/sunspot/spec/api/query/dsl_spec.rb +18 -0
  114. data/sunspot/spec/api/query/dynamic_fields_examples.rb +165 -0
  115. data/sunspot/spec/api/query/faceting_examples.rb +497 -0
  116. data/sunspot/spec/api/query/fulltext_examples.rb +313 -0
  117. data/sunspot/spec/api/query/function_spec.rb +79 -0
  118. data/sunspot/spec/api/query/geo_examples.rb +68 -0
  119. data/sunspot/spec/api/query/group_spec.rb +32 -0
  120. data/sunspot/spec/api/query/highlighting_examples.rb +245 -0
  121. data/sunspot/spec/api/query/more_like_this_spec.rb +140 -0
  122. data/sunspot/spec/api/query/ordering_pagination_examples.rb +116 -0
  123. data/sunspot/spec/api/query/scope_examples.rb +275 -0
  124. data/sunspot/spec/api/query/spatial_examples.rb +27 -0
  125. data/sunspot/spec/api/query/spec_helper.rb +1 -0
  126. data/sunspot/spec/api/query/standard_spec.rb +29 -0
  127. data/sunspot/spec/api/query/text_field_scoping_examples.rb +30 -0
  128. data/sunspot/spec/api/query/types_spec.rb +20 -0
  129. data/sunspot/spec/api/search/dynamic_fields_spec.rb +33 -0
  130. data/sunspot/spec/api/search/faceting_spec.rb +360 -0
  131. data/sunspot/spec/api/search/highlighting_spec.rb +69 -0
  132. data/sunspot/spec/api/search/hits_spec.rb +147 -0
  133. data/sunspot/spec/api/search/paginated_collection_spec.rb +36 -0
  134. data/sunspot/spec/api/search/results_spec.rb +72 -0
  135. data/sunspot/spec/api/search/search_spec.rb +23 -0
  136. data/sunspot/spec/api/search/spec_helper.rb +1 -0
  137. data/sunspot/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  138. data/sunspot/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  139. data/sunspot/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  140. data/sunspot/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +78 -0
  141. data/sunspot/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  142. data/sunspot/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  143. data/sunspot/spec/api/session_proxy/spec_helper.rb +9 -0
  144. data/sunspot/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
  145. data/sunspot/spec/api/session_spec.rb +232 -0
  146. data/sunspot/spec/api/spec_helper.rb +3 -0
  147. data/sunspot/spec/api/sunspot_spec.rb +29 -0
  148. data/sunspot/spec/ext.rb +11 -0
  149. data/sunspot/spec/helpers/indexer_helper.rb +17 -0
  150. data/sunspot/spec/helpers/integration_helper.rb +8 -0
  151. data/sunspot/spec/helpers/mock_session_helper.rb +13 -0
  152. data/sunspot/spec/helpers/query_helper.rb +26 -0
  153. data/sunspot/spec/helpers/search_helper.rb +68 -0
  154. data/sunspot/spec/integration/dynamic_fields_spec.rb +57 -0
  155. data/sunspot/spec/integration/faceting_spec.rb +330 -0
  156. data/sunspot/spec/integration/field_grouping_spec.rb +100 -0
  157. data/sunspot/spec/integration/geospatial_spec.rb +96 -0
  158. data/sunspot/spec/integration/highlighting_spec.rb +44 -0
  159. data/sunspot/spec/integration/indexing_spec.rb +55 -0
  160. data/sunspot/spec/integration/keyword_search_spec.rb +317 -0
  161. data/sunspot/spec/integration/local_search_spec.rb +64 -0
  162. data/sunspot/spec/integration/more_like_this_spec.rb +43 -0
  163. data/sunspot/spec/integration/scoped_search_spec.rb +386 -0
  164. data/sunspot/spec/integration/stored_fields_spec.rb +12 -0
  165. data/sunspot/spec/integration/test_pagination.rb +43 -0
  166. data/sunspot/spec/integration/unicode_spec.rb +15 -0
  167. data/sunspot/spec/mocks/adapters.rb +33 -0
  168. data/sunspot/spec/mocks/blog.rb +3 -0
  169. data/sunspot/spec/mocks/comment.rb +21 -0
  170. data/sunspot/spec/mocks/connection.rb +126 -0
  171. data/sunspot/spec/mocks/mock_adapter.rb +30 -0
  172. data/sunspot/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  173. data/sunspot/spec/mocks/mock_record.rb +52 -0
  174. data/sunspot/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  175. data/sunspot/spec/mocks/photo.rb +11 -0
  176. data/sunspot/spec/mocks/post.rb +86 -0
  177. data/sunspot/spec/mocks/super_class.rb +2 -0
  178. data/sunspot/spec/mocks/user.rb +13 -0
  179. data/sunspot/spec/spec_helper.rb +40 -0
  180. data/sunspot/sunspot.gemspec +37 -0
  181. data/sunspot/tasks/rdoc.rake +27 -0
  182. data/sunspot/tasks/schema.rake +19 -0
  183. data/sunspot/tasks/todo.rake +4 -0
  184. data/sunspot_rails/.gitignore +7 -0
  185. data/sunspot_rails/.rspec +1 -0
  186. data/sunspot_rails/History.txt +74 -0
  187. data/sunspot_rails/LICENSE +18 -0
  188. data/sunspot_rails/MIT-LICENSE +20 -0
  189. data/sunspot_rails/README.rdoc +265 -0
  190. data/sunspot_rails/Rakefile +17 -0
  191. data/sunspot_rails/TODO +8 -0
  192. data/sunspot_rails/dev_tasks/rdoc.rake +24 -0
  193. data/sunspot_rails/dev_tasks/release.rake +4 -0
  194. data/sunspot_rails/dev_tasks/spec.rake +107 -0
  195. data/sunspot_rails/dev_tasks/todo.rake +4 -0
  196. data/sunspot_rails/gemfiles/rails-2.3.14 +12 -0
  197. data/sunspot_rails/gemfiles/rails-3.0.15 +12 -0
  198. data/sunspot_rails/gemfiles/rails-3.1.6 +12 -0
  199. data/sunspot_rails/gemfiles/rails-3.2.6 +12 -0
  200. data/sunspot_rails/generators/sunspot/sunspot_generator.rb +9 -0
  201. data/sunspot_rails/generators/sunspot/templates/sunspot.yml +20 -0
  202. data/sunspot_rails/install.rb +1 -0
  203. data/sunspot_rails/lib/generators/sunspot_rails.rb +9 -0
  204. data/sunspot_rails/lib/generators/sunspot_rails/install/install_generator.rb +13 -0
  205. data/sunspot_rails/lib/generators/sunspot_rails/install/templates/config/sunspot.yml +19 -0
  206. data/sunspot_rails/lib/sunspot/rails.rb +69 -0
  207. data/sunspot_rails/lib/sunspot/rails/adapters.rb +88 -0
  208. data/sunspot_rails/lib/sunspot/rails/configuration.rb +352 -0
  209. data/sunspot_rails/lib/sunspot/rails/init.rb +5 -0
  210. data/sunspot_rails/lib/sunspot/rails/log_subscriber.rb +45 -0
  211. data/sunspot_rails/lib/sunspot/rails/railtie.rb +41 -0
  212. data/sunspot_rails/lib/sunspot/rails/railties/controller_runtime.rb +36 -0
  213. data/sunspot_rails/lib/sunspot/rails/request_lifecycle.rb +36 -0
  214. data/sunspot_rails/lib/sunspot/rails/searchable.rb +495 -0
  215. data/sunspot_rails/lib/sunspot/rails/server.rb +106 -0
  216. data/sunspot_rails/lib/sunspot/rails/solr_instrumentation.rb +19 -0
  217. data/sunspot_rails/lib/sunspot/rails/solr_logging.rb +62 -0
  218. data/sunspot_rails/lib/sunspot/rails/spec_helper.rb +26 -0
  219. data/sunspot_rails/lib/sunspot/rails/stub_session_proxy.rb +146 -0
  220. data/sunspot_rails/lib/sunspot/rails/tasks.rb +94 -0
  221. data/sunspot_rails/lib/sunspot_rails.rb +12 -0
  222. data/sunspot_rails/spec/configuration_spec.rb +209 -0
  223. data/sunspot_rails/spec/model_lifecycle_spec.rb +63 -0
  224. data/sunspot_rails/spec/model_spec.rb +601 -0
  225. data/sunspot_rails/spec/rails_template/app/controllers/application_controller.rb +10 -0
  226. data/sunspot_rails/spec/rails_template/app/controllers/posts_controller.rb +6 -0
  227. data/sunspot_rails/spec/rails_template/app/models/author.rb +10 -0
  228. data/sunspot_rails/spec/rails_template/app/models/blog.rb +14 -0
  229. data/sunspot_rails/spec/rails_template/app/models/location.rb +3 -0
  230. data/sunspot_rails/spec/rails_template/app/models/photo_post.rb +2 -0
  231. data/sunspot_rails/spec/rails_template/app/models/post.rb +13 -0
  232. data/sunspot_rails/spec/rails_template/app/models/post_with_auto.rb +12 -0
  233. data/sunspot_rails/spec/rails_template/app/models/post_with_default_scope.rb +13 -0
  234. data/sunspot_rails/spec/rails_template/config/boot.rb +127 -0
  235. data/sunspot_rails/spec/rails_template/config/preinitializer.rb +22 -0
  236. data/sunspot_rails/spec/rails_template/config/routes.rb +9 -0
  237. data/sunspot_rails/spec/rails_template/config/sunspot.yml +24 -0
  238. data/sunspot_rails/spec/rails_template/db/schema.rb +27 -0
  239. data/sunspot_rails/spec/request_lifecycle_spec.rb +61 -0
  240. data/sunspot_rails/spec/schema.rb +27 -0
  241. data/sunspot_rails/spec/searchable_spec.rb +12 -0
  242. data/sunspot_rails/spec/server_spec.rb +33 -0
  243. data/sunspot_rails/spec/session_spec.rb +57 -0
  244. data/sunspot_rails/spec/shared_examples/indexed_after_save.rb +8 -0
  245. data/sunspot_rails/spec/shared_examples/not_indexed_after_save.rb +8 -0
  246. data/sunspot_rails/spec/spec_helper.rb +48 -0
  247. data/sunspot_rails/spec/stub_session_proxy_spec.rb +122 -0
  248. data/sunspot_rails/sunspot_rails.gemspec +43 -0
  249. data/{Gemfile → sunspot_solr/Gemfile} +0 -0
  250. data/{README.rdoc → sunspot_solr/README.rdoc} +0 -0
  251. data/{bin → sunspot_solr/bin}/sunspot-installer +0 -0
  252. data/{bin → sunspot_solr/bin}/sunspot-solr +0 -0
  253. data/{lib → sunspot_solr/lib}/sunspot/solr/installer.rb +0 -0
  254. data/{lib → sunspot_solr/lib}/sunspot/solr/installer/config_installer.rb +0 -0
  255. data/{lib → sunspot_solr/lib}/sunspot/solr/installer/task_helper.rb +0 -0
  256. data/{lib → sunspot_solr/lib}/sunspot/solr/java.rb +0 -0
  257. data/{lib → sunspot_solr/lib}/sunspot/solr/railtie.rb +0 -0
  258. data/{lib → sunspot_solr/lib}/sunspot/solr/server.rb +1 -2
  259. data/{lib → sunspot_solr/lib}/sunspot/solr/tasks.rb +0 -0
  260. data/{lib → sunspot_solr/lib}/sunspot_solr.rb +0 -0
  261. data/{solr → sunspot_solr/solr}/README.txt +0 -0
  262. data/{solr → sunspot_solr/solr}/etc/jetty.xml +0 -0
  263. data/{solr → sunspot_solr/solr}/etc/webdefault.xml +0 -0
  264. data/{solr → sunspot_solr/solr}/lib/jetty-6.1.26-patched-JETTY-1340.jar +0 -0
  265. data/{solr → sunspot_solr/solr}/lib/jetty-util-6.1.26-patched-JETTY-1340.jar +0 -0
  266. data/{solr → sunspot_solr/solr}/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  267. data/{solr → sunspot_solr/solr}/lib/jsp-2.1/core-3.1.1.jar +0 -0
  268. data/{solr → sunspot_solr/solr}/lib/jsp-2.1/jsp-2.1.jar +0 -0
  269. data/{solr → sunspot_solr/solr}/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  270. data/{solr → sunspot_solr/solr}/lib/servlet-api-2.5-20081211.jar +0 -0
  271. data/{solr → sunspot_solr/solr}/solr/.gitignore +0 -0
  272. data/{solr → sunspot_solr/solr}/solr/README.txt +0 -0
  273. data/{solr → sunspot_solr/solr}/solr/conf/admin-extra.html +0 -0
  274. data/{solr → sunspot_solr/solr}/solr/conf/elevate.xml +0 -0
  275. data/{solr → sunspot_solr/solr}/solr/conf/mapping-ISOLatin1Accent.txt +0 -0
  276. data/{solr → sunspot_solr/solr}/solr/conf/protwords.txt +0 -0
  277. data/{solr → sunspot_solr/solr}/solr/conf/schema.xml +0 -0
  278. data/{solr → sunspot_solr/solr}/solr/conf/scripts.conf +0 -0
  279. data/{solr → sunspot_solr/solr}/solr/conf/solrconfig.xml +0 -0
  280. data/{solr → sunspot_solr/solr}/solr/conf/spellings.txt +0 -0
  281. data/{solr → sunspot_solr/solr}/solr/conf/stopwords.txt +0 -0
  282. data/{solr → sunspot_solr/solr}/solr/conf/synonyms.txt +0 -0
  283. data/{solr → sunspot_solr/solr}/solr/conf/xslt/example.xsl +0 -0
  284. data/{solr → sunspot_solr/solr}/solr/conf/xslt/example_atom.xsl +0 -0
  285. data/{solr → sunspot_solr/solr}/solr/conf/xslt/example_rss.xsl +0 -0
  286. data/{solr → sunspot_solr/solr}/solr/conf/xslt/luke.xsl +0 -0
  287. data/{solr → sunspot_solr/solr}/start.jar +0 -0
  288. data/{solr → sunspot_solr/solr}/webapps/solr.war +0 -0
  289. data/{spec → sunspot_solr/spec}/server_spec.rb +0 -0
  290. data/{spec → sunspot_solr/spec}/spec_helper.rb +0 -0
  291. data/{sunspot_solr.gemspec → sunspot_solr/sunspot_solr.gemspec} +0 -0
  292. data/tools/gem_tasks.rb +69 -0
  293. metadata +340 -110
@@ -0,0 +1,36 @@
1
+ module Sunspot #:nodoc:
2
+ module Rails #:nodoc:
3
+ #
4
+ # This module adds an after_filter to ActionController::Base that commits
5
+ # the Sunspot session if any documents have been added, changed, or removed
6
+ # in the course of the request.
7
+ #
8
+ module RequestLifecycle
9
+ class <<self
10
+ def included(base) #:nodoc:
11
+ subclasses = base.subclasses.map do |subclass|
12
+ begin
13
+ subclass.constantize
14
+ rescue NameError
15
+ end
16
+ end.compact
17
+ loaded_controllers = [base].concat(subclasses)
18
+ # Depending on how Sunspot::Rails is loaded, there may already be
19
+ # controllers loaded into memory that subclass this controller. In
20
+ # this case, since after_filter uses the inheritable_attribute
21
+ # structure, the already-loaded subclasses don't get the filters. So,
22
+ # the below ensures that all loaded controllers have the filter.
23
+ loaded_controllers.each do |controller|
24
+ controller.after_filter do
25
+ if Sunspot::Rails.configuration.auto_commit_after_request?
26
+ Sunspot.commit_if_dirty
27
+ elsif Sunspot::Rails.configuration.auto_commit_after_delete_request?
28
+ Sunspot.commit_if_delete_dirty
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,495 @@
1
+ module Sunspot #:nodoc:
2
+ module Rails #:nodoc:
3
+ #
4
+ # This module adds Sunspot functionality to ActiveRecord models. As well as
5
+ # providing class and instance methods, it optionally adds lifecycle hooks
6
+ # to automatically add and remove models from the Solr index as they are
7
+ # created and destroyed.
8
+ #
9
+ module Searchable
10
+ class <<self
11
+ def included(base) #:nodoc:
12
+ base.module_eval do
13
+ extend(ActsAsMethods)
14
+ end
15
+ end
16
+ end
17
+
18
+ module ActsAsMethods
19
+ #
20
+ # Makes a class searchable if it is not already, or adds search
21
+ # configuration if it is. Note that the options passed in are only used
22
+ # the first time this method is called for a particular class; so,
23
+ # search should be defined before activating any mixins that extend
24
+ # search configuration.
25
+ #
26
+ # The block passed into this method is evaluated by the
27
+ # <code>Sunspot.setup</code> method. See the Sunspot documentation for
28
+ # complete information on the functionality provided by that method.
29
+ #
30
+ # ==== Options (+options+)
31
+ #
32
+ # :auto_index<Boolean>::
33
+ # Automatically index models in Solr when they are saved.
34
+ # Default: true
35
+ # :auto_remove<Boolean>::
36
+ # Automatically remove models from the Solr index when they are
37
+ # destroyed. <b>Setting this option to +false+ is not recommended
38
+ # </b>(see the README).
39
+ # :if<Mixed>::
40
+ # Only index models in Solr if the method, proc or string evaluates
41
+ # to true (e.g. <code>:if => :should_index?</code> or <code>:if =>
42
+ # proc { |model| model.foo > 2 }</code>). Models that do not match
43
+ # the constraint will be removed from the index upon save. Multiple
44
+ # constraints can be specified by passing an array (e.g. <code>:if =>
45
+ # [:method1, :method2]</code>).
46
+ # :ignore_attribute_changes_of<Array>::
47
+ # Define attributes, that should not trigger a reindex of that
48
+ # object. Usual suspects are updated_at or counters.
49
+ # :include<Mixed>::
50
+ # Define default ActiveRecord includes, set this to allow ActiveRecord
51
+ # to load required associations when indexing. See ActiveRecord's
52
+ # documentation on eager-loading for examples on how to set this
53
+ # Default: []
54
+ # :unless<Mixed>::
55
+ # Only index models in Solr if the method, proc or string evaluates
56
+ # to false (e.g. <code>:unless => :should_not_index?</code> or <code>
57
+ # :unless => proc { |model| model.foo <= 2 }</code>). Models that do
58
+ # not match the constraint will be removed from the index upon save.
59
+ # Multiple constraints can be specified by passing an array (e.g.
60
+ # <code>:unless => [:method1, :method2]</code>).
61
+ #
62
+ # ==== Example
63
+ #
64
+ # class Post < ActiveRecord::Base
65
+ # searchable do
66
+ # text :title, :body
67
+ # string :sort_title do
68
+ # title.downcase.sub(/^(an?|the)/, '')
69
+ # end
70
+ # integer :blog_id
71
+ # time :updated_at
72
+ # end
73
+ # end
74
+ #
75
+ def searchable(options = {}, &block)
76
+ Sunspot.setup(self, &block)
77
+
78
+ if searchable?
79
+ sunspot_options[:include].concat(Util::Array(options[:include]))
80
+ else
81
+ extend ClassMethods
82
+ include InstanceMethods
83
+
84
+ class_attribute :sunspot_options
85
+
86
+ unless options[:auto_index] == false
87
+ before_save :mark_for_auto_indexing_or_removal
88
+ after_save :perform_index_tasks
89
+ end
90
+
91
+ unless options[:auto_remove] == false
92
+ after_destroy do |searchable|
93
+ searchable.remove_from_index
94
+ end
95
+ end
96
+ options[:include] = Util::Array(options[:include])
97
+
98
+ self.sunspot_options = options
99
+ end
100
+ end
101
+
102
+ #
103
+ # This method is defined on all ActiveRecord::Base subclasses. It
104
+ # is false for classes on which #searchable has not been called, and
105
+ # true for classes on which #searchable has been called.
106
+ #
107
+ # ==== Returns
108
+ #
109
+ # +false+
110
+ #
111
+ def searchable?
112
+ false
113
+ end
114
+ end
115
+
116
+ module ClassMethods
117
+ def self.extended(base) #:nodoc:
118
+ class <<base
119
+ alias_method :search, :solr_search unless method_defined? :search
120
+ alias_method :search_ids, :solr_search_ids unless method_defined? :search_ids
121
+ alias_method :remove_all_from_index, :solr_remove_all_from_index unless method_defined? :remove_all_from_index
122
+ alias_method :remove_all_from_index!, :solr_remove_all_from_index! unless method_defined? :remove_all_from_index!
123
+ alias_method :reindex, :solr_reindex unless method_defined? :reindex
124
+ alias_method :index, :solr_index unless method_defined? :index
125
+ alias_method :index_orphans, :solr_index_orphans unless method_defined? :index_orphans
126
+ alias_method :clean_index_orphans, :solr_clean_index_orphans unless method_defined? :clean_index_orphans
127
+ end
128
+ end
129
+ #
130
+ # Search for instances of this class in Solr. The block is delegated to
131
+ # the Sunspot.search method - see the Sunspot documentation for the full
132
+ # API.
133
+ #
134
+ # ==== Example
135
+ #
136
+ # Post.search(:include => [:blog]) do
137
+ # keywords 'best pizza'
138
+ # with :blog_id, 1
139
+ # order :updated_at, :desc
140
+ # facet :category_ids
141
+ # end
142
+ #
143
+ # ==== Options
144
+ #
145
+ # :include:: Specify associations to eager load
146
+ # :select:: Specify columns to select from database when loading results
147
+ #
148
+ # ==== Returns
149
+ #
150
+ # Sunspot::Search:: Object containing results, totals, facets, etc.
151
+ #
152
+ def solr_search(options = {}, &block)
153
+ solr_execute_search(options) do
154
+ Sunspot.new_search(self, &block)
155
+ end
156
+ end
157
+
158
+ #
159
+ # Get IDs of matching results without loading the result objects from
160
+ # the database. This method may be useful if search is used as an
161
+ # intermediate step in a larger find operation. The block is the same
162
+ # as the block provided to the #search method.
163
+ #
164
+ # ==== Returns
165
+ #
166
+ # Array:: Array of IDs, in the order returned by the search
167
+ #
168
+ def solr_search_ids(&block)
169
+ solr_execute_search_ids do
170
+ solr_search(&block)
171
+ end
172
+ end
173
+
174
+ #
175
+ # Remove instances of this class from the Solr index.
176
+ #
177
+ def solr_remove_all_from_index
178
+ Sunspot.remove_all(self)
179
+ end
180
+
181
+ #
182
+ # Remove all instances of this class from the Solr index and immediately
183
+ # commit.
184
+ #
185
+ #
186
+ def solr_remove_all_from_index!
187
+ Sunspot.remove_all!(self)
188
+ end
189
+
190
+ #
191
+ # Completely rebuild the index for this class. First removes all
192
+ # instances from the index, then loads records and indexes them.
193
+ #
194
+ # See #index for information on options, etc.
195
+ #
196
+ def solr_reindex(options = {})
197
+ solr_remove_all_from_index
198
+ solr_index(options)
199
+ end
200
+
201
+ #
202
+ # Add/update all existing records in the Solr index. The
203
+ # +batch_size+ argument specifies how many records to load out of the
204
+ # database at a time. The default batch size is 50; if nil is passed,
205
+ # records will not be indexed in batches. By default, a commit is issued
206
+ # after each batch; passing +false+ for +batch_commit+ will disable
207
+ # this, and only issue a commit at the end of the process. If associated
208
+ # objects need to indexed also, you can specify +include+ in format
209
+ # accepted by ActiveRecord to improve your sql select performance
210
+ #
211
+ # ==== Options (passed as a hash)
212
+ #
213
+ # batch_size<Integer>:: Batch size with which to load records. Passing
214
+ # 'nil' will skip batches. Default is 50.
215
+ # batch_commit<Boolean>:: Flag signalling if a commit should be done after
216
+ # after each batch is indexed, default is 'true'
217
+ # include<Mixed>:: include option to be passed to the ActiveRecord find,
218
+ # used for including associated objects that need to be
219
+ # indexed with the parent object, accepts all formats
220
+ # ActiveRecord::Base.find does
221
+ # first_id:: The lowest possible ID for this class. Defaults to 0, which
222
+ # is fine for integer IDs; string primary keys will need to
223
+ # specify something reasonable here.
224
+ #
225
+ # ==== Examples
226
+ #
227
+ # # index in batches of 50, commit after each
228
+ # Post.index
229
+ #
230
+ # # index all rows at once, then commit
231
+ # Post.index(:batch_size => nil)
232
+ #
233
+ # # index in batches of 50, commit when all batches complete
234
+ # Post.index(:batch_commit => false)
235
+ #
236
+ # # include the associated +author+ object when loading to index
237
+ # Post.index(:include => :author)
238
+ #
239
+ def solr_index(opts={})
240
+ options = {
241
+ :batch_size => Sunspot.config.indexing.default_batch_size,
242
+ :batch_commit => true,
243
+ :include => self.sunspot_options[:include],
244
+ :start => opts.delete(:first_id) || 0
245
+ }.merge(opts)
246
+ find_in_batch_options = {
247
+ :include => options[:include],
248
+ :batch_size => options[:batch_size],
249
+ :start => options[:start]
250
+ }
251
+ progress_bar = options[:progress_bar]
252
+ if options[:batch_size]
253
+ batch_counter = 0
254
+ find_in_batches(find_in_batch_options) do |records|
255
+ solr_benchmark options[:batch_size], batch_counter do
256
+ Sunspot.index(records.select { |model| model.indexable? })
257
+ Sunspot.commit if options[:batch_commit]
258
+ end
259
+ # track progress
260
+ progress_bar.increment!(records.length) if progress_bar
261
+ batch_counter += 1
262
+ end
263
+ else
264
+ records = all(:include => options[:include]).select { |model| model.indexable? }
265
+ Sunspot.index!(records)
266
+ end
267
+ # perform a final commit if not committing in batches
268
+ Sunspot.commit unless options[:batch_commit]
269
+ end
270
+
271
+ #
272
+ # Return the IDs of records of this class that are indexed in Solr but
273
+ # do not exist in the database. Under normal circumstances, this should
274
+ # never happen, but this method is provided in case something goes
275
+ # wrong. Usually you will want to rectify the situation by calling
276
+ # #clean_index_orphans or #reindex
277
+ #
278
+ # ==== Options (passed as a hash)
279
+ #
280
+ # batch_size<Integer>:: Batch size with which to load records. Passing
281
+ # Default is 1000 (from ActiveRecord).
282
+ #
283
+ # ==== Returns
284
+ #
285
+ # Array:: Collection of IDs that exist in Solr but not in the database
286
+ def solr_index_orphans(opts={})
287
+ batch_size = opts[:batch_size] || Sunspot.config.indexing.default_batch_size
288
+
289
+ solr_page = 0
290
+ solr_ids = []
291
+ while (solr_page = solr_page.next)
292
+ ids = solr_search_ids { paginate(:page => solr_page, :per_page => 1000) }.to_a
293
+ break if ids.empty?
294
+ solr_ids.concat ids
295
+ end
296
+
297
+ return solr_ids - self.connection.select_values(select(:id).arel).collect(&:to_i)
298
+ end
299
+
300
+ #
301
+ # Find IDs of records of this class that are indexed in Solr but do not
302
+ # exist in the database, and remove them from Solr. Under normal
303
+ # circumstances, this should not be necessary; this method is provided
304
+ # in case something goes wrong.
305
+ #
306
+ # ==== Options (passed as a hash)
307
+ #
308
+ # batch_size<Integer>:: Batch size with which to load records
309
+ # Default is 50
310
+ #
311
+ def solr_clean_index_orphans(opts={})
312
+ solr_index_orphans(opts).each do |id|
313
+ new do |fake_instance|
314
+ fake_instance.id = id
315
+ end.solr_remove_from_index
316
+ end
317
+ end
318
+
319
+ #
320
+ # Classes that have been defined as searchable return +true+ for this
321
+ # method.
322
+ #
323
+ # ==== Returns
324
+ #
325
+ # +true+
326
+ #
327
+ def searchable?
328
+ true
329
+ end
330
+
331
+ def solr_execute_search(options = {})
332
+ options.assert_valid_keys(:include, :select)
333
+ search = yield
334
+ unless options.empty?
335
+ search.build do |query|
336
+ if options[:include]
337
+ query.data_accessor_for(self).include = options[:include]
338
+ end
339
+ if options[:select]
340
+ query.data_accessor_for(self).select = options[:select]
341
+ end
342
+ end
343
+ end
344
+ search.execute
345
+ end
346
+
347
+ def solr_execute_search_ids(options = {})
348
+ search = yield
349
+ search.raw_results.map { |raw_result| raw_result.primary_key.to_i }
350
+ end
351
+
352
+ protected
353
+
354
+ #
355
+ # Does some logging for benchmarking indexing performance
356
+ #
357
+ def solr_benchmark(batch_size, counter, &block)
358
+ start = Time.now
359
+ logger.info("[#{Time.now}] Start Indexing")
360
+ yield
361
+ elapsed = Time.now-start
362
+ logger.info("[#{Time.now}] Completed Indexing. Rows indexed #{counter * batch_size}. Rows/sec: #{batch_size/elapsed.to_f} (Elapsed: #{elapsed} sec.)")
363
+ end
364
+
365
+ end
366
+
367
+ module InstanceMethods
368
+ def self.included(base) #:nodoc:
369
+ base.module_eval do
370
+ alias_method :index, :solr_index unless method_defined? :index
371
+ alias_method :index!, :solr_index! unless method_defined? :index!
372
+ alias_method :remove_from_index, :solr_remove_from_index unless method_defined? :remove_from_index
373
+ alias_method :remove_from_index!, :solr_remove_from_index! unless method_defined? :remove_from_index!
374
+ alias_method :more_like_this, :solr_more_like_this unless method_defined? :more_like_this
375
+ alias_method :more_like_this_ids, :solr_more_like_this_ids unless method_defined? :more_like_this_ids
376
+ end
377
+ end
378
+ #
379
+ # Index the model in Solr. If the model is already indexed, it will be
380
+ # updated. Using the defaults, you will usually not need to call this
381
+ # method, as models are indexed automatically when they are created or
382
+ # updated. If you have disabled automatic indexing (see
383
+ # ClassMethods#searchable), this method allows you to manage indexing
384
+ # manually.
385
+ #
386
+ def solr_index
387
+ Sunspot.index(self)
388
+ end
389
+
390
+ #
391
+ # Index the model in Solr and immediately commit. See #index
392
+ #
393
+ def solr_index!
394
+ Sunspot.index!(self)
395
+ end
396
+
397
+ #
398
+ # Remove the model from the Solr index. Using the defaults, this should
399
+ # not be necessary, as models will automatically be removed from the
400
+ # index when they are destroyed. If you disable automatic removal
401
+ # (which is not recommended!), you can use this method to manage removal
402
+ # manually.
403
+ #
404
+ def solr_remove_from_index
405
+ Sunspot.remove(self)
406
+ end
407
+
408
+ #
409
+ # Remove the model from the Solr index and commit immediately. See
410
+ # #remove_from_index
411
+ #
412
+ def solr_remove_from_index!
413
+ Sunspot.remove!(self)
414
+ end
415
+
416
+ def solr_more_like_this(*args, &block)
417
+ options = args.extract_options!
418
+ self.class.solr_execute_search(options) do
419
+ Sunspot.new_more_like_this(self, *args, &block)
420
+ end
421
+ end
422
+
423
+ def solr_more_like_this_ids(&block)
424
+ self.class.solr_execute_search_ids do
425
+ solr_more_like_this(&block)
426
+ end
427
+ end
428
+
429
+ def indexable?
430
+ # options[:if] is not specified or they successfully pass
431
+ if_passes = self.class.sunspot_options[:if].nil? ||
432
+ constraint_passes?(self.class.sunspot_options[:if])
433
+
434
+ # options[:unless] is not specified or they successfully pass
435
+ unless_passes = self.class.sunspot_options[:unless].nil? ||
436
+ !constraint_passes?(self.class.sunspot_options[:unless])
437
+
438
+ if_passes and unless_passes
439
+ end
440
+
441
+ private
442
+
443
+ def constraint_passes?(constraint)
444
+ case constraint
445
+ when Symbol
446
+ self.__send__(constraint)
447
+ when String
448
+ self.__send__(constraint.to_sym)
449
+ when Enumerable
450
+ # All constraints must pass
451
+ constraint.all? { |inner_constraint| constraint_passes?(inner_constraint) }
452
+ else
453
+ if constraint.respond_to?(:call) # could be a Proc or anything else that responds to call
454
+ constraint.call(self)
455
+ else
456
+ raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
457
+ end
458
+ end
459
+ end
460
+
461
+ def mark_for_auto_indexing_or_removal
462
+ if indexable?
463
+ # :if/:unless constraints pass or were not present
464
+
465
+ @marked_for_auto_indexing =
466
+ if !new_record? && ignore_attributes = self.class.sunspot_options[:ignore_attribute_changes_of]
467
+ !(changed.map { |attr| attr.to_sym } - ignore_attributes).blank?
468
+ else
469
+ true
470
+ end
471
+
472
+ @marked_for_auto_removal = false
473
+ else
474
+ # :if/:unless constraints did not pass; do not auto index and
475
+ # actually go one step further by removing it from the index
476
+ @marked_for_auto_indexing = false
477
+ @marked_for_auto_removal = true
478
+ end
479
+
480
+ true
481
+ end
482
+
483
+ def perform_index_tasks
484
+ if @marked_for_auto_indexing
485
+ solr_index
486
+ remove_instance_variable(:@marked_for_auto_indexing)
487
+ elsif @marked_for_auto_removal
488
+ solr_remove_from_index
489
+ remove_instance_variable(:@marked_for_auto_removal)
490
+ end
491
+ end
492
+ end
493
+ end
494
+ end
495
+ end