thinking-sphinx 1.2.13 → 6.0.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 (517) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +99 -0
  3. data/.github/actions/test/action.yml +46 -0
  4. data/.github/workflows/ci.yml +75 -0
  5. data/.gitignore +18 -0
  6. data/.travis.yml +36 -0
  7. data/Appraisals +71 -0
  8. data/CHANGELOG.markdown +782 -0
  9. data/Gemfile +18 -0
  10. data/LICENCE +1 -1
  11. data/Procfile.support +2 -0
  12. data/README.textile +91 -136
  13. data/Rakefile +26 -0
  14. data/bin/console +15 -0
  15. data/bin/loadsphinx +99 -0
  16. data/bin/testmatrix +48 -0
  17. data/lib/thinking/sphinx.rb +3 -0
  18. data/lib/thinking-sphinx.rb +3 -0
  19. data/lib/thinking_sphinx/active_record/association.rb +19 -0
  20. data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +44 -0
  21. data/lib/thinking_sphinx/active_record/association_proxy/attribute_matcher.rb +40 -0
  22. data/lib/thinking_sphinx/active_record/association_proxy.rb +34 -0
  23. data/lib/thinking_sphinx/active_record/attribute/sphinx_presenter.rb +52 -0
  24. data/lib/thinking_sphinx/active_record/attribute/type.rb +106 -0
  25. data/lib/thinking_sphinx/active_record/attribute/values.rb +20 -0
  26. data/lib/thinking_sphinx/active_record/attribute.rb +22 -0
  27. data/lib/thinking_sphinx/active_record/base.rb +91 -0
  28. data/lib/thinking_sphinx/active_record/callbacks/association_delta_callbacks.rb +21 -0
  29. data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +27 -0
  30. data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +66 -0
  31. data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +78 -0
  32. data/lib/thinking_sphinx/active_record/column.rb +44 -0
  33. data/lib/thinking_sphinx/active_record/column_sql_presenter.rb +54 -0
  34. data/lib/thinking_sphinx/active_record/database_adapters/abstract_adapter.rb +19 -0
  35. data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +51 -0
  36. data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +47 -0
  37. data/lib/thinking_sphinx/active_record/database_adapters.rb +59 -0
  38. data/lib/thinking_sphinx/active_record/depolymorph/association_reflection.rb +32 -0
  39. data/lib/thinking_sphinx/active_record/depolymorph/base_reflection.rb +32 -0
  40. data/lib/thinking_sphinx/active_record/depolymorph/conditions_reflection.rb +40 -0
  41. data/lib/thinking_sphinx/active_record/depolymorph/overridden_reflection.rb +50 -0
  42. data/lib/thinking_sphinx/active_record/depolymorph/scoped_reflection.rb +26 -0
  43. data/lib/thinking_sphinx/active_record/field.rb +18 -0
  44. data/lib/thinking_sphinx/active_record/filter_reflection.rb +18 -0
  45. data/lib/thinking_sphinx/active_record/index.rb +71 -0
  46. data/lib/thinking_sphinx/active_record/interpreter.rb +77 -0
  47. data/lib/thinking_sphinx/active_record/join_association.rb +17 -0
  48. data/lib/thinking_sphinx/active_record/log_subscriber.rb +37 -0
  49. data/lib/thinking_sphinx/active_record/polymorpher.rb +64 -0
  50. data/lib/thinking_sphinx/active_record/property.rb +30 -0
  51. data/lib/thinking_sphinx/active_record/property_query.rb +149 -0
  52. data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +89 -0
  53. data/lib/thinking_sphinx/active_record/simple_many_query.rb +37 -0
  54. data/lib/thinking_sphinx/active_record/source_joins.rb +68 -0
  55. data/lib/thinking_sphinx/active_record/sql_builder/clause_builder.rb +29 -0
  56. data/lib/thinking_sphinx/active_record/sql_builder/query.rb +59 -0
  57. data/lib/thinking_sphinx/active_record/sql_builder/statement.rb +147 -0
  58. data/lib/thinking_sphinx/active_record/sql_builder.rb +107 -0
  59. data/lib/thinking_sphinx/active_record/sql_source/template.rb +55 -0
  60. data/lib/thinking_sphinx/active_record/sql_source.rb +173 -0
  61. data/lib/thinking_sphinx/active_record.rb +39 -301
  62. data/lib/thinking_sphinx/attribute_types.rb +72 -0
  63. data/lib/thinking_sphinx/batched_search.rb +28 -0
  64. data/lib/thinking_sphinx/callbacks/appender.rb +63 -0
  65. data/lib/thinking_sphinx/callbacks.rb +44 -0
  66. data/lib/thinking_sphinx/capistrano/v2.rb +60 -0
  67. data/lib/thinking_sphinx/capistrano/v3.rb +104 -0
  68. data/lib/thinking_sphinx/capistrano.rb +10 -0
  69. data/lib/thinking_sphinx/commander.rb +27 -0
  70. data/lib/thinking_sphinx/commands/base.rb +53 -0
  71. data/lib/thinking_sphinx/commands/clear_real_time.rb +22 -0
  72. data/lib/thinking_sphinx/commands/clear_sql.rb +18 -0
  73. data/lib/thinking_sphinx/commands/configure.rb +15 -0
  74. data/lib/thinking_sphinx/commands/index_real_time.rb +15 -0
  75. data/lib/thinking_sphinx/commands/index_sql.rb +25 -0
  76. data/lib/thinking_sphinx/commands/merge.rb +27 -0
  77. data/lib/thinking_sphinx/commands/merge_and_update.rb +57 -0
  78. data/lib/thinking_sphinx/commands/prepare.rb +15 -0
  79. data/lib/thinking_sphinx/commands/rotate.rb +13 -0
  80. data/lib/thinking_sphinx/commands/running.rb +15 -0
  81. data/lib/thinking_sphinx/commands/start_attached.rb +22 -0
  82. data/lib/thinking_sphinx/commands/start_detached.rb +21 -0
  83. data/lib/thinking_sphinx/commands/stop.rb +24 -0
  84. data/lib/thinking_sphinx/commands.rb +20 -0
  85. data/lib/thinking_sphinx/configuration/consistent_ids.rb +35 -0
  86. data/lib/thinking_sphinx/configuration/defaults.rb +7 -0
  87. data/lib/thinking_sphinx/configuration/distributed_indices.rb +31 -0
  88. data/lib/thinking_sphinx/configuration/duplicate_names.rb +36 -0
  89. data/lib/thinking_sphinx/configuration/minimum_fields.rb +36 -0
  90. data/lib/thinking_sphinx/configuration.rb +196 -269
  91. data/lib/thinking_sphinx/connection/client.rb +74 -0
  92. data/lib/thinking_sphinx/connection/jruby.rb +58 -0
  93. data/lib/thinking_sphinx/connection/mri.rb +26 -0
  94. data/lib/thinking_sphinx/connection.rb +73 -0
  95. data/lib/thinking_sphinx/core/field.rb +11 -0
  96. data/lib/thinking_sphinx/core/index.rb +110 -0
  97. data/lib/thinking_sphinx/core/interpreter.rb +25 -0
  98. data/lib/thinking_sphinx/core/property.rb +15 -0
  99. data/lib/thinking_sphinx/core/settings.rb +11 -0
  100. data/lib/thinking_sphinx/core.rb +11 -0
  101. data/lib/thinking_sphinx/deletion.rb +70 -0
  102. data/lib/thinking_sphinx/deltas/default_delta.rb +57 -66
  103. data/lib/thinking_sphinx/deltas/delete_job.rb +27 -0
  104. data/lib/thinking_sphinx/deltas/index_job.rb +28 -0
  105. data/lib/thinking_sphinx/deltas.rb +52 -27
  106. data/lib/thinking_sphinx/distributed/index.rb +46 -0
  107. data/lib/thinking_sphinx/distributed.rb +7 -0
  108. data/lib/thinking_sphinx/errors.rb +96 -0
  109. data/lib/thinking_sphinx/excerpter.rb +36 -19
  110. data/lib/thinking_sphinx/facet.rb +29 -122
  111. data/lib/thinking_sphinx/facet_search.rb +132 -125
  112. data/lib/thinking_sphinx/float_formatter.rb +35 -0
  113. data/lib/thinking_sphinx/frameworks/plain.rb +10 -0
  114. data/lib/thinking_sphinx/frameworks/rails.rb +11 -0
  115. data/lib/thinking_sphinx/frameworks.rb +11 -0
  116. data/lib/thinking_sphinx/guard/file.rb +28 -0
  117. data/lib/thinking_sphinx/guard/files.rb +40 -0
  118. data/lib/thinking_sphinx/guard/none.rb +7 -0
  119. data/lib/thinking_sphinx/guard.rb +9 -0
  120. data/lib/thinking_sphinx/hooks/guard_presence.rb +34 -0
  121. data/lib/thinking_sphinx/index.rb +50 -92
  122. data/lib/thinking_sphinx/index_set.rb +96 -0
  123. data/lib/thinking_sphinx/indexing_strategies/all_at_once.rb +9 -0
  124. data/lib/thinking_sphinx/indexing_strategies/one_at_a_time.rb +16 -0
  125. data/lib/thinking_sphinx/interfaces/base.rb +13 -0
  126. data/lib/thinking_sphinx/interfaces/daemon.rb +27 -0
  127. data/lib/thinking_sphinx/interfaces/real_time.rb +46 -0
  128. data/lib/thinking_sphinx/interfaces/sql.rb +53 -0
  129. data/lib/thinking_sphinx/interfaces.rb +10 -0
  130. data/lib/thinking_sphinx/logger.rb +9 -0
  131. data/lib/thinking_sphinx/masks/group_enumerators_mask.rb +30 -0
  132. data/lib/thinking_sphinx/masks/pagination_mask.rb +63 -0
  133. data/lib/thinking_sphinx/masks/scopes_mask.rb +56 -0
  134. data/lib/thinking_sphinx/masks/weight_enumerator_mask.rb +17 -0
  135. data/lib/thinking_sphinx/masks.rb +10 -0
  136. data/lib/thinking_sphinx/middlewares/active_record_translator.rb +103 -0
  137. data/lib/thinking_sphinx/middlewares/geographer.rb +94 -0
  138. data/lib/thinking_sphinx/middlewares/glazier.rb +52 -0
  139. data/lib/thinking_sphinx/middlewares/ids_only.rb +15 -0
  140. data/lib/thinking_sphinx/middlewares/inquirer.rb +64 -0
  141. data/lib/thinking_sphinx/middlewares/middleware.rb +11 -0
  142. data/lib/thinking_sphinx/middlewares/sphinxql.rb +256 -0
  143. data/lib/thinking_sphinx/middlewares/stale_id_checker.rb +47 -0
  144. data/lib/thinking_sphinx/middlewares/stale_id_filter.rb +48 -0
  145. data/lib/thinking_sphinx/middlewares/valid_options.rb +25 -0
  146. data/lib/thinking_sphinx/middlewares.rb +35 -0
  147. data/lib/thinking_sphinx/panes/attributes_pane.rb +11 -0
  148. data/lib/thinking_sphinx/panes/distance_pane.rb +15 -0
  149. data/lib/thinking_sphinx/panes/excerpts_pane.rb +43 -0
  150. data/lib/thinking_sphinx/panes/weight_pane.rb +11 -0
  151. data/lib/thinking_sphinx/panes.rb +10 -0
  152. data/lib/thinking_sphinx/processor.rb +71 -0
  153. data/lib/thinking_sphinx/query.rb +11 -0
  154. data/lib/thinking_sphinx/railtie.rb +38 -0
  155. data/lib/thinking_sphinx/rake_interface.rb +34 -0
  156. data/lib/thinking_sphinx/real_time/attribute.rb +27 -0
  157. data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +60 -0
  158. data/lib/thinking_sphinx/real_time/field.rb +9 -0
  159. data/lib/thinking_sphinx/real_time/index/template.rb +52 -0
  160. data/lib/thinking_sphinx/real_time/index.rb +102 -0
  161. data/lib/thinking_sphinx/real_time/interpreter.rb +54 -0
  162. data/lib/thinking_sphinx/real_time/populator.rb +46 -0
  163. data/lib/thinking_sphinx/real_time/processor.rb +36 -0
  164. data/lib/thinking_sphinx/real_time/property.rb +21 -0
  165. data/lib/thinking_sphinx/real_time/transcribe_instance.rb +38 -0
  166. data/lib/thinking_sphinx/real_time/transcriber.rb +89 -0
  167. data/lib/thinking_sphinx/real_time/translator.rb +39 -0
  168. data/lib/thinking_sphinx/real_time.rb +40 -0
  169. data/lib/thinking_sphinx/scopes.rb +34 -0
  170. data/lib/thinking_sphinx/search/batch_inquirer.rb +23 -0
  171. data/lib/thinking_sphinx/search/context.rb +31 -0
  172. data/lib/thinking_sphinx/search/glaze.rb +39 -0
  173. data/lib/thinking_sphinx/search/merger.rb +34 -0
  174. data/lib/thinking_sphinx/search/query.rb +33 -0
  175. data/lib/thinking_sphinx/search/stale_ids_exception.rb +15 -0
  176. data/lib/thinking_sphinx/search.rb +166 -704
  177. data/lib/thinking_sphinx/settings.rb +128 -0
  178. data/lib/thinking_sphinx/sinatra.rb +7 -0
  179. data/lib/thinking_sphinx/subscribers/populator_subscriber.rb +48 -0
  180. data/lib/thinking_sphinx/tasks.rb +70 -150
  181. data/lib/thinking_sphinx/test.rb +56 -0
  182. data/lib/thinking_sphinx/utf8.rb +18 -0
  183. data/lib/thinking_sphinx/wildcard.rb +42 -0
  184. data/lib/thinking_sphinx/with_output.rb +13 -0
  185. data/lib/thinking_sphinx.rb +83 -185
  186. data/spec/acceptance/association_scoping_spec.rb +65 -0
  187. data/spec/acceptance/attribute_access_spec.rb +58 -0
  188. data/spec/acceptance/attribute_updates_spec.rb +18 -0
  189. data/spec/acceptance/batch_searching_spec.rb +23 -0
  190. data/spec/acceptance/big_integers_spec.rb +61 -0
  191. data/spec/acceptance/excerpts_spec.rb +50 -0
  192. data/spec/acceptance/facets_spec.rb +141 -0
  193. data/spec/acceptance/geosearching_spec.rb +70 -0
  194. data/spec/acceptance/grouping_by_attributes_spec.rb +79 -0
  195. data/spec/acceptance/index_options_spec.rb +154 -0
  196. data/spec/acceptance/indexing_spec.rb +38 -0
  197. data/spec/acceptance/merging_spec.rb +90 -0
  198. data/spec/acceptance/paginating_search_results_spec.rb +42 -0
  199. data/spec/acceptance/real_time_updates_spec.rb +115 -0
  200. data/spec/acceptance/remove_deleted_records_spec.rb +99 -0
  201. data/spec/acceptance/search_counts_spec.rb +20 -0
  202. data/spec/acceptance/search_for_just_ids_spec.rb +21 -0
  203. data/spec/acceptance/searching_across_models_spec.rb +47 -0
  204. data/spec/acceptance/searching_across_schemas_spec.rb +40 -0
  205. data/spec/acceptance/searching_on_fields_spec.rb +59 -0
  206. data/spec/acceptance/searching_with_filters_spec.rb +159 -0
  207. data/spec/acceptance/searching_with_sti_spec.rb +76 -0
  208. data/spec/acceptance/searching_within_a_model_spec.rb +117 -0
  209. data/spec/acceptance/sorting_search_results_spec.rb +50 -0
  210. data/spec/acceptance/spec_helper.rb +6 -0
  211. data/spec/acceptance/specifying_sql_spec.rb +516 -0
  212. data/spec/acceptance/sphinx_scopes_spec.rb +87 -0
  213. data/spec/acceptance/sql_deltas_spec.rb +78 -0
  214. data/spec/acceptance/support/database_cleaner.rb +13 -0
  215. data/spec/acceptance/support/sphinx_controller.rb +62 -0
  216. data/spec/acceptance/support/sphinx_helpers.rb +45 -0
  217. data/spec/acceptance/suspended_deltas_spec.rb +56 -0
  218. data/spec/fixtures/database.yml +4 -0
  219. data/spec/internal/app/indices/admin_person_index.rb +9 -0
  220. data/spec/internal/app/indices/album_index.rb +9 -0
  221. data/spec/internal/app/indices/animal_index.rb +5 -0
  222. data/spec/internal/app/indices/article_index.rb +31 -0
  223. data/spec/internal/app/indices/bird_index.rb +6 -0
  224. data/spec/internal/app/indices/book_index.rb +11 -0
  225. data/spec/internal/app/indices/car_index.rb +7 -0
  226. data/spec/internal/app/indices/city_index.rb +9 -0
  227. data/spec/internal/app/indices/colour_index.rb +7 -0
  228. data/spec/internal/app/indices/product_index.rb +27 -0
  229. data/spec/internal/app/indices/tee_index.rb +6 -0
  230. data/spec/internal/app/indices/user_index.rb +9 -0
  231. data/spec/internal/app/models/admin/person.rb +9 -0
  232. data/spec/internal/app/models/album.rb +25 -0
  233. data/spec/internal/app/models/animal.rb +5 -0
  234. data/spec/internal/app/models/article.rb +9 -0
  235. data/spec/internal/app/models/bird.rb +5 -0
  236. data/spec/internal/app/models/book.rb +18 -0
  237. data/spec/internal/app/models/car.rb +7 -0
  238. data/spec/internal/app/models/categorisation.rb +15 -0
  239. data/spec/internal/app/models/category.rb +6 -0
  240. data/spec/internal/app/models/city.rb +7 -0
  241. data/spec/internal/app/models/colour.rb +7 -0
  242. data/spec/internal/app/models/event.rb +5 -0
  243. data/spec/internal/app/models/flightless_bird.rb +4 -0
  244. data/spec/internal/app/models/genre.rb +5 -0
  245. data/spec/internal/app/models/hardcover.rb +5 -0
  246. data/spec/internal/app/models/mammal.rb +4 -0
  247. data/spec/internal/app/models/manufacturer.rb +5 -0
  248. data/spec/internal/app/models/product.rb +8 -0
  249. data/spec/internal/app/models/tag.rb +6 -0
  250. data/{features/support → spec/internal/app}/models/tagging.rb +3 -1
  251. data/spec/internal/app/models/tee.rb +10 -0
  252. data/spec/internal/app/models/tweet.rb +5 -0
  253. data/spec/internal/app/models/user.rb +10 -0
  254. data/spec/internal/config/database.yml +17 -0
  255. data/spec/internal/db/schema.rb +115 -0
  256. data/spec/internal/tmp/.gitkeep +0 -0
  257. data/spec/spec_helper.rb +29 -0
  258. data/spec/support/json_column.rb +35 -0
  259. data/spec/support/multi_schema.rb +50 -0
  260. data/spec/support/mysql.rb +25 -0
  261. data/spec/support/sphinx_yaml_helpers.rb +16 -0
  262. data/spec/thinking_sphinx/active_record/association_spec.rb +14 -0
  263. data/spec/thinking_sphinx/active_record/attribute/type_spec.rb +165 -0
  264. data/spec/thinking_sphinx/active_record/base_spec.rb +131 -0
  265. data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +128 -0
  266. data/spec/thinking_sphinx/active_record/callbacks/delta_callbacks_spec.rb +176 -0
  267. data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +91 -0
  268. data/spec/thinking_sphinx/active_record/column_spec.rb +72 -0
  269. data/spec/thinking_sphinx/active_record/column_sql_presenter_spec.rb +39 -0
  270. data/spec/thinking_sphinx/active_record/database_adapters/abstract_adapter_spec.rb +33 -0
  271. data/spec/thinking_sphinx/active_record/database_adapters/mysql_adapter_spec.rb +70 -0
  272. data/spec/thinking_sphinx/active_record/database_adapters/postgresql_adapter_spec.rb +66 -0
  273. data/spec/thinking_sphinx/active_record/database_adapters_spec.rb +128 -0
  274. data/spec/thinking_sphinx/active_record/field_spec.rb +51 -0
  275. data/spec/thinking_sphinx/active_record/filter_reflection_spec.rb +207 -0
  276. data/spec/thinking_sphinx/active_record/index_spec.rb +220 -0
  277. data/spec/thinking_sphinx/active_record/interpreter_spec.rb +329 -0
  278. data/spec/thinking_sphinx/active_record/polymorpher_spec.rb +87 -0
  279. data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +264 -0
  280. data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +662 -0
  281. data/spec/thinking_sphinx/active_record/sql_source_spec.rb +507 -0
  282. data/spec/thinking_sphinx/attribute_types_spec.rb +52 -0
  283. data/spec/thinking_sphinx/commands/clear_real_time_spec.rb +46 -0
  284. data/spec/thinking_sphinx/commands/clear_sql_spec.rb +52 -0
  285. data/spec/thinking_sphinx/commands/configure_spec.rb +31 -0
  286. data/spec/thinking_sphinx/commands/index_real_time_spec.rb +33 -0
  287. data/spec/thinking_sphinx/commands/index_sql_spec.rb +86 -0
  288. data/spec/thinking_sphinx/commands/merge_and_update_spec.rb +106 -0
  289. data/spec/thinking_sphinx/commands/merge_spec.rb +48 -0
  290. data/spec/thinking_sphinx/commands/prepare_spec.rb +31 -0
  291. data/spec/thinking_sphinx/commands/running_spec.rb +30 -0
  292. data/spec/thinking_sphinx/commands/start_detached_spec.rb +67 -0
  293. data/spec/thinking_sphinx/commands/stop_spec.rb +63 -0
  294. data/spec/thinking_sphinx/configuration/minimum_fields_spec.rb +60 -0
  295. data/spec/thinking_sphinx/configuration_spec.rb +582 -0
  296. data/spec/thinking_sphinx/connection/mri_spec.rb +49 -0
  297. data/spec/thinking_sphinx/connection_spec.rb +87 -0
  298. data/spec/thinking_sphinx/deletion_spec.rb +57 -0
  299. data/spec/thinking_sphinx/deltas/default_delta_spec.rb +123 -0
  300. data/spec/thinking_sphinx/deltas_spec.rb +77 -0
  301. data/spec/thinking_sphinx/errors_spec.rb +103 -0
  302. data/spec/thinking_sphinx/excerpter_spec.rb +53 -0
  303. data/spec/thinking_sphinx/facet_search_spec.rb +133 -0
  304. data/spec/thinking_sphinx/hooks/guard_presence_spec.rb +30 -0
  305. data/spec/thinking_sphinx/index_set_spec.rb +132 -0
  306. data/spec/thinking_sphinx/index_spec.rb +140 -0
  307. data/spec/thinking_sphinx/interfaces/daemon_spec.rb +60 -0
  308. data/spec/thinking_sphinx/interfaces/real_time_spec.rb +109 -0
  309. data/spec/thinking_sphinx/interfaces/sql_spec.rb +122 -0
  310. data/spec/thinking_sphinx/masks/pagination_mask_spec.rb +123 -0
  311. data/spec/thinking_sphinx/masks/scopes_mask_spec.rb +139 -0
  312. data/spec/thinking_sphinx/middlewares/active_record_translator_spec.rb +180 -0
  313. data/spec/thinking_sphinx/middlewares/geographer_spec.rb +102 -0
  314. data/spec/thinking_sphinx/middlewares/glazier_spec.rb +65 -0
  315. data/spec/thinking_sphinx/middlewares/inquirer_spec.rb +72 -0
  316. data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +401 -0
  317. data/spec/thinking_sphinx/middlewares/stale_id_checker_spec.rb +50 -0
  318. data/spec/thinking_sphinx/middlewares/stale_id_filter_spec.rb +113 -0
  319. data/spec/thinking_sphinx/middlewares/valid_options_spec.rb +51 -0
  320. data/spec/thinking_sphinx/panes/attributes_pane_spec.rb +23 -0
  321. data/spec/thinking_sphinx/panes/distance_pane_spec.rb +43 -0
  322. data/spec/thinking_sphinx/panes/excerpts_pane_spec.rb +53 -0
  323. data/spec/thinking_sphinx/panes/weight_pane_spec.rb +22 -0
  324. data/spec/thinking_sphinx/rake_interface_spec.rb +39 -0
  325. data/spec/thinking_sphinx/real_time/attribute_spec.rb +64 -0
  326. data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +238 -0
  327. data/spec/thinking_sphinx/real_time/field_spec.rb +69 -0
  328. data/spec/thinking_sphinx/real_time/index_spec.rb +230 -0
  329. data/spec/thinking_sphinx/real_time/interpreter_spec.rb +203 -0
  330. data/spec/thinking_sphinx/real_time/transcribe_instance_spec.rb +35 -0
  331. data/spec/thinking_sphinx/real_time/transcriber_spec.rb +109 -0
  332. data/spec/thinking_sphinx/real_time/translator_spec.rb +17 -0
  333. data/spec/thinking_sphinx/scopes_spec.rb +51 -0
  334. data/spec/thinking_sphinx/search/glaze_spec.rb +79 -0
  335. data/spec/thinking_sphinx/search/query_spec.rb +87 -0
  336. data/spec/thinking_sphinx/search_spec.rb +214 -0
  337. data/spec/thinking_sphinx/wildcard_spec.rb +53 -0
  338. data/spec/thinking_sphinx_spec.rb +44 -0
  339. data/thinking-sphinx.gemspec +42 -0
  340. metadata +656 -243
  341. data/VERSION.yml +0 -5
  342. data/features/alternate_primary_key.feature +0 -27
  343. data/features/attribute_transformation.feature +0 -22
  344. data/features/attribute_updates.feature +0 -33
  345. data/features/datetime_deltas.feature +0 -66
  346. data/features/delayed_delta_indexing.feature +0 -37
  347. data/features/deleting_instances.feature +0 -64
  348. data/features/direct_attributes.feature +0 -11
  349. data/features/excerpts.feature +0 -13
  350. data/features/extensible_delta_indexing.feature +0 -9
  351. data/features/facets.feature +0 -76
  352. data/features/facets_across_model.feature +0 -29
  353. data/features/handling_edits.feature +0 -92
  354. data/features/retry_stale_indexes.feature +0 -24
  355. data/features/searching_across_models.feature +0 -20
  356. data/features/searching_by_model.feature +0 -175
  357. data/features/searching_with_find_arguments.feature +0 -56
  358. data/features/sphinx_detection.feature +0 -25
  359. data/features/sphinx_scopes.feature +0 -35
  360. data/features/step_definitions/alpha_steps.rb +0 -3
  361. data/features/step_definitions/beta_steps.rb +0 -7
  362. data/features/step_definitions/common_steps.rb +0 -178
  363. data/features/step_definitions/datetime_delta_steps.rb +0 -15
  364. data/features/step_definitions/delayed_delta_indexing_steps.rb +0 -7
  365. data/features/step_definitions/extensible_delta_indexing_steps.rb +0 -7
  366. data/features/step_definitions/facet_steps.rb +0 -92
  367. data/features/step_definitions/find_arguments_steps.rb +0 -36
  368. data/features/step_definitions/gamma_steps.rb +0 -15
  369. data/features/step_definitions/scope_steps.rb +0 -11
  370. data/features/step_definitions/search_steps.rb +0 -89
  371. data/features/step_definitions/sphinx_steps.rb +0 -31
  372. data/features/sti_searching.feature +0 -14
  373. data/features/support/database.example.yml +0 -3
  374. data/features/support/database.yml +0 -5
  375. data/features/support/db/active_record.rb +0 -40
  376. data/features/support/db/database.yml +0 -5
  377. data/features/support/db/fixtures/alphas.rb +0 -10
  378. data/features/support/db/fixtures/authors.rb +0 -1
  379. data/features/support/db/fixtures/betas.rb +0 -10
  380. data/features/support/db/fixtures/boxes.rb +0 -9
  381. data/features/support/db/fixtures/categories.rb +0 -1
  382. data/features/support/db/fixtures/cats.rb +0 -3
  383. data/features/support/db/fixtures/comments.rb +0 -24
  384. data/features/support/db/fixtures/delayed_betas.rb +0 -10
  385. data/features/support/db/fixtures/developers.rb +0 -29
  386. data/features/support/db/fixtures/dogs.rb +0 -3
  387. data/features/support/db/fixtures/extensible_betas.rb +0 -10
  388. data/features/support/db/fixtures/gammas.rb +0 -10
  389. data/features/support/db/fixtures/people.rb +0 -1001
  390. data/features/support/db/fixtures/posts.rb +0 -6
  391. data/features/support/db/fixtures/robots.rb +0 -14
  392. data/features/support/db/fixtures/tags.rb +0 -27
  393. data/features/support/db/fixtures/thetas.rb +0 -10
  394. data/features/support/db/migrations/create_alphas.rb +0 -7
  395. data/features/support/db/migrations/create_animals.rb +0 -5
  396. data/features/support/db/migrations/create_authors.rb +0 -3
  397. data/features/support/db/migrations/create_authors_posts.rb +0 -6
  398. data/features/support/db/migrations/create_betas.rb +0 -5
  399. data/features/support/db/migrations/create_boxes.rb +0 -5
  400. data/features/support/db/migrations/create_categories.rb +0 -3
  401. data/features/support/db/migrations/create_comments.rb +0 -10
  402. data/features/support/db/migrations/create_delayed_betas.rb +0 -17
  403. data/features/support/db/migrations/create_developers.rb +0 -9
  404. data/features/support/db/migrations/create_extensible_betas.rb +0 -5
  405. data/features/support/db/migrations/create_gammas.rb +0 -3
  406. data/features/support/db/migrations/create_people.rb +0 -13
  407. data/features/support/db/migrations/create_posts.rb +0 -5
  408. data/features/support/db/migrations/create_robots.rb +0 -4
  409. data/features/support/db/migrations/create_taggings.rb +0 -5
  410. data/features/support/db/migrations/create_tags.rb +0 -4
  411. data/features/support/db/migrations/create_thetas.rb +0 -5
  412. data/features/support/db/mysql.rb +0 -3
  413. data/features/support/db/postgresql.rb +0 -3
  414. data/features/support/env.rb +0 -18
  415. data/features/support/lib/generic_delta_handler.rb +0 -8
  416. data/features/support/models/alpha.rb +0 -10
  417. data/features/support/models/animal.rb +0 -5
  418. data/features/support/models/author.rb +0 -3
  419. data/features/support/models/beta.rb +0 -8
  420. data/features/support/models/box.rb +0 -8
  421. data/features/support/models/cat.rb +0 -3
  422. data/features/support/models/category.rb +0 -4
  423. data/features/support/models/comment.rb +0 -10
  424. data/features/support/models/delayed_beta.rb +0 -7
  425. data/features/support/models/developer.rb +0 -16
  426. data/features/support/models/dog.rb +0 -3
  427. data/features/support/models/extensible_beta.rb +0 -9
  428. data/features/support/models/gamma.rb +0 -5
  429. data/features/support/models/person.rb +0 -23
  430. data/features/support/models/post.rb +0 -20
  431. data/features/support/models/robot.rb +0 -12
  432. data/features/support/models/tag.rb +0 -3
  433. data/features/support/models/theta.rb +0 -7
  434. data/features/support/post_database.rb +0 -43
  435. data/lib/cucumber/thinking_sphinx/internal_world.rb +0 -125
  436. data/lib/cucumber/thinking_sphinx/sql_logger.rb +0 -20
  437. data/lib/thinking_sphinx/active_record/attribute_updates.rb +0 -48
  438. data/lib/thinking_sphinx/active_record/delta.rb +0 -87
  439. data/lib/thinking_sphinx/active_record/has_many_association.rb +0 -28
  440. data/lib/thinking_sphinx/active_record/scopes.rb +0 -39
  441. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +0 -42
  442. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +0 -54
  443. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +0 -143
  444. data/lib/thinking_sphinx/association.rb +0 -164
  445. data/lib/thinking_sphinx/attribute.rb +0 -341
  446. data/lib/thinking_sphinx/class_facet.rb +0 -15
  447. data/lib/thinking_sphinx/core/array.rb +0 -7
  448. data/lib/thinking_sphinx/core/string.rb +0 -15
  449. data/lib/thinking_sphinx/deltas/datetime_delta.rb +0 -50
  450. data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +0 -24
  451. data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +0 -27
  452. data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +0 -26
  453. data/lib/thinking_sphinx/deltas/delayed_delta.rb +0 -30
  454. data/lib/thinking_sphinx/deploy/capistrano.rb +0 -100
  455. data/lib/thinking_sphinx/field.rb +0 -82
  456. data/lib/thinking_sphinx/index/builder.rb +0 -286
  457. data/lib/thinking_sphinx/index/faux_column.rb +0 -110
  458. data/lib/thinking_sphinx/property.rb +0 -162
  459. data/lib/thinking_sphinx/rails_additions.rb +0 -150
  460. data/lib/thinking_sphinx/search_methods.rb +0 -421
  461. data/lib/thinking_sphinx/source/internal_properties.rb +0 -46
  462. data/lib/thinking_sphinx/source/sql.rb +0 -128
  463. data/lib/thinking_sphinx/source.rb +0 -150
  464. data/rails/init.rb +0 -14
  465. data/spec/lib/thinking_sphinx/active_record/delta_spec.rb +0 -130
  466. data/spec/lib/thinking_sphinx/active_record/has_many_association_spec.rb +0 -49
  467. data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +0 -96
  468. data/spec/lib/thinking_sphinx/active_record_spec.rb +0 -353
  469. data/spec/lib/thinking_sphinx/association_spec.rb +0 -239
  470. data/spec/lib/thinking_sphinx/attribute_spec.rb +0 -507
  471. data/spec/lib/thinking_sphinx/configuration_spec.rb +0 -268
  472. data/spec/lib/thinking_sphinx/core/array_spec.rb +0 -9
  473. data/spec/lib/thinking_sphinx/core/string_spec.rb +0 -9
  474. data/spec/lib/thinking_sphinx/deltas/job_spec.rb +0 -32
  475. data/spec/lib/thinking_sphinx/excerpter_spec.rb +0 -57
  476. data/spec/lib/thinking_sphinx/facet_search_spec.rb +0 -176
  477. data/spec/lib/thinking_sphinx/facet_spec.rb +0 -333
  478. data/spec/lib/thinking_sphinx/field_spec.rb +0 -154
  479. data/spec/lib/thinking_sphinx/index/builder_spec.rb +0 -455
  480. data/spec/lib/thinking_sphinx/index/faux_column_spec.rb +0 -30
  481. data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
  482. data/spec/lib/thinking_sphinx/rails_additions_spec.rb +0 -203
  483. data/spec/lib/thinking_sphinx/search_methods_spec.rb +0 -152
  484. data/spec/lib/thinking_sphinx/search_spec.rb +0 -1101
  485. data/spec/lib/thinking_sphinx/source_spec.rb +0 -227
  486. data/spec/lib/thinking_sphinx_spec.rb +0 -162
  487. data/tasks/distribution.rb +0 -53
  488. data/tasks/rails.rake +0 -1
  489. data/tasks/testing.rb +0 -72
  490. data/vendor/after_commit/LICENSE +0 -20
  491. data/vendor/after_commit/README +0 -16
  492. data/vendor/after_commit/Rakefile +0 -22
  493. data/vendor/after_commit/init.rb +0 -8
  494. data/vendor/after_commit/lib/after_commit/active_record.rb +0 -114
  495. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +0 -103
  496. data/vendor/after_commit/lib/after_commit.rb +0 -45
  497. data/vendor/after_commit/test/after_commit_test.rb +0 -53
  498. data/vendor/delayed_job/lib/delayed/job.rb +0 -251
  499. data/vendor/delayed_job/lib/delayed/message_sending.rb +0 -7
  500. data/vendor/delayed_job/lib/delayed/performable_method.rb +0 -55
  501. data/vendor/delayed_job/lib/delayed/worker.rb +0 -54
  502. data/vendor/riddle/lib/riddle/client/filter.rb +0 -53
  503. data/vendor/riddle/lib/riddle/client/message.rb +0 -66
  504. data/vendor/riddle/lib/riddle/client/response.rb +0 -84
  505. data/vendor/riddle/lib/riddle/client.rb +0 -635
  506. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +0 -48
  507. data/vendor/riddle/lib/riddle/configuration/index.rb +0 -142
  508. data/vendor/riddle/lib/riddle/configuration/indexer.rb +0 -19
  509. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +0 -17
  510. data/vendor/riddle/lib/riddle/configuration/searchd.rb +0 -25
  511. data/vendor/riddle/lib/riddle/configuration/section.rb +0 -43
  512. data/vendor/riddle/lib/riddle/configuration/source.rb +0 -23
  513. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +0 -34
  514. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +0 -28
  515. data/vendor/riddle/lib/riddle/configuration.rb +0 -33
  516. data/vendor/riddle/lib/riddle/controller.rb +0 -53
  517. data/vendor/riddle/lib/riddle.rb +0 -30
