thinking-sphinx 3.1.4 → 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 (347) hide show
  1. checksums.yaml +5 -5
  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 +7 -2
  6. data/.travis.yml +31 -16
  7. data/Appraisals +68 -12
  8. data/CHANGELOG.markdown +782 -0
  9. data/Gemfile +13 -4
  10. data/Procfile.support +2 -0
  11. data/README.textile +41 -27
  12. data/Rakefile +2 -0
  13. data/bin/console +15 -0
  14. data/bin/loadsphinx +99 -0
  15. data/bin/testmatrix +48 -0
  16. data/lib/thinking/sphinx.rb +2 -0
  17. data/lib/thinking-sphinx.rb +2 -0
  18. data/lib/thinking_sphinx/active_record/association.rb +2 -0
  19. data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +3 -1
  20. data/lib/thinking_sphinx/active_record/association_proxy/attribute_matcher.rb +2 -0
  21. data/lib/thinking_sphinx/active_record/association_proxy.rb +3 -2
  22. data/lib/thinking_sphinx/active_record/attribute/sphinx_presenter.rb +3 -1
  23. data/lib/thinking_sphinx/active_record/attribute/type.rb +20 -4
  24. data/lib/thinking_sphinx/active_record/attribute/values.rb +2 -0
  25. data/lib/thinking_sphinx/active_record/attribute.rb +2 -0
  26. data/lib/thinking_sphinx/active_record/base.rb +43 -11
  27. data/lib/thinking_sphinx/active_record/callbacks/association_delta_callbacks.rb +21 -0
  28. data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +15 -9
  29. data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +20 -5
  30. data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +18 -4
  31. data/lib/thinking_sphinx/active_record/column.rb +2 -0
  32. data/lib/thinking_sphinx/active_record/column_sql_presenter.rb +7 -1
  33. data/lib/thinking_sphinx/active_record/database_adapters/abstract_adapter.rb +2 -0
  34. data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +9 -1
  35. data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +2 -0
  36. data/lib/thinking_sphinx/active_record/database_adapters.rb +3 -1
  37. data/lib/thinking_sphinx/active_record/depolymorph/association_reflection.rb +32 -0
  38. data/lib/thinking_sphinx/active_record/depolymorph/base_reflection.rb +32 -0
  39. data/lib/thinking_sphinx/active_record/depolymorph/conditions_reflection.rb +40 -0
  40. data/lib/thinking_sphinx/active_record/depolymorph/overridden_reflection.rb +50 -0
  41. data/lib/thinking_sphinx/active_record/depolymorph/scoped_reflection.rb +26 -0
  42. data/lib/thinking_sphinx/active_record/field.rb +2 -0
  43. data/lib/thinking_sphinx/active_record/filter_reflection.rb +13 -71
  44. data/lib/thinking_sphinx/active_record/index.rb +7 -5
  45. data/lib/thinking_sphinx/active_record/interpreter.rb +6 -4
  46. data/lib/thinking_sphinx/active_record/join_association.rb +5 -1
  47. data/lib/thinking_sphinx/active_record/log_subscriber.rb +22 -3
  48. data/lib/thinking_sphinx/active_record/polymorpher.rb +2 -0
  49. data/lib/thinking_sphinx/active_record/property.rb +2 -0
  50. data/lib/thinking_sphinx/active_record/property_query.rb +4 -1
  51. data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +2 -0
  52. data/lib/thinking_sphinx/active_record/simple_many_query.rb +2 -0
  53. data/lib/thinking_sphinx/active_record/source_joins.rb +68 -0
  54. data/lib/thinking_sphinx/active_record/sql_builder/clause_builder.rb +2 -0
  55. data/lib/thinking_sphinx/active_record/sql_builder/query.rb +13 -0
  56. data/lib/thinking_sphinx/active_record/sql_builder/statement.rb +2 -0
  57. data/lib/thinking_sphinx/active_record/sql_builder.rb +5 -18
  58. data/lib/thinking_sphinx/active_record/sql_source/template.rb +5 -3
  59. data/lib/thinking_sphinx/active_record/sql_source.rb +35 -9
  60. data/lib/thinking_sphinx/active_record.rb +14 -1
  61. data/lib/thinking_sphinx/attribute_types.rb +72 -0
  62. data/lib/thinking_sphinx/batched_search.rb +2 -0
  63. data/lib/thinking_sphinx/callbacks/appender.rb +63 -0
  64. data/lib/thinking_sphinx/callbacks.rb +29 -0
  65. data/lib/thinking_sphinx/capistrano/v2.rb +2 -0
  66. data/lib/thinking_sphinx/capistrano/v3.rb +2 -0
  67. data/lib/thinking_sphinx/capistrano.rb +2 -0
  68. data/lib/thinking_sphinx/commander.rb +27 -0
  69. data/lib/thinking_sphinx/commands/base.rb +53 -0
  70. data/lib/thinking_sphinx/commands/clear_real_time.rb +22 -0
  71. data/lib/thinking_sphinx/commands/clear_sql.rb +18 -0
  72. data/lib/thinking_sphinx/commands/configure.rb +15 -0
  73. data/lib/thinking_sphinx/commands/index_real_time.rb +15 -0
  74. data/lib/thinking_sphinx/commands/index_sql.rb +25 -0
  75. data/lib/thinking_sphinx/commands/merge.rb +27 -0
  76. data/lib/thinking_sphinx/commands/merge_and_update.rb +57 -0
  77. data/lib/thinking_sphinx/commands/prepare.rb +15 -0
  78. data/lib/thinking_sphinx/commands/rotate.rb +13 -0
  79. data/lib/thinking_sphinx/commands/running.rb +15 -0
  80. data/lib/thinking_sphinx/commands/start_attached.rb +22 -0
  81. data/lib/thinking_sphinx/commands/start_detached.rb +21 -0
  82. data/lib/thinking_sphinx/commands/stop.rb +24 -0
  83. data/lib/thinking_sphinx/commands.rb +20 -0
  84. data/lib/thinking_sphinx/configuration/consistent_ids.rb +2 -0
  85. data/lib/thinking_sphinx/configuration/defaults.rb +2 -0
  86. data/lib/thinking_sphinx/configuration/distributed_indices.rb +3 -1
  87. data/lib/thinking_sphinx/configuration/duplicate_names.rb +36 -0
  88. data/lib/thinking_sphinx/configuration/minimum_fields.rb +17 -12
  89. data/lib/thinking_sphinx/configuration.rb +90 -74
  90. data/lib/thinking_sphinx/connection/client.rb +74 -0
  91. data/lib/thinking_sphinx/connection/jruby.rb +58 -0
  92. data/lib/thinking_sphinx/connection/mri.rb +26 -0
  93. data/lib/thinking_sphinx/connection.rb +13 -114
  94. data/lib/thinking_sphinx/core/field.rb +2 -0
  95. data/lib/thinking_sphinx/core/index.rb +22 -2
  96. data/lib/thinking_sphinx/core/interpreter.rb +2 -0
  97. data/lib/thinking_sphinx/core/property.rb +2 -0
  98. data/lib/thinking_sphinx/core/settings.rb +2 -0
  99. data/lib/thinking_sphinx/core.rb +2 -0
  100. data/lib/thinking_sphinx/deletion.rb +33 -13
  101. data/lib/thinking_sphinx/deltas/default_delta.rb +3 -1
  102. data/lib/thinking_sphinx/deltas/delete_job.rb +16 -4
  103. data/lib/thinking_sphinx/deltas/index_job.rb +14 -2
  104. data/lib/thinking_sphinx/deltas.rb +2 -0
  105. data/lib/thinking_sphinx/distributed/index.rb +25 -3
  106. data/lib/thinking_sphinx/distributed.rb +2 -0
  107. data/lib/thinking_sphinx/errors.rb +47 -3
  108. data/lib/thinking_sphinx/excerpter.rb +2 -0
  109. data/lib/thinking_sphinx/facet.rb +4 -3
  110. data/lib/thinking_sphinx/facet_search.rb +4 -2
  111. data/lib/thinking_sphinx/float_formatter.rb +2 -0
  112. data/lib/thinking_sphinx/frameworks/plain.rb +2 -0
  113. data/lib/thinking_sphinx/frameworks/rails.rb +2 -0
  114. data/lib/thinking_sphinx/frameworks.rb +2 -0
  115. data/lib/thinking_sphinx/guard/file.rb +3 -1
  116. data/lib/thinking_sphinx/guard/files.rb +2 -0
  117. data/lib/thinking_sphinx/guard/none.rb +7 -0
  118. data/lib/thinking_sphinx/guard.rb +3 -0
  119. data/lib/thinking_sphinx/hooks/guard_presence.rb +34 -0
  120. data/lib/thinking_sphinx/index.rb +2 -0
  121. data/lib/thinking_sphinx/index_set.rb +27 -4
  122. data/lib/thinking_sphinx/indexing_strategies/all_at_once.rb +9 -0
  123. data/lib/thinking_sphinx/indexing_strategies/one_at_a_time.rb +16 -0
  124. data/lib/thinking_sphinx/interfaces/base.rb +13 -0
  125. data/lib/thinking_sphinx/interfaces/daemon.rb +27 -0
  126. data/lib/thinking_sphinx/interfaces/real_time.rb +46 -0
  127. data/lib/thinking_sphinx/interfaces/sql.rb +53 -0
  128. data/lib/thinking_sphinx/interfaces.rb +10 -0
  129. data/lib/thinking_sphinx/logger.rb +2 -0
  130. data/lib/thinking_sphinx/masks/group_enumerators_mask.rb +6 -4
  131. data/lib/thinking_sphinx/masks/pagination_mask.rb +2 -0
  132. data/lib/thinking_sphinx/masks/scopes_mask.rb +8 -0
  133. data/lib/thinking_sphinx/masks/weight_enumerator_mask.rb +3 -1
  134. data/lib/thinking_sphinx/masks.rb +2 -0
  135. data/lib/thinking_sphinx/middlewares/active_record_translator.rb +32 -9
  136. data/lib/thinking_sphinx/middlewares/geographer.rb +2 -0
  137. data/lib/thinking_sphinx/middlewares/glazier.rb +14 -1
  138. data/lib/thinking_sphinx/middlewares/ids_only.rb +2 -0
  139. data/lib/thinking_sphinx/middlewares/inquirer.rb +3 -1
  140. data/lib/thinking_sphinx/middlewares/middleware.rb +2 -0
  141. data/lib/thinking_sphinx/middlewares/sphinxql.rb +18 -16
  142. data/lib/thinking_sphinx/middlewares/stale_id_checker.rb +3 -1
  143. data/lib/thinking_sphinx/middlewares/stale_id_filter.rb +4 -2
  144. data/lib/thinking_sphinx/middlewares/valid_options.rb +25 -0
  145. data/lib/thinking_sphinx/middlewares.rb +7 -3
  146. data/lib/thinking_sphinx/panes/attributes_pane.rb +2 -0
  147. data/lib/thinking_sphinx/panes/distance_pane.rb +2 -0
  148. data/lib/thinking_sphinx/panes/excerpts_pane.rb +2 -0
  149. data/lib/thinking_sphinx/panes/weight_pane.rb +3 -1
  150. data/lib/thinking_sphinx/panes.rb +2 -0
  151. data/lib/thinking_sphinx/processor.rb +71 -0
  152. data/lib/thinking_sphinx/query.rb +2 -0
  153. data/lib/thinking_sphinx/railtie.rb +29 -2
  154. data/lib/thinking_sphinx/rake_interface.rb +14 -69
  155. data/lib/thinking_sphinx/real_time/attribute.rb +9 -1
  156. data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +17 -7
  157. data/lib/thinking_sphinx/real_time/field.rb +2 -0
  158. data/lib/thinking_sphinx/real_time/index/template.rb +19 -1
  159. data/lib/thinking_sphinx/real_time/index.rb +11 -5
  160. data/lib/thinking_sphinx/real_time/interpreter.rb +10 -6
  161. data/lib/thinking_sphinx/real_time/populator.rb +12 -14
  162. data/lib/thinking_sphinx/real_time/processor.rb +36 -0
  163. data/lib/thinking_sphinx/real_time/property.rb +3 -5
  164. data/lib/thinking_sphinx/real_time/transcribe_instance.rb +38 -0
  165. data/lib/thinking_sphinx/real_time/transcriber.rb +63 -15
  166. data/lib/thinking_sphinx/real_time/translator.rb +39 -0
  167. data/lib/thinking_sphinx/real_time.rb +22 -1
  168. data/lib/thinking_sphinx/scopes.rb +6 -0
  169. data/lib/thinking_sphinx/search/batch_inquirer.rb +2 -0
  170. data/lib/thinking_sphinx/search/context.rb +11 -0
  171. data/lib/thinking_sphinx/search/glaze.rb +2 -0
  172. data/lib/thinking_sphinx/search/merger.rb +2 -0
  173. data/lib/thinking_sphinx/search/query.rb +9 -1
  174. data/lib/thinking_sphinx/search/stale_ids_exception.rb +7 -3
  175. data/lib/thinking_sphinx/search.rb +30 -2
  176. data/lib/thinking_sphinx/settings.rb +128 -0
  177. data/lib/thinking_sphinx/sinatra.rb +3 -1
  178. data/lib/thinking_sphinx/subscribers/populator_subscriber.rb +19 -5
  179. data/lib/thinking_sphinx/tasks.rb +55 -28
  180. data/lib/thinking_sphinx/test.rb +3 -1
  181. data/lib/thinking_sphinx/utf8.rb +2 -0
  182. data/lib/thinking_sphinx/wildcard.rb +3 -1
  183. data/lib/thinking_sphinx/with_output.rb +13 -0
  184. data/lib/thinking_sphinx.rb +35 -4
  185. data/spec/acceptance/association_scoping_spec.rb +7 -5
  186. data/spec/acceptance/attribute_access_spec.rb +29 -12
  187. data/spec/acceptance/attribute_updates_spec.rb +5 -3
  188. data/spec/acceptance/batch_searching_spec.rb +6 -4
  189. data/spec/acceptance/big_integers_spec.rb +9 -7
  190. data/spec/acceptance/excerpts_spec.rb +11 -10
  191. data/spec/acceptance/facets_spec.rb +29 -16
  192. data/spec/acceptance/geosearching_spec.rb +22 -10
  193. data/spec/acceptance/grouping_by_attributes_spec.rb +30 -28
  194. data/spec/acceptance/index_options_spec.rb +18 -16
  195. data/spec/acceptance/indexing_spec.rb +5 -3
  196. data/spec/acceptance/merging_spec.rb +90 -0
  197. data/spec/acceptance/paginating_search_results_spec.rb +21 -3
  198. data/spec/acceptance/real_time_updates_spec.rb +103 -5
  199. data/spec/acceptance/remove_deleted_records_spec.rb +64 -8
  200. data/spec/acceptance/search_counts_spec.rb +4 -2
  201. data/spec/acceptance/search_for_just_ids_spec.rb +4 -2
  202. data/spec/acceptance/searching_across_models_spec.rb +15 -6
  203. data/spec/acceptance/searching_across_schemas_spec.rb +12 -10
  204. data/spec/acceptance/searching_on_fields_spec.rb +11 -9
  205. data/spec/acceptance/searching_with_filters_spec.rb +35 -20
  206. data/spec/acceptance/searching_with_sti_spec.rb +11 -9
  207. data/spec/acceptance/searching_within_a_model_spec.rb +44 -17
  208. data/spec/acceptance/sorting_search_results_spec.rb +21 -19
  209. data/spec/acceptance/spec_helper.rb +2 -13
  210. data/spec/acceptance/specifying_sql_spec.rb +118 -62
  211. data/spec/acceptance/sphinx_scopes_spec.rb +30 -21
  212. data/spec/acceptance/sql_deltas_spec.rb +35 -9
  213. data/spec/acceptance/support/database_cleaner.rb +3 -1
  214. data/spec/acceptance/support/sphinx_controller.rb +25 -9
  215. data/spec/acceptance/support/sphinx_helpers.rb +17 -2
  216. data/spec/acceptance/suspended_deltas_spec.rb +14 -12
  217. data/spec/fixtures/database.yml +1 -1
  218. data/spec/internal/app/indices/admin_person_index.rb +6 -0
  219. data/spec/internal/app/indices/album_index.rb +9 -0
  220. data/spec/internal/app/indices/animal_index.rb +2 -0
  221. data/spec/internal/app/indices/article_index.rb +10 -3
  222. data/spec/internal/app/indices/bird_index.rb +2 -0
  223. data/spec/internal/app/indices/book_index.rb +4 -1
  224. data/spec/internal/app/indices/car_index.rb +2 -0
  225. data/spec/internal/app/indices/city_index.rb +2 -0
  226. data/spec/internal/app/indices/colour_index.rb +7 -0
  227. data/spec/internal/app/indices/product_index.rb +3 -2
  228. data/spec/internal/app/indices/tee_index.rb +3 -1
  229. data/spec/internal/app/indices/user_index.rb +2 -0
  230. data/spec/internal/app/models/admin/person.rb +6 -0
  231. data/spec/internal/app/models/album.rb +25 -0
  232. data/spec/internal/app/models/animal.rb +3 -0
  233. data/spec/internal/app/models/article.rb +4 -0
  234. data/spec/internal/app/models/bird.rb +3 -0
  235. data/spec/internal/app/models/book.rb +9 -5
  236. data/spec/internal/app/models/car.rb +3 -1
  237. data/spec/internal/app/models/categorisation.rb +10 -1
  238. data/spec/internal/app/models/category.rb +2 -0
  239. data/spec/internal/app/models/city.rb +4 -0
  240. data/spec/internal/app/models/colour.rb +4 -0
  241. data/spec/internal/app/models/event.rb +2 -0
  242. data/spec/internal/app/models/flightless_bird.rb +2 -0
  243. data/spec/internal/app/models/genre.rb +2 -0
  244. data/spec/internal/app/models/hardcover.rb +2 -0
  245. data/spec/internal/app/models/mammal.rb +2 -0
  246. data/spec/internal/app/models/manufacturer.rb +2 -0
  247. data/spec/internal/app/models/product.rb +3 -1
  248. data/spec/internal/app/models/tag.rb +2 -0
  249. data/spec/internal/app/models/tagging.rb +2 -0
  250. data/spec/internal/app/models/tee.rb +7 -0
  251. data/spec/internal/app/models/tweet.rb +2 -0
  252. data/spec/internal/app/models/user.rb +4 -0
  253. data/spec/internal/config/database.yml +12 -1
  254. data/spec/internal/db/schema.rb +13 -1
  255. data/spec/spec_helper.rb +12 -1
  256. data/spec/support/json_column.rb +35 -0
  257. data/spec/support/multi_schema.rb +5 -1
  258. data/spec/support/mysql.rb +25 -0
  259. data/spec/support/sphinx_yaml_helpers.rb +8 -1
  260. data/spec/thinking_sphinx/active_record/association_spec.rb +3 -1
  261. data/spec/thinking_sphinx/active_record/attribute/type_spec.rb +43 -38
  262. data/spec/thinking_sphinx/active_record/base_spec.rb +31 -29
  263. data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +81 -10
  264. data/spec/thinking_sphinx/active_record/callbacks/delta_callbacks_spec.rb +56 -29
  265. data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +27 -10
  266. data/spec/thinking_sphinx/active_record/column_spec.rb +15 -13
  267. data/spec/thinking_sphinx/active_record/column_sql_presenter_spec.rb +39 -0
  268. data/spec/thinking_sphinx/active_record/database_adapters/abstract_adapter_spec.rb +7 -5
  269. data/spec/thinking_sphinx/active_record/database_adapters/mysql_adapter_spec.rb +26 -12
  270. data/spec/thinking_sphinx/active_record/database_adapters/postgresql_adapter_spec.rb +15 -13
  271. data/spec/thinking_sphinx/active_record/database_adapters_spec.rb +50 -48
  272. data/spec/thinking_sphinx/active_record/field_spec.rb +9 -7
  273. data/spec/thinking_sphinx/active_record/filter_reflection_spec.rb +113 -80
  274. data/spec/thinking_sphinx/active_record/index_spec.rb +41 -37
  275. data/spec/thinking_sphinx/active_record/interpreter_spec.rb +62 -59
  276. data/spec/thinking_sphinx/active_record/polymorpher_spec.rb +16 -14
  277. data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +69 -67
  278. data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +146 -154
  279. data/spec/thinking_sphinx/active_record/sql_source_spec.rb +185 -109
  280. data/spec/thinking_sphinx/attribute_types_spec.rb +52 -0
  281. data/spec/thinking_sphinx/commands/clear_real_time_spec.rb +46 -0
  282. data/spec/thinking_sphinx/commands/clear_sql_spec.rb +52 -0
  283. data/spec/thinking_sphinx/commands/configure_spec.rb +31 -0
  284. data/spec/thinking_sphinx/commands/index_real_time_spec.rb +33 -0
  285. data/spec/thinking_sphinx/commands/index_sql_spec.rb +86 -0
  286. data/spec/thinking_sphinx/commands/merge_and_update_spec.rb +106 -0
  287. data/spec/thinking_sphinx/commands/merge_spec.rb +48 -0
  288. data/spec/thinking_sphinx/commands/prepare_spec.rb +31 -0
  289. data/spec/thinking_sphinx/commands/running_spec.rb +30 -0
  290. data/spec/thinking_sphinx/commands/start_detached_spec.rb +67 -0
  291. data/spec/thinking_sphinx/commands/stop_spec.rb +63 -0
  292. data/spec/thinking_sphinx/configuration/minimum_fields_spec.rb +60 -0
  293. data/spec/thinking_sphinx/configuration_spec.rb +247 -94
  294. data/spec/thinking_sphinx/connection/mri_spec.rb +49 -0
  295. data/spec/thinking_sphinx/connection_spec.rb +15 -13
  296. data/spec/thinking_sphinx/deletion_spec.rb +17 -17
  297. data/spec/thinking_sphinx/deltas/default_delta_spec.rb +28 -21
  298. data/spec/thinking_sphinx/deltas_spec.rb +14 -12
  299. data/spec/thinking_sphinx/errors_spec.rb +59 -27
  300. data/spec/thinking_sphinx/excerpter_spec.rb +12 -9
  301. data/spec/thinking_sphinx/facet_search_spec.rb +24 -22
  302. data/spec/thinking_sphinx/hooks/guard_presence_spec.rb +30 -0
  303. data/spec/thinking_sphinx/index_set_spec.rb +59 -18
  304. data/spec/thinking_sphinx/index_spec.rb +28 -26
  305. data/spec/thinking_sphinx/interfaces/daemon_spec.rb +60 -0
  306. data/spec/thinking_sphinx/interfaces/real_time_spec.rb +109 -0
  307. data/spec/thinking_sphinx/interfaces/sql_spec.rb +122 -0
  308. data/spec/thinking_sphinx/masks/pagination_mask_spec.rb +22 -20
  309. data/spec/thinking_sphinx/masks/scopes_mask_spec.rb +20 -18
  310. data/spec/thinking_sphinx/middlewares/active_record_translator_spec.rb +45 -21
  311. data/spec/thinking_sphinx/middlewares/geographer_spec.rb +11 -9
  312. data/spec/thinking_sphinx/middlewares/glazier_spec.rb +12 -9
  313. data/spec/thinking_sphinx/middlewares/inquirer_spec.rb +9 -7
  314. data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +60 -58
  315. data/spec/thinking_sphinx/middlewares/stale_id_checker_spec.rb +7 -4
  316. data/spec/thinking_sphinx/middlewares/stale_id_filter_spec.rb +39 -17
  317. data/spec/thinking_sphinx/middlewares/valid_options_spec.rb +51 -0
  318. data/spec/thinking_sphinx/panes/attributes_pane_spec.rb +3 -1
  319. data/spec/thinking_sphinx/panes/distance_pane_spec.rb +6 -4
  320. data/spec/thinking_sphinx/panes/excerpts_pane_spec.rb +8 -6
  321. data/spec/thinking_sphinx/panes/weight_pane_spec.rb +4 -2
  322. data/spec/thinking_sphinx/rake_interface_spec.rb +17 -235
  323. data/spec/thinking_sphinx/real_time/attribute_spec.rb +15 -13
  324. data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +114 -28
  325. data/spec/thinking_sphinx/real_time/field_spec.rb +16 -14
  326. data/spec/thinking_sphinx/real_time/index_spec.rb +83 -32
  327. data/spec/thinking_sphinx/real_time/interpreter_spec.rb +44 -42
  328. data/spec/thinking_sphinx/real_time/transcribe_instance_spec.rb +35 -0
  329. data/spec/thinking_sphinx/real_time/transcriber_spec.rb +109 -0
  330. data/spec/thinking_sphinx/real_time/translator_spec.rb +17 -0
  331. data/spec/thinking_sphinx/scopes_spec.rb +11 -5
  332. data/spec/thinking_sphinx/search/glaze_spec.rb +17 -15
  333. data/spec/thinking_sphinx/search/query_spec.rb +19 -10
  334. data/spec/thinking_sphinx/search_spec.rb +30 -28
  335. data/spec/thinking_sphinx/wildcard_spec.rb +18 -11
  336. data/spec/thinking_sphinx_spec.rb +10 -7
  337. data/thinking-sphinx.gemspec +17 -10
  338. metadata +157 -49
  339. data/HISTORY +0 -248
  340. data/gemfiles/.gitignore +0 -1
  341. data/gemfiles/rails_3_2.gemfile +0 -11
  342. data/gemfiles/rails_4_0.gemfile +0 -11
  343. data/gemfiles/rails_4_1.gemfile +0 -11
  344. data/gemfiles/rails_4_2.gemfile +0 -11
  345. data/lib/thinking_sphinx/controller.rb +0 -10
  346. data/lib/thinking_sphinx/middlewares/utf8.rb +0 -27
  347. data/lib/thinking_sphinx/sphinxql.rb +0 -23
@@ -1,10 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks <
2
4
  ThinkingSphinx::Callbacks
3
5
 
6
+ if ActiveRecord::Base.instance_methods.grep(/saved_changes/).any?
7
+ CHANGED_ATTRIBUTES = lambda { |instance| instance.saved_changes.keys }
8
+ else
9
+ CHANGED_ATTRIBUTES = lambda { |instance| instance.changed }
10
+ end
11
+
4
12
  callbacks :after_update
5
13
 
6
14
  def after_update
7
- return unless updates_enabled?
15
+ return unless !ThinkingSphinx::Callbacks.suspended? && updates_enabled?
8
16
 
9
17
  indices.each do |index|
10
18
  update index unless index.distributed?
@@ -15,7 +23,7 @@ class ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks <
15
23
 
16
24
  def attributes_hash_for(index)
17
25
  updateable_attributes_for(index).inject({}) do |hash, attribute|
18
- if instance.changed.include?(attribute.columns.first.__name.to_s)
26
+ if changed_attributes.include?(attribute.columns.first.__name.to_s)
19
27
  hash[attribute.name] = attribute.value_for(instance)
20
28
  end
21
29
 
@@ -23,6 +31,10 @@ class ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks <
23
31
  end
24
32
  end
25
33
 
34
+ def changed_attributes
35
+ @changed_attributes ||= CHANGED_ATTRIBUTES.call instance
36
+ end
37
+
26
38
  def configuration