@@ -1,708 +1,170 @@
1
- # encoding: UTF-8
2
- module ThinkingSphinx
3
- # Once you've got those indexes in and built, this is the stuff that
4
- # matters - how to search! This class provides a generic search
5
- # interface - which you can use to search all your indexed models at once.
6
- # Most times, you will just want a specific model's results - to search and
7
- # search_for_ids methods will do the job in exactly the same manner when
8
- # called from a model.
9
- #
10
- class Search
11
- CoreMethods = %w( == class class_eval extend frozen? id instance_eval
12
- instance_of? instance_values instance_variable_defined?
13
- instance_variable_get instance_variable_set instance_variables is_a?
14
- kind_of? member? method methods nil? object_id respond_to? send should
15
- type )
16
- SafeMethods = %w( partition private_methods protected_methods
17
- public_methods send )
18
-
19
- instance_methods.select { |method|
20
- method.to_s[/^__/].nil? && !CoreMethods.include?(method.to_s)
21
- }.each { |method|
22
- undef_method method
23
- }
24
-
25
- HashOptions = [:conditions, :with, :without, :with_all]
26
- ArrayOptions = [:classes, :without_ids]
27
-
28
- attr_reader :args, :options
29
-
30
- # Deprecated. Use ThinkingSphinx.search
31
- def self.search(*args)
32
- log 'ThinkingSphinx::Search.search is deprecated. Please use ThinkingSphinx.search instead.'
33
- ThinkingSphinx.search *args
34
- end
35
-
36
- # Deprecated. Use ThinkingSphinx.search_for_ids
37
- def self.search_for_ids(*args)
38
- log 'ThinkingSphinx::Search.search_for_ids is deprecated. Please use ThinkingSphinx.search_for_ids instead.'
39
- ThinkingSphinx.search_for_ids *args
40
- end
41
-
42
- # Deprecated. Use ThinkingSphinx.search_for_ids
43
- def self.search_for_id(*args)
44
- log 'ThinkingSphinx::Search.search_for_id is deprecated. Please use ThinkingSphinx.search_for_id instead.'
45
- ThinkingSphinx.search_for_id *args
46
- end
47
-
48
- # Deprecated. Use ThinkingSphinx.count
49
- def self.count(*args)
50
- log 'ThinkingSphinx::Search.count is deprecated. Please use ThinkingSphinx.count instead.'
51
- ThinkingSphinx.count *args
52
- end
53
-
54
- # Deprecated. Use ThinkingSphinx.facets
55
- def self.facets(*args)
56
- log 'ThinkingSphinx::Search.facets is deprecated. Please use ThinkingSphinx.facets instead.'
57
- ThinkingSphinx.facets *args
58
- end
59
-
60
- def initialize(*args)
61
- @array = []
62
- @options = args.extract_options!
63
- @args = args
64
- end
65
-
66
- def to_a
67
- populate
68
- @array
69
- end
70
-
71
- # Indication of whether the request has been made to Sphinx for the search
72
- # query.
73
- #
74
- # @return [Boolean] true if the results have been requested.
75
- #
76
- def populated?
77
- !!@populated
78
- end
79
-
80
- # The query result hash from Riddle.
81
- #
82
- # @return [Hash] Raw Sphinx results
83
- #
84
- def results
85
- populate
86
- @results
87
- end
88
-
89
- def method_missing(method, *args, &block)
90
- if is_scope?(method)
91
- add_scope(method, *args, &block)
92
- return self
93
- elsif method.to_s[/^each_with_.*/].nil? && !@array.respond_to?(method)
94
- super
95
- elsif !SafeMethods.include?(method.to_s)
96
- populate
97
- end
98
-
99
- if method.to_s[/^each_with_.*/] && !@array.respond_to?(method)
100
- each_with_attribute method.to_s.gsub(/^each_with_/, ''), &block
101
- else
102
- @array.send(method, *args, &block)
103
- end
104
- end
105
-
106
- # Returns true if the Search object or the underlying Array object respond
107
- # to the requested method.
108
- #
109
- # @param [Symbol] method The method name
110
- # @return [Boolean] true if either Search or Array responds to the method.
111
- #
112
- def respond_to?(method)
113
- super || @array.respond_to?(method)
114
- end
115
-
116
- # The current page number of the result set. Defaults to 1 if no page was
117
- # explicitly requested.
118
- #
119
- # @return [Integer]
120
- #
121
- def current_page
122
- @options[:page].blank? ? 1 : @options[:page].to_i
123
- end
124
-
125
- # The next page number of the result set. If there are no more pages
126
- # available, nil is returned.
127
- #
128
- # @return [Integer, nil]
129
- #
130
- def next_page
131
- current_page >= total_pages ? nil : current_page + 1
132
- end
133
-
134
- # The previous page number of the result set. If this is the first page,
135
- # then nil is returned.
136
- #
137
- # @return [Integer, nil]
138
- #
139
- def previous_page
140
- current_page == 1 ? nil : current_page - 1
141
- end
142
-
143
- # The amount of records per set of paged results. Defaults to 20 unless a
144
- # specific page size is requested.
145
- #
146
- # @return [Integer]
147
- #
148
- def per_page
149
- @options[:limit] || @options[:per_page] || 20
150
- end
151
-
152
- # The total number of pages available if the results are paginated.
153
- #
154
- # @return [Integer]
155
- #
156
- def total_pages
157
- populate
158
- @total_pages ||= (@results[:total] / per_page.to_f).ceil
159
- end
160
- # Compatibility with older versions of will_paginate
161
- alias_method :page_count, :total_pages
162
-
163
- # The total number of search results available.
164
- #
165
- # @return [Integer]
166
- #
167
- def total_entries
168
- populate
169
- @total_entries ||= @results[:total_found]
170
- end
171
-
172
- # The current page's offset, based on the number of records per page.
173
- #
174
- # @return [Integer]
175
- #
176
- def offset
177
- (current_page - 1) * per_page
178
- end
179
-
180
- def indexes
181
- return options[:index] if options[:index]
182
- return '*' if classes.empty?
183
-
184
- classes.collect { |klass| klass.sphinx_index_names }.flatten.join(',')
185
- end
186
-
187
- def each_with_groupby_and_count(&block)
188
- populate
189
- results[:matches].each_with_index do |match, index|
190
- yield self[index],
191
- match[:attributes]["@groupby"],
192
- match[:attributes]["@count"]
193
- end
194
- end
195
- alias_method :each_with_group_and_count, :each_with_groupby_and_count
196
-
197
- def each_with_weighting(&block)
198
- populate
199
- results[:matches].each_with_index do |match, index|
200
- yield self[index], match[:weight]
201
- end
202
- end
203
-
204
- def excerpt_for(string, model = nil)
205
- if model.nil? && one_class
206
- model ||= one_class
207
- end
208
-
209
- populate
210
- client.excerpts(
211
- :docs => [string],
212
- :words => results[:words].keys.join(' '),
213
- :index => "#{model.source_of_sphinx_index.sphinx_name}_core"
214
- ).first
215
- end
216
-
217
- def search(*args)
218
- merge_search ThinkingSphinx::Search.new(*args)
219
- self
220
- end
221
-
222
- private
223
-
224
- def config
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Search < Array
4
+ CORE_METHODS = %w( == class class_eval extend frozen? id instance_eval
5
+ instance_exec instance_of? instance_values instance_variable_defined?
6
+ instance_variable_get instance_variable_set instance_variables is_a?
7
+ kind_of? member? method methods nil? object_id respond_to?
8
+ respond_to_missing? send should should_not type )
9
+ SAFE_METHODS = %w( partition private_methods protected_methods public_methods
10
+ send class )
11
+ KNOWN_OPTIONS = (
12
+ [
13
+ :classes, :conditions, :excerpts, :geo, :group_by, :ids_only,
14
+ :ignore_scopes, :indices, :limit, :masks, :max_matches, :middleware,
15
+ :none, :offset, :order, :order_group_by, :page, :per_page, :populate,
16
+ :retry_stale, :select, :skip_sti, :sql, :star, :with, :with_all, :without,
17
+ :without_ids
18
+ ] +
19
+ ThinkingSphinx::Middlewares::SphinxQL::SELECT_OPTIONS
20
+ ).uniq
21
+ DEFAULT_MASKS = [
22
+ ThinkingSphinx::Masks::PaginationMask,
23
+ ThinkingSphinx::Masks::ScopesMask,
24
+ ThinkingSphinx::Masks::GroupEnumeratorsMask
25
+ ]
26
+
27
+ instance_methods.select { |method|
28
+ method.to_s[/^__/].nil? && !CORE_METHODS.include?(method.to_s)
29
+ }.each { |method|
30
+ undef_method method
31
+ }
32
+
33
+ attr_reader :options
34
+ attr_accessor :query
35
+
36
+ def self.valid_options
37
+ @valid_options
38
+ end
39
+
40
+ @valid_options = KNOWN_OPTIONS.dup
41
+
42
+ def initialize(query = nil, options = {})
43
+ query, options = nil, query if query.is_a?(Hash)
44
+ @query, @options = query, options
45
+
46
+ populate if options[:populate]
47
+ end
48
+
49
+ def context
50
+ @context ||= ThinkingSphinx::Search::Context.new self,
225
51
  ThinkingSphinx::Configuration.instance