27
39
  ThinkingSphinx::Configuration.instance
28
40
  end
@@ -35,7 +47,7 @@ class ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks <
35
47
  end
36
48
 
37
49
  def reference
38
- instance.class.name.underscore.to_sym
50
+ configuration.index_set_class.reference_name(instance.class)
39
51
  end
40
52
 
41
53
  def update(index)
@@ -43,7 +55,9 @@ class ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks <
43
55
  return if attributes.empty?
44
56
 
45
57
  sphinxql = Riddle::Query.update(
46
- index.name, index.document_id_for_key(instance.id), attributes
58
+ index.name,
59
+ index.document_id_for_key(instance.public_send(index.primary_key)),
60
+ attributes
47
61
  )
48
62
  ThinkingSphinx::Connection.take do |connection|
49
63
  connection.execute(sphinxql)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::Column
2
4
  def initialize(*stack)
3
5
  @stack = stack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::ColumnSQLPresenter
2
4
  def initialize(model, column, adapter, associations)
3
5
  @model, @column, @adapter, @associations = model, column, adapter, associations
@@ -13,7 +15,7 @@ class ThinkingSphinx::ActiveRecord::ColumnSQLPresenter
13
15
  return __name if string?
14
16
  return nil unless exists?
15
17
 
16
- quoted_table = escape_table? ? adapter.quote(table) : table
18
+ quoted_table = escape_table? ? escape_table(table) : table
17
19
 