52
+ end
53
+
54
+ def current_page
55
+ options[:page] = 1 if options[:page].blank?
56
+ options[:page].to_i
57
+ end
58
+
59
+ def marshal_dump
60
+ populate
61
+
62
+ [@populated, @query, @options, @context]
63
+ end
64
+
65
+ def marshal_load(array)
66
+ @populated, @query, @options, @context = array
67
+ end
68
+
69
+ def masks
70
+ @masks ||= @options[:masks] || DEFAULT_MASKS.clone
71
+ end
72
+
73
+ def meta
74
+ populate
75
+ context[:meta]
76
+ end
77
+
78
+ def offset
79
+ @options[:offset] || ((current_page - 1) * per_page)
80
+ end
81
+
82
+ alias_method :offset_value, :offset
83
+
84
+ def per_page(value = nil)
85
+ @options[:limit] = value unless value.nil?
86
+ @options[:limit] ||= (@options[:per_page] || 20)
87
+ @options[:limit].to_i
88
+ end
89
+
90
+ alias_method :limit_value, :per_page
91
+
92
+ def populate
93
+ return self if @populated
94
+
95
+ middleware.call [context] unless options[:none]
96
+ @populated = true
97
+
98
+ self
99
+ end
100
+
101
+ def populated!
102
+ @populated = true
103
+ end
104
+
105
+ def populated?
106
+ @populated
107
+ end
108
+
109
+ def query_time
110
+ meta['time'].to_f
111
+ end
112
+
113
+ def raw
114
+ populate
115
+ context[:raw]
116
+ end
117
+
118
+ def to_a
119
+ populate
120
+ context[:results].collect { |result|
121
+ result.respond_to?(:unglazed) ? result.unglazed : result
122
+ }
123
+ end
124
+
125
+ private
126
+
127
+ def default_middleware
128
+ options[:ids_only] ? ThinkingSphinx::Middlewares::IDS_ONLY :
129
+ ThinkingSphinx::Middlewares::DEFAULT
130
+ end
131
+
132
+ def mask_stack
133
+ @mask_stack ||= masks.collect { |klass| klass.new self }
134
+ end
135
+
136
+ def masks_respond_to?(method)
137
+ mask_stack.any? { |mask| mask.can_handle? method }
138
+ end
139
+
140
+ def method_missing(method, *args, &block)
141
+ mask_stack.each do |mask|
142
+ return mask.send(method, *args, &block) if mask.can_handle?(method)
226
143
  end