18
20
  "#{quoted_table}.#{adapter.quote __name}"
19
21
  end
@@ -24,6 +26,10 @@ class ThinkingSphinx::ActiveRecord::ColumnSQLPresenter
24
26
 
25
27
  delegate :__stack, :__name, :string?, :to => :column
26
28
 
29
+ def escape_table(table_name)
30
+ table_name.split('.').map { |t| adapter.quote(t) }.join('.')
31
+ end
32
+
27
33
  def escape_table?
28
34
  table[/[`"]/].nil?
29
35
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::DatabaseAdapters::AbstractAdapter
2
4
  def initialize(model)
3
5
  @model = model
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter <
2
4
  ThinkingSphinx::ActiveRecord::DatabaseAdapters::AbstractAdapter
3
5
 
@@ -38,6 +40,12 @@ class ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter <
38
40
  end
39
41
 
40
42
  def utf8_query_pre
41
- ['SET NAMES utf8']
43
+ ["SET NAMES #{settings['mysql_encoding']}"]
44
+ end
45
+
46
+ private
47
+
48
+ def settings
49
+ ThinkingSphinx::Configuration.instance.settings
42
50
  end
43
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::DatabaseAdapters::PostgreSQLAdapter <
2
4
  ThinkingSphinx::ActiveRecord::DatabaseAdapters::AbstractAdapter
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::ActiveRecord::DatabaseAdapters
2
4
  class << self
3
5
  attr_accessor :default
@@ -12,7 +14,7 @@ module ThinkingSphinx::ActiveRecord::DatabaseAdapters
12
14
  when :postgresql
13
15
  PostgreSQLAdapter
14
16
  else
15
- raise "Invalid Database Adapter '#{adapter}': Thinking Sphinx only supports MySQL and PostgreSQL."
17
+ raise ThinkingSphinx::InvalidDatabaseAdapter, "Invalid adapter '#{adapter}': Thinking Sphinx only supports MySQL and PostgreSQL."
16
18
  end
17
19
 
18
20
  klass.new model
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This custom association approach is only available in Rails 4.1-5.1. This
4
+ # behaviour is superseded by OverriddenReflection for Rails 5.2, and was
5
+ # preceded by ScopedReflection for Rails 4.0.
6
+ class ThinkingSphinx::ActiveRecord::Depolymorph::AssociationReflection <
7
+ ThinkingSphinx::ActiveRecord::Depolymorph::BaseReflection
8
+
9
+ # Since Rails 4.2, the macro argument has been removed. The underlying
10
+ # behaviour remains the same, though.
11
+ def call
12
+ if explicit_macro?
13
+ klass.new name, nil, options, reflection.active_record
14
+ else
15
+ klass.new reflection.macro, name, nil, options, reflection.active_record
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def explicit_macro?
22
+ ActiveRecord::Reflection::MacroReflection.instance_method(:initialize).
23
+ arity == 4
24
+ end
25
+
26
+ def options
27
+ super
28
+
29
+ @options[:sphinx_internal_filtered] = true
30
+ @options
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::ActiveRecord::Depolymorph::BaseReflection
4
+ def initialize(reflection, name, class_name)
5
+ @reflection = reflection
6
+ @name = name
7
+ @class_name = class_name
8
+
9
+ @options = reflection.options.clone
10
+ end
11
+
12
+ def call
13
+ # Should be implemented by subclasses.
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :reflection, :name, :class_name
19
+
20
+ def klass
21
+ reflection.class
22
+ end
23
+
24
+ def options
25
+ @options.delete :polymorphic
26
+ @options[:class_name] = class_name
27
+ @options[:foreign_key] ||= "#{reflection.name}_id"
28
+ @options[:foreign_type] = reflection.foreign_type
29
+
30
+ @options
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The conditions approach is only available in Rails 3. This behaviour is
4
+ # superseded by ScopedReflection for Rails 4.0.
5
+ class ThinkingSphinx::ActiveRecord::Depolymorph::ConditionsReflection <
6
+ ThinkingSphinx::ActiveRecord::Depolymorph::BaseReflection
7
+
8
+ def call
9
+ klass.new reflection.macro, name, options, active_record
10
+ end
11
+
12
+ private
13
+
14
+ delegate :foreign_type, :active_record, :to => :reflection
15
+
16
+ def condition
17
+ "::ts_join_alias::.#{quoted_foreign_type} = '#{class_name}'"
18
+ end
19
+
20
+ def options
21
+ super
22
+
23
+ case @options[:conditions]
24
+ when nil
25
+ @options[:conditions] = condition
26
+ when Array
27
+ @options[:conditions] << condition
28
+ when Hash
29
+ @options[:conditions].merge! foreign_type => @options[:class_name]
30
+ else
31
+ @options[:conditions] = "#{@options[:conditions]} AND #{condition}"
32
+ end
33
+
34
+ @options
35
+ end
36
+
37
+ def quoted_foreign_type
38
+ active_record.connection.quote_column_name foreign_type
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This overriding approach is only available in Rails 5.2+. This behaviour
4
+ # was preceded by AssociationReflection for Rails 4.1-5.1.
5
+ class ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection <
6
+ ThinkingSphinx::ActiveRecord::Depolymorph::BaseReflection
7
+
8
+ module BuildJoinConstraint
9
+ def build_join_constraint(table, foreign_table)
10
+ super.and(
11
+ foreign_table[options[:foreign_type]].eq(
12
+ options[:class_name].constantize.base_class.name
13
+ )
14
+ )
15
+ end
16
+ end
17
+
18
+ module JoinScope
19
+ def join_scope(table, foreign_table, foreign_klass)
20
+ super.where(
21
+ foreign_table[options[:foreign_type]].eq(
22
+ options[:class_name].constantize.base_class.name
23
+ )
24
+ )
25
+ end
26
+ end
27
+
28
+ def self.overridden_classes
29
+ @overridden_classes ||= {}
30
+ end
31
+
32
+ def call
33
+ klass.new name, nil, options, reflection.active_record
34
+ end
35
+
36
+ private
37
+
38
+ def klass
39
+ self.class.overridden_classes[reflection.class] ||= begin
40
+ subclass = Class.new reflection.class
41
+ subclass.include extension(reflection)
42
+ subclass
43
+ end
44
+ end
45
+
46
+ def extension(reflection)
47
+ reflection.respond_to?(:build_join_constraint) ?
48
+ BuildJoinConstraint : JoinScope
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This scoped approach is only available in Rails 4.0. This behaviour is
4
+ # superseded by AssociationReflection for Rails 4.1, and was preceded by
5
+ # ConditionsReflection for Rails 3.2.
6
+ class ThinkingSphinx::ActiveRecord::Depolymorph::ScopedReflection <
7
+ ThinkingSphinx::ActiveRecord::Depolymorph::BaseReflection
8
+
9
+ def call
10
+ klass.new reflection.macro, name, scope, options,
11
+ reflection.active_record
12
+ end
13
+
14
+ private
15
+
16
+ def scope
17
+ lambda { |association|
18
+ reflection = association.reflection
19
+ klass = reflection.class_name.constantize
20
+ where(
21
+ association.parent.aliased_table_name.to_sym =>
22
+ {reflection.foreign_type => klass.base_class.name}
23
+ )
24
+ }
25
+ end
26
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::Field <
2
4
  ThinkingSphinx::ActiveRecord::Property
3
5
  include ThinkingSphinx::Core::Field
@@ -1,76 +1,18 @@
1
- class ThinkingSphinx::ActiveRecord::FilterReflection
2
- attr_reader :reflection, :class_name
3
-
4
- delegate :foreign_type, :active_record, :to => :reflection
5
-
6
- def self.call(reflection, name, class_name)
7
- filter = new(reflection, class_name)
8
- klass = reflection.class
9
- arity = klass.instance_method(:initialize).arity
10
-
11
- if defined?(ActiveRecord::Reflection::MacroReflection) && arity == 4
12
- klass.new name, filter.scope, filter.options, reflection.active_record
13
- elsif reflection.respond_to?(:scope)
14
- klass.new reflection.macro, name, filter.scope, filter.options,
15
- reflection.active_record
16
- else
17
- klass.new reflection.macro, name, filter.options,
18
- reflection.active_record
19
- end
20
- end
21
-
22
- def initialize(reflection, class_name)
23
- @reflection, @class_name = reflection, class_name
24
- @options = reflection.options.clone
25
- end
26
-
27
- def options
28
- @options.delete :polymorphic
29
- @options[:class_name] = class_name
30
- @options[:foreign_key] ||= "#{reflection.name}_id"
31
- @options[:foreign_type] = reflection.foreign_type
32
-
33
- if reflection.respond_to?(:scope)
34
- @options[:sphinx_internal_filtered] = true
35
- return @options
36
- end
1
+ # frozen_string_literal: true
37
2
 
38
- case @options[:conditions]
39
- when nil
40
- @options[:conditions] = condition
41
- when Array
42
- @options[:conditions] << condition
43
- when Hash
44
- @options[:conditions].merge!(reflection.foreign_type => @options[:class_name])
45
- else
46
- @options[:conditions] << " AND #{condition}"
47
- end
48
-
49
- @options
50
- end
51
-
52
- def scope
53
- if ::Joiner::Joins.instance_methods.include?(:join_association_class)
54
- return nil
55
- end
56
-
57
- lambda { |association|
58
- reflection = association.reflection
59
- klass = reflection.class_name.constantize
60
- where(
61
- association.parent.aliased_table_name.to_sym =>
62
- {reflection.foreign_type => klass.base_class.name}
63
- )
64
- }
65
- end
66
-
67
- private
68
-
69
- def condition
70
- "::ts_join_alias::.#{quoted_foreign_type} = '#{class_name}'"
3
+ class ThinkingSphinx::ActiveRecord::FilterReflection
4
+ ReflectionGenerator = case ActiveRecord::VERSION::STRING.to_f
5
+ when (5.2..)
6
+ ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection
7
+ when 4.1..5.1
8
+ ThinkingSphinx::ActiveRecord::Depolymorph::AssociationReflection
9
+ when 4.0
10
+ ThinkingSphinx::ActiveRecord::Depolymorph::ScopedReflection
11
+ when 3.2
12
+ ThinkingSphinx::ActiveRecord::Depolymorph::ConditionsReflection
71
13
  end
72
14
 
73
- def quoted_foreign_type
74
- active_record.connection.quote_column_name foreign_type
15
+ def self.call(reflection, name, class_name)
16
+ ReflectionGenerator.new(reflection, name, class_name).call
75
17
  end
76
18
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::Index < Riddle::Configuration::Index
2
4
  include ThinkingSphinx::Core::Index
3
5
 
@@ -28,6 +30,10 @@ class ThinkingSphinx::ActiveRecord::Index < Riddle::Configuration::Index
28
30
  @facets ||= sources.collect(&:facets).flatten
29
31
  end
30
32
 
33
+ def fields
34
+ sources.collect(&:fields).flatten
35
+ end
36
+
31
37
  def sources
32
38
  interpret_definition!
33
39
  super
@@ -44,10 +50,6 @@ class ThinkingSphinx::ActiveRecord::Index < Riddle::Configuration::Index
44
50
  adapter_for(model)
45
51
  end
46
52
 
47
- def fields
48
- sources.collect(&:fields).flatten
49
- end
50
-
51
53
  def interpreter
52
54
  ThinkingSphinx::ActiveRecord::Interpreter
53
55
  end
@@ -63,7 +65,7 @@ class ThinkingSphinx::ActiveRecord::Index < Riddle::Configuration::Index
63
65
  :delta? => @options[:delta?],
64
66
  :delta_processor => @options[:delta_processor],
65
67
  :delta_options => @options[:delta_options],
66
- :primary_key => @options[:primary_key] || model.primary_key || :id
68
+ :primary_key => primary_key
67
69
  }
68
70
  end
69
71
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::Interpreter <
2
4
  ::ThinkingSphinx::Core::Interpreter
3
5
 
@@ -11,15 +13,15 @@ class ThinkingSphinx::ActiveRecord::Interpreter <
11
13
  end
12
14
 
13
15
  def has(*columns)
14
- __source.attributes += build_properties(
16
+ build_properties(
15
17
  ::ThinkingSphinx::ActiveRecord::Attribute, columns
16
- )
18
+ ).each { |attribute| __source.add_attribute attribute }
17
19
  end
18
20
 
19
21
  def indexes(*columns)
20
- __source.fields += build_properties(
22
+ build_properties(
21
23
  ::ThinkingSphinx::ActiveRecord::Field, columns
22
- )
24
+ ).each { |field| __source.add_field field }
23
25
  end
24
26
 
25
27
  def join(*columns)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::JoinAssociation <
2
4
  ::ActiveRecord::Associations::JoinDependency::JoinAssociation
3
5
 
@@ -5,7 +7,9 @@ class ThinkingSphinx::ActiveRecord::JoinAssociation <
5
7
  constraint = super
6
8
 
7
9
  constraint = constraint.and(
8
- foreign_table[reflection.options[:foreign_type]].eq(base_klass.name)
10
+ foreign_table[reflection.options[:foreign_type]].eq(
11
+ base_klass.base_class.name
12
+ )
9
13
  ) if reflection.options[:sphinx_internal_filtered]
10
14
 
11
15
  constraint
@@ -1,18 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::LogSubscriber < ActiveSupport::LogSubscriber
2
4
  def guard(event)
3
- identifier = color 'Sphinx', GREEN, true
5
+ identifier = colored_text "Sphinx"
4
6
  warn " #{identifier} #{event.payload[:guard]}"
5
7
  end
6
8
 
7
9
  def message(event)
8
- identifier = color 'Sphinx', GREEN, true
10
+ identifier = colored_text "Sphinx"
9
11
  debug " #{identifier} #{event.payload[:message]}"
10
12
  end
11
13
 
12
14
  def query(event)
13
- identifier = color('Sphinx Query (%.1fms)' % event.duration, GREEN, true)
15
+ identifier = colored_text("Sphinx Query (%.1fms)" % event.duration)
14
16
  debug " #{identifier} #{event.payload[:query]}"
15
17
  end
18
+
19
+ def caution(event)
20
+ identifier = colored_text "Sphinx"
21
+ warn " #{identifier} #{event.payload[:caution]}"
22
+ end
23
+
24
+ private
25
+
26
+ if Rails.gem_version >= Gem::Version.new("7.1.0")
27
+ def colored_text(text)
28
+ color text, GREEN, bold: true
29
+ end
30
+ else
31
+ def colored_text(text)
32
+ color text, GREEN, true
33
+ end
34
+ end
16
35
  end
17
36
 
18
37
  ThinkingSphinx::ActiveRecord::LogSubscriber.attach_to :thinking_sphinx
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::Polymorpher
2
4
  def initialize(source, column, class_names)
3
5
  @source, @column, @class_names = source, column, class_names
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::Property
2
4
  include ThinkingSphinx::Core::Property
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::PropertyQuery
2
4
  def initialize(property, source, type = nil)
3
5
  @property, @source, @type = property, source, type
@@ -25,6 +27,7 @@ primary key.
25
27
  attr_reader :property, :source, :type
26
28
 
27
29
  delegate :unscoped, :to => :base_association_class, :prefix => true
30
+ delegate :sql, :to => Arel
28
31
 
29
32
  def base_association
30
33
  reflections.first
@@ -133,7 +136,7 @@ primary key.
133
136
  relation = relation.joins(joins) if joins.present?
134
137
  relation = relation.where("#{quoted_foreign_key} BETWEEN $start AND $end") if ranged?
135
138
  relation = relation.where("#{quoted_foreign_key} IS NOT NULL")
136
- relation = relation.order("#{quoted_foreign_key} ASC") if type.nil?
139
+ relation = relation.order(sql("#{quoted_foreign_key} ASC")) if type.nil?
137
140
 
138
141
  relation.to_sql
139
142
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::PropertySQLPresenter
2
4
  attr_reader :property, :adapter, :associations
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::ActiveRecord::SimpleManyQuery <
2
4
  ThinkingSphinx::ActiveRecord::PropertyQuery
3
5
 
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::ActiveRecord::SourceJoins
4
+ def self.call(model, source)
5
+ new(model, source).call
6
+ end
7
+
8
+ def initialize(model, source)
9
+ @model, @source = model, source
10
+ end
11
+
12
+ def call
13
+ append_specified_associations
14
+ append_property_associations
15
+
16
+ joins
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :model, :source
22
+
23
+ def append_property_associations
24
+ source.properties.collect(&:columns).each do |columns|
25
+ columns.each { |column| append_column_associations column }
26
+ end
27
+ end
28
+
29
+ def append_column_associations(column)
30
+ return if column.__stack.empty? or column_included_in_queries?(column)
31
+
32
+ joins.add_join_to column.__stack if column_exists?(column)
33
+ end
34
+
35
+ def append_specified_associations
36
+ source.associations.reject(&:string?).each do |association|
37
+ joins.add_join_to association.stack
38
+ end
39
+ end
40
+
41
+ def column_exists?(column)
42
+ Joiner::Path.new(model, column.__stack).model
43
+ true
44
+ rescue Joiner::AssociationNotFound
45
+ false
46
+ end
47
+
48
+ def joins
49
+ @joins ||= begin
50
+ joins = Joiner::Joins.new model
51
+ if joins.respond_to?(:join_association_class)
52
+ joins.join_association_class = ThinkingSphinx::ActiveRecord::JoinAssociation
53
+ end
54
+ joins
55
+ end
56
+ end
57
+
58
+ def source_query_properties
59
+ source.properties.select { |field| field.source_type == :query }
60
+ end
61
+
62
+ # Use "first" here instead of a more intuitive flatten because flatten
63
+ # will also ask each column to become an Array and that will start
64
+ # to retrieve data.
65
+ def column_included_in_queries?(column)
66
+ source_query_properties.collect(&:columns).collect(&:first).include?(column)
67
+ end
68
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx
2
4
  module ActiveRecord
3
5
  class SQLBuilder::ClauseBuilder