227
-
228
- def populate
229
- return if @populated
230
- @populated = true
231
-
232
- retry_on_stale_index do
233
- begin
234
- log "Querying Sphinx: #{query}"
235
- @results = client.query query, indexes, comment
236
- rescue Errno::ECONNREFUSED => err
237
- raise ThinkingSphinx::ConnectionError,
238
- 'Connection to Sphinx Daemon (searchd) failed.'
239
- end
240
-
241
- if options[:ids_only]
242
- replace @results[:matches].collect { |match|
243
- match[:attributes]["sphinx_internal_id"]
244
- }
245
- else
246
- replace instances_from_matches
247
- add_excerpter
248
- add_sphinx_attributes
249
- end
250
- end
251
- end
252
-
253
- def add_excerpter
254
- each do |object|
255
- next if object.respond_to?(:excerpts)
256
-
257
- excerpter = ThinkingSphinx::Excerpter.new self, object
258
- block = lambda { excerpter }
259
-
260
- object.metaclass.instance_eval do
261
- define_method(:excerpts, &block)
262
- end
263
- end
264
- end
265
-
266
- def add_sphinx_attributes
267
- each do |object|
268
- next if object.nil? || object.respond_to?(:sphinx_attributes)
269
-
270
- match = @results[:matches].detect { |match|
271
- match[:attributes]['sphinx_internal_id'] == object.
272
- primary_key_for_sphinx &&
273
- match[:attributes]['class_crc'] == object.class.to_crc32
274
- }
275
- next if match.nil?
276
-
277
- object.metaclass.instance_eval do
278
- define_method(:sphinx_attributes) { match[:attributes] }
279
- end
280
- end
281
- end
282
-
283
- def self.log(message, method = :debug)
284
- return if ::ActiveRecord::Base.logger.nil?
285
- ::ActiveRecord::Base.logger.send method, message
286
- end
287
-
288
- def log(message, method = :debug)
289
- self.class.log(message, method)
290
- end
291
-
292
- def client
293
- client = config.client
294
-
295
- index_options = one_class ?
296
- one_class.sphinx_indexes.first.local_options : {}
297
-
298
- [
299
- :max_matches, :group_by, :group_function, :group_clause,
300
- :group_distinct, :id_range, :cut_off, :retry_count, :retry_delay,
301
- :rank_mode, :max_query_time, :field_weights
302
- ].each do |key|
303
- # puts "key: #{key}"
304
- value = options[key] || index_options[key]
305
- # puts "value: #{value.inspect}"
306
- client.send("#{key}=", value) if value
307
- end
308
-
309
- client.limit = per_page
310
- client.offset = offset
311
- client.match_mode = match_mode
312
- client.filters = filters
313
- client.sort_mode = sort_mode
314
- client.sort_by = sort_by
315
- client.group_by = group_by if group_by
316
- client.group_function = group_function if group_function
317
- client.index_weights = index_weights
318
- client.anchor = anchor
319
-
320
- client
321
- end
322
-
323
- def retry_on_stale_index(&block)
324
- stale_ids = []
325
- retries = stale_retries
326
-
327
- begin
328
- options[:raise_on_stale] = retries > 0
329
- block.call
330
-
331
- # If ThinkingSphinx::Search#instances_from_matches found records in
332
- # Sphinx but not in the DB and the :raise_on_stale option is set, this
333
- # exception is raised. We retry a limited number of times, excluding the
334
- # stale ids from the search.
335
- rescue StaleIdsException => err
336
- retries -= 1
337
-
338
- # For logging
339
- stale_ids |= err.ids
340
- # ID exclusion
341
- options[:without_ids] = Array(options[:without_ids]) | err.ids
342
-
343
- log 'Sphinx Stale Ids (%s %s left): %s' % [
344
- retries, (retries == 1 ? 'try' : 'tries'), stale_ids.join(', ')
345
- ]
346
- retry
347
- end
348
- end
349
-
350
- def classes
351
- @classes ||= options[:classes] || []
352
- end
353
-
354
- def one_class
355
- @one_class ||= classes.length != 1 ? nil : classes.first
356
- end
357
-
358
- def query
359
- @query ||= begin
360
- q = @args.join(' ') << conditions_as_query
361
- (options[:star] ? star_query(q) : q).strip
362
- end
363
- end
364
-
365
- def conditions_as_query
366
- return '' if @options[:conditions].blank?
367
-
368
- # Soon to be deprecated.
369
- keys = @options[:conditions].keys.reject { |key|
370
- attributes.include?(key.to_sym)
371
- }
372
-
373
- ' ' + keys.collect { |key|
374
- "@#{key} #{options[:conditions][key]}"
375
- }.join(' ')
376
- end
377
-
378
- def star_query(query)
379
- token = options[:star].is_a?(Regexp) ? options[:star] : /\w+/u
380
-
381
- query.gsub(/("#{token}(.*?#{token})?"|(?![!-])#{token})/u) do
382
- pre, proper, post = $`, $&, $'
383
- # E.g. "@foo", "/2", "~3", but not as part of a token
384
- is_operator = pre.match(%r{(\W|^)[@~/]\Z})
385
- # E.g. "foo bar", with quotes
386
- is_quote = proper.starts_with?('"') && proper.ends_with?('"')
387
- has_star = pre.ends_with?("*") || post.starts_with?("*")
388
- if is_operator || is_quote || has_star
389
- proper
390
- else
391
- "*#{proper}*"
392
- end
393
- end
394
- end
395
-
396
- def comment
397
- options[:comment] || ''
398
- end
399
-
400
- def match_mode
401
- options[:match_mode] || (options[:conditions].blank? ? :all : :extended)
402
- end
403
-
404
- def sort_mode
405
- @sort_mode ||= case options[:sort_mode]
406
- when :asc
407
- :attr_asc
408
- when :desc
409
- :attr_desc
410
- when nil
411
- case options[:order]
412
- when String
413
- :extended
414
- when Symbol
415
- :attr_asc
416
- else
417
- :relevance
418
- end
419
- else
420
- options[:sort_mode]
421
- end
422
- end
423
-
424
- def sort_by
425
- case @sort_by = (options[:sort_by] || options[:order])
426
- when String
427
- sorted_fields_to_attributes(@sort_by)
428
- when Symbol
429
- field_names.include?(@sort_by) ?
430
- @sort_by.to_s.concat('_sort') : @sort_by.to_s
431
- else
432
- ''
433
- end
434
- end
435
-
436
- def field_names
437
- return [] unless one_class
438
-
439
- one_class.sphinx_indexes.collect { |index|
440
- index.fields.collect { |field| field.unique_name }
441
- }.flatten
442
- end
443
-
444
- def sorted_fields_to_attributes(order_string)
445
- field_names.each { |field|
446
- order_string.gsub!(/(^|\s)#{field}(,?\s|$)/) { |match|
447
- match.gsub field.to_s, field.to_s.concat("_sort")
448
- }
449
- }
450
-
451
- order_string
452
- end
453
-
454
- # Turn :index_weights => { "foo" => 2, User => 1 } into :index_weights =>
455
- # { "foo" => 2, "user_core" => 1, "user_delta" => 1 }
456
- #
457
- def index_weights
458
- weights = options[:index_weights] || {}
459
- weights.keys.inject({}) do |hash, key|
460
- if key.is_a?(Class)
461
- name = ThinkingSphinx::Index.name_for(key)
462
- hash["#{name}_core"] = weights[key]
463
- hash["#{name}_delta"] = weights[key]
464
- else
465
- hash[key] = weights[key]
466
- end
467
-
468
- hash
469
- end
470
- end
471
-
472
- def group_by
473
- options[:group] ? options[:group].to_s : nil
474
- end
475
-
476
- def group_function
477
- options[:group] ? :attr : nil
478
- end
479
-
480
- def internal_filters
481
- filters = [Riddle::Client::Filter.new('sphinx_deleted', [0])]
482
-
483
- class_crcs = classes.collect { |klass|
484
- klass.to_crc32s
485
- }.flatten
486
-
487
- unless class_crcs.empty?
488
- filters << Riddle::Client::Filter.new('class_crc', class_crcs)
489
- end
490
-
491
- filters << Riddle::Client::Filter.new(
492
- 'sphinx_internal_id', filter_value(options[:without_ids]), true
493
- ) if options[:without_ids]
494
-
495
- filters
496
- end
497
-
498
- def condition_filters
499
- (options[:conditions] || {}).collect { |attrib, value|
500
- if attributes.include?(attrib.to_sym)
501
- puts <<-MSG
502
- Deprecation Warning: filters on attributes should be done using the :with
503
- option, not :conditions. For example:
504
- :with => {:#{attrib} => #{value.inspect}}
505
- MSG
506
- Riddle::Client::Filter.new attrib.to_s, filter_value(value)
507
- else
508
- nil
509
- end
510
- }.compact
511
- end
512
-
513
- def filters
514
- internal_filters +
515
- condition_filters +
516
- (options[:with] || {}).collect { |attrib, value|
517
- Riddle::Client::Filter.new attrib.to_s, filter_value(value)
518
- } +
519
- (options[:without] || {}).collect { |attrib, value|
520
- Riddle::Client::Filter.new attrib.to_s, filter_value(value), true
521
- } +
522
- (options[:with_all] || {}).collect { |attrib, values|
523
- Array(values).collect { |value|
524
- Riddle::Client::Filter.new attrib.to_s, filter_value(value)
525
- }
526
- }.flatten
527
- end
528
-
529
- # When passed a Time instance, returns the integer timestamp.
530
- #
531
- # If using Rails 2.1+, need to handle timezones to translate them back to
532
- # UTC, as that's what datetimes will be stored as by MySQL.
533
- #
534
- # in_time_zone is a method that was added for the timezone support in
535
- # Rails 2.1, which is why it's used for testing. I'm sure there's better
536
- # ways, but this does the job.
537
- #
538
- def filter_value(value)
539
- case value
540
- when Range
541
- filter_value(value.first).first..filter_value(value.last).first
542
- when Array
543
- value.collect { |v| filter_value(v) }.flatten
544
- when Time
545
- value.respond_to?(:in_time_zone) ? [value.utc.to_i] : [value.to_i]
546
- when NilClass
547
- 0
548
- else
549
- Array(value)
550
- end
551
- end
552
-
553
- def anchor
554
- return {} unless options[:geo] || (options[:lat] && options[:lng])
555
-
556
- {
557
- :latitude => options[:geo] ? options[:geo].first : options[:lat],
558
- :longitude => options[:geo] ? options[:geo].last : options[:lng],
559
- :latitude_attribute => latitude_attr.to_s,
560
- :longitude_attribute => longitude_attr.to_s
561
- }
562
- end
563
-
564
- def latitude_attr
565
- options[:latitude_attr] ||
566
- index_option(:latitude_attr) ||
567
- attribute(:lat, :latitude)
568
- end
569
-
570
- def longitude_attr
571
- options[:longitude_attr] ||
572
- index_option(:longitude_attr) ||
573
- attribute(:lon, :lng, :longitude)
574
- end
575
-
576
- def index_option(key)
577
- return nil unless one_class
578
-
579
- one_class.sphinx_indexes.collect { |index|
580
- index.local_options[key]
581
- }.compact.first
582
- end
583
-
584
- def attribute(*keys)
585
- return nil unless one_class
586
-
587
- keys.detect { |key|
588
- attributes.include?(key)
589
- }
590
- end
591
-
592
- def attributes
593
- return [] unless one_class
594
-
595
- attributes = one_class.sphinx_indexes.collect { |index|
596
- index.attributes.collect { |attrib| attrib.unique_name }
597
- }.flatten
598
- end
599
-
600
- def stale_retries
601
- case options[:retry_stale]
602
- when TrueClass
603
- 3
604
- when nil, FalseClass
605
- 0
606
- else
607
- options[:retry_stale].to_i
608
- end
609
- end
610
-
611
- def instances_from_class(klass, matches)
612
- index_options = klass.sphinx_index_options
613
-
614
- ids = matches.collect { |match| match[:attributes]["sphinx_internal_id"] }
615
- instances = ids.length > 0 ? klass.find(
616
- :all,
617
- :joins => options[:joins],
618
- :conditions => {klass.primary_key_for_sphinx.to_sym => ids},
619
- :include => (options[:include] || index_options[:include]),
620
- :select => (options[:select] || index_options[:select]),
621
- :order => (options[:sql_order] || index_options[:sql_order])
622
- ) : []
623
-
624
- # Raise an exception if we find records in Sphinx but not in the DB, so
625
- # the search method can retry without them. See
626
- # ThinkingSphinx::Search.retry_search_on_stale_index.
627
- if options[:raise_on_stale] && instances.length < ids.length
628
- stale_ids = ids - instances.map { |i| i.id }
629
- raise StaleIdsException, stale_ids
630
- end
631
-
632
- # if the user has specified an SQL order, return the collection
633
- # without rearranging it into the Sphinx order
634
- return instances if (options[:sql_order] || index_options[:sql_order])
635
-
636
- ids.collect { |obj_id|
637
- instances.detect do |obj|
638
- obj.primary_key_for_sphinx == obj_id
639
- end
640
- }
641
- end
642
-
643
- # Group results by class and call #find(:all) once for each group to reduce
644
- # the number of #find's in multi-model searches.
645
- #
646
- def instances_from_matches
647
- return single_class_results if one_class
648
-
649
- groups = results[:matches].group_by { |match|
650
- match[:attributes]["class_crc"]
651
- }
652
- groups.each do |crc, group|
653
- group.replace(
654
- instances_from_class(class_from_crc(crc), group)
655
- )
656
- end
657
-
658
- results[:matches].collect do |match|
659
- groups.detect { |crc, group|
660
- crc == match[:attributes]["class_crc"]
661
- }[1].compact.detect { |obj|
662
- obj.primary_key_for_sphinx == match[:attributes]["sphinx_internal_id"]
663
- }
664
- end
665
- end
666
-
667
- def single_class_results
668
- instances_from_class one_class, results[:matches]
669
- end
670
-
671
- def class_from_crc(crc)
672
- config.models_by_crc[crc].constantize
673
- end
674
-
675
- def each_with_attribute(attribute, &block)
676
- populate
677
- results[:matches].each_with_index do |match, index|
678
- yield self[index],
679
- (match[:attributes][attribute] || match[:attributes]["@#{attribute}"])
680
- end
681
- end
682
-
683
- def is_scope?(method)
684
- one_class && one_class.sphinx_scopes.include?(method)
685
- end
686
-
687
- def add_scope(method, *args, &block)
688
- merge_search one_class.send(method, *args, &block)
689
- end
690
-
691
- def merge_search(search)
692
- search.args.each { |arg| args << arg }
693
-
694
- search.options.keys.each do |key|
695
- if HashOptions.include?(key)
696
- options[key] ||= {}
697
- options[key].merge! search.options[key]
698
- elsif ArrayOptions.include?(key)
699
- options[key] ||= []
700
- options[key] += search.options[key]
701
- options[key].uniq!
702
- else
703
- options[key] = search.options[key]
704
- end
705
- end
706
- end
144
+
145
+ populate if !SAFE_METHODS.include?(method.to_s)
146
+
147
+ context[:results].send(method, *args, &block)
148
+ end
149
+
150
+ def respond_to_missing?(method, include_private = false)
151
+ super ||
152
+ masks_respond_to?(method) ||
153
+ results_respond_to?(method, include_private)
154
+ end
155
+
156
+ def middleware
157
+ @options[:middleware] || default_middleware
158
+ end
159
+
160
+ def results_respond_to?(method, include_private = true)
161
+ context[:results].respond_to?(method, include_private)
707
162
  end
708
163
  end
164
+
165
+ require 'thinking_sphinx/search/batch_inquirer'
166
+ require 'thinking_sphinx/search/context'
167
+ require 'thinking_sphinx/search/glaze'
168
+ require 'thinking_sphinx/search/merger'
169
+ require 'thinking_sphinx/search/query'
170
+ require 'thinking_sphinx/search/stale_ids_exception'