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
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Commands::MergeAndUpdate < ThinkingSphinx::Commands::Base
4
+ def call
5
+ configuration.preload_indices
6
+ configuration.render
7
+
8
+ index_pairs.each do |(core_index, delta_index)|
9
+ command :merge,
10
+ :core_index => core_index,
11
+ :delta_index => delta_index,
12
+ :filters => {:sphinx_deleted => 0}
13
+
14
+ core_index.model.where(:delta => true).update_all(:delta => false)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ delegate :controller, :to => :configuration
21
+
22
+ def core_indices
23
+ indices.select { |index| !index.delta? }.select do |index|
24
+ name_filters.empty? ||
25
+ name_filters.include?(index.name.gsub(/_core$/, ''))
26
+ end
27
+ end
28
+
29
+ def delta_for(core_index)
30
+ name = core_index.name.gsub(/_core$/, "_delta")
31
+ indices.detect { |index| index.name == name }
32
+ end
33
+
34
+ def index_pairs
35
+ core_indices.collect { |core_index|
36
+ [core_index, delta_for(core_index)]
37
+ }
38
+ end
39
+
40
+ def indices
41
+ @indices ||= configuration.indices.select { |index|
42
+ index.type == "plain" && index.options[:delta_processor]
43
+ }
44
+ end
45
+
46
+ def indices_exist?(*indices)
47
+ indices.all? { |index| File.exist?("#{index.path}.spi") }
48
+ end
49
+
50
+ def name_filters
51
+ @name_filters ||= options[:index_names] || []
52
+ end
53
+
54
+ def type
55
+ 'merging_and_updating'
56
+ end
57
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Commands::Prepare < ThinkingSphinx::Commands::Base
4
+ def call
5
+ return if skip_directories?
6
+
7
+ FileUtils.mkdir_p configuration.indices_location
8
+ end
9
+
10
+ private
11
+
12
+ def type
13
+ 'prepare'
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Commands::Rotate < ThinkingSphinx::Commands::Base
4
+ def call
5
+ controller.rotate
6
+ end
7
+
8
+ private
9
+
10
+ def type
11
+ 'rotate'
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Commands::Running < ThinkingSphinx::Commands::Base
4
+ def call
5
+ return true if configuration.settings['skip_running_check']
6
+
7
+ controller.running?
8
+ end
9
+
10
+ private
11
+
12
+ def type
13
+ 'running'
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Commands::StartAttached < ThinkingSphinx::Commands::Base
4
+ def call
5
+ FileUtils.mkdir_p configuration.indices_location unless skip_directories?
6
+
7
+ unless pid = fork
8
+ controller.start :verbose => options[:verbose], :nodetach => true
9
+ end
10
+
11
+ Signal.trap('TERM') { Process.kill(:TERM, pid) }
12
+ Signal.trap('INT') { Process.kill(:TERM, pid) }
13
+
14
+ Process.wait(pid)
15
+ end
16
+
17
+ private
18
+
19
+ def type
20
+ 'start'
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Commands::StartDetached < ThinkingSphinx::Commands::Base
4
+ def call
5
+ FileUtils.mkdir_p configuration.indices_location unless skip_directories?
6
+
7
+ result = controller.start :verbose => options[:verbose]
8
+
9
+ if command :running
10
+ log "Started searchd successfully (pid: #{controller.pid})."
11
+ else
12
+ handle_failure result
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def type
19
+ 'start'
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Commands::Stop < ThinkingSphinx::Commands::Base
4
+ def call
5
+ unless command :running
6
+ log 'searchd is not currently running.'
7
+ return
8
+ end
9
+
10
+ pid = controller.pid
11
+ until !command :running do
12
+ controller.stop options
13
+ sleep(0.5)
14
+ end
15
+
16
+ log "Stopped searchd daemon (pid: #{pid})."
17
+ end
18
+
19
+ private
20
+
21
+ def type
22
+ 'stop'
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThinkingSphinx::Commands
4
+ #
5
+ end
6
+
7
+ require 'thinking_sphinx/commands/base'
8
+ require 'thinking_sphinx/commands/clear_real_time'
9
+ require 'thinking_sphinx/commands/clear_sql'
10
+ require 'thinking_sphinx/commands/configure'
11
+ require 'thinking_sphinx/commands/index_sql'
12
+ require 'thinking_sphinx/commands/index_real_time'
13
+ require 'thinking_sphinx/commands/merge'
14
+ require 'thinking_sphinx/commands/merge_and_update'
15
+ require 'thinking_sphinx/commands/prepare'
16
+ require 'thinking_sphinx/commands/rotate'
17
+ require 'thinking_sphinx/commands/running'
18
+ require 'thinking_sphinx/commands/start_attached'
19
+ require 'thinking_sphinx/commands/start_detached'
20
+ require 'thinking_sphinx/commands/stop'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Configuration::ConsistentIds
2
4
  def initialize(indices)
3
5
  @indices = indices
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Configuration::Defaults
2
4
  ADDRESS = '127.0.0.1'
3
5
  PORT = 9306
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Configuration::DistributedIndices
2
4
  def initialize(indices)
3
5
  @indices = indices
@@ -19,7 +21,7 @@ class ThinkingSphinx::Configuration::DistributedIndices
19
21
 
20
22
  def distributed_index(reference, indices)
21
23
  index = ThinkingSphinx::Distributed::Index.new reference
22
- index.local_indices += indices.collect &:name
24
+ index.local_index_objects = indices
23
25
  index
24
26
  end
25
27
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Configuration::DuplicateNames
4
+ def initialize(indices)
5
+ @indices = indices
6
+ end
7
+
8
+ def reconcile
9
+ indices.each do |index|
10
+ return if index.distributed?
11
+
12
+ counts_for(index).each do |name, count|
13
+ next if count <= 1
14
+
15
+ raise ThinkingSphinx::DuplicateNameError,
16
+ "Duplicate field/attribute name '#{name}' in index '#{index.name}'"
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :indices
24
+
25
+ def counts_for(index)
26
+ names_for(index).inject({}) do |hash, name|
27
+ hash[name] ||= 0
28
+ hash[name] += 1
29
+ hash
30
+ end
31
+ end
32
+
33
+ def names_for(index)
34
+ index.fields.collect(&:name) + index.attributes.collect(&:name)
35
+ end
36
+ end
@@ -1,13 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Configuration::MinimumFields
2
4
  def initialize(indices)
3
5
  @indices = indices
4
6
  end
5
7
 
6
8
  def reconcile
7
- return unless no_inheritance_columns?
8
-
9
- sources.each do |source|
10
- source.fields.delete_if do |field|
9
+ field_collections.each do |collection|
10
+ collection.fields.delete_if do |field|
11
11
  field.name == 'sphinx_internal_class_name'
12
12
  end
13
13
  end
@@ -17,15 +17,20 @@ class ThinkingSphinx::Configuration::MinimumFields
17
17
 
18
18
  attr_reader :indices
19
19
 
20
- def no_inheritance_columns?
21
- indices.select { |index|
22
- index.model.column_names.include?(index.model.inheritance_column)
23
- }.empty?
20
+ def field_collections
21
+ indices_without_inheritance_of_type('plain').collect(&:sources).flatten +
22
+ indices_without_inheritance_of_type('rt')
23
+ end
24
+
25
+ def inheritance_columns?(index)
26
+ index.model.table_exists? && index.model.column_names.include?(index.model.inheritance_column)
27
+ end
28
+
29
+ def indices_without_inheritance_of_type(type)
30
+ indices_without_inheritance.select { |index| index.type == type }
24
31
  end
25
32
 
26
- def sources
27
- @sources ||= @indices.select { |index|
28
- index.respond_to?(:sources)
29
- }.collect(&:sources).flatten
33
+ def indices_without_inheritance
34
+ indices.reject(&method(:inheritance_columns?))
30
35
  end
31
36
  end
@@ -1,16 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
 
3
5
  class ThinkingSphinx::Configuration < Riddle::Configuration
4
- attr_accessor :configuration_file, :indices_location, :version
6
+ attr_accessor :configuration_file, :indices_location, :version, :batch_size
5
7
  attr_reader :index_paths
6
- attr_writer :controller, :index_set_class
8
+ attr_writer :controller, :index_set_class, :indexing_strategy,
9
+ :guarding_strategy
7
10
 
8
11
  delegate :environment, :to => :framework
9
12
 
13
+ @@mutex = defined?(ActiveSupport::Concurrency::LoadInterlockAwareMonitor) ?
14
+ ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new : Mutex.new
15
+
10
16
  def initialize
11
17
  super
12
18
 
13
- setup
19
+ reset
14
20
  end
15
21
 
16
22
  def self.instance
@@ -27,7 +33,7 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
27
33
 
28
34
  def controller
29
35
  @controller ||= begin
30
- rc = ThinkingSphinx::Controller.new self, configuration_file
36
+ rc = Riddle::Controller.new self, configuration_file
31
37
  rc.bin_path = bin_path.gsub(/([^\/])$/, '\1/') if bin_path.present?
32
38
  rc
33
39
  end
@@ -39,14 +45,14 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
39
45
 
40
46
  def framework=(framework)
41
47
  @framework = framework
42
- setup
48
+ reset
43
49
  framework
44
50
  end
45
51
 
46
52
  def engine_index_paths
47
53
  return [] unless defined?(Rails)
48
54
 
49
- engine_indice_paths.flatten.compact
55
+ engine_indice_paths.flatten.compact.sort
50
56
  end
51
57
 
52
58
  def engine_indice_paths
@@ -56,10 +62,18 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
56
62
  end
57
63
  end
58
64
 
65
+ def guarding_strategy
66
+ @guarding_strategy ||= ThinkingSphinx::Guard::Files
67
+ end
68
+
59
69
  def index_set_class
60
70
  @index_set_class ||= ThinkingSphinx::IndexSet
61
71
  end
62
72
 
73
+ def indexing_strategy
74
+ @indexing_strategy ||= ThinkingSphinx::IndexingStrategies::AllAtOnce
75
+ end
76
+
63
77
  def indices_for_references(*references)
64
78
  index_set_class.new(:references => references).to_a
65
79
  end
@@ -69,105 +83,105 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
69
83
  end
70
84
 
71
85
  def preload_indices
72
- return if @preloaded_indices
86
+ @@mutex.synchronize do
87
+ return if @preloaded_indices
73
88
 
74
- index_paths.each do |path|
75
- Dir["#{path}/**/*.rb"].sort.each do |file|
76
- ActiveSupport::Dependencies.require_or_load file
89
+ index_paths.each do |path|
90
+ Dir["#{path}/**/*.rb"].sort.each { |file| preload_index file }
77
91
  end
78
- end
79
92
 
80
- if settings['distributed_indices'].nil? || settings['distributed_indices']
81
- ThinkingSphinx::Configuration::DistributedIndices.new(indices).reconcile
93
+ normalise
94
+ verify
95
+
96
+ @preloaded_indices = true
82
97
  end
98
+ end
83
99
 
84
- @preloaded_indices = true
100
+ def preload_index(file)
101
+ if ActiveRecord::VERSION::MAJOR <= 5
102
+ ActiveSupport::Dependencies.require_or_load file
103
+ else
104
+ load file
105
+ end
85
106
  end
86
107
 
87
108
  def render
88
109
  preload_indices
89
110
 
90
- ThinkingSphinx::Configuration::ConsistentIds.new(indices).reconcile
91
- ThinkingSphinx::Configuration::MinimumFields.new(indices).reconcile
92
-
93
111
  super
94
112
  end
95
113
 
96
114
  def render_to_file
97
- FileUtils.mkdir_p searchd.binlog_path unless searchd.binlog_path.blank?
115
+ unless settings['skip_directory_creation'] || searchd.binlog_path.blank?
116
+ FileUtils.mkdir_p searchd.binlog_path
117
+ end
98
118
 
99
119
  open(configuration_file, 'w') { |file| file.write render }
100
120
  end
101
121
 
102
122
  def settings
103
- @settings ||= File.exists?(settings_file) ? settings_to_hash : {}
123
+ @settings ||= ThinkingSphinx::Settings.call self
104
124
  end
105
125
 
106
- private
107
-
108
- def configure_searchd
109
- configure_searchd_log_files
126
+ def setup
127
+ @configuration_file = settings['configuration_file']
128
+ @index_paths = engine_index_paths +
129
+ [Pathname.new(framework.root).join('app', 'indices').to_s]
130
+ @indices_location = settings['indices_location']
131
+ @version = settings['version'] || '2.2.11'
132
+ @batch_size = settings['batch_size'] || 1000
110
133
 
111
- searchd.binlog_path = tmp_path.join('binlog', environment).to_s
112
- searchd.address = settings['address'].presence || Defaults::ADDRESS
113
- searchd.mysql41 = settings['mysql41'] || settings['port'] || Defaults::PORT
114
- searchd.workers = 'threads'
115
- searchd.mysql_version_string = '5.5.21' if RUBY_PLATFORM == 'java'
116
- end
134
+ if settings['common_sphinx_configuration']
135
+ common.common_sphinx_configuration = true
136
+ indexer.common_sphinx_configuration = true
137
+ end
117
138
 
118
- def configure_searchd_log_files
119
- searchd.pid_file = log_root.join("#{environment}.sphinx.pid").to_s
120
- searchd.log = log_root.join("#{environment}.searchd.log").to_s
121
- searchd.query_log = log_root.join("#{environment}.searchd.query.log").to_s
122
- end
139
+ configure_searchd
123
140
 
124
- def log_root
125
- real_path 'log'
126
- end
141
+ apply_sphinx_settings!
127
142
 
128
- def framework_root
129
- Pathname.new(framework.root)
143
+ @offsets = {}
130
144
  end
131
145
 
132
- def real_path(*arguments)
133
- path = framework_root.join(*arguments)
134
- path.exist? ? path.realpath : path
135
- end
146
+ private
136
147
 
137
- def settings_to_hash
138
- contents = YAML.load(ERB.new(File.read(settings_file)).result)
139
- contents && contents[environment] || {}
140
- end
148
+ def apply_sphinx_settings!
149
+ sphinx_sections.each do |object|
150
+ settings.each do |key, value|
151
+ next unless object.class.settings.include?(key.to_sym)
141
152
 
142
- def settings_file
143
- framework_root.join 'config', 'thinking_sphinx.yml'
153
+ object.send("#{key}=", value)
154
+ end
155
+ end
144
156
  end
145
157
 
146
- def setup
147
- @settings = nil
148
- @configuration_file = settings['configuration_file'] || framework_root.join(
149
- 'config', "#{environment}.sphinx.conf"
150
- ).to_s
151
- @index_paths = engine_index_paths + [framework_root.join('app', 'indices').to_s]
152
- @indices_location = settings['indices_location'] || framework_root.join(
153
- 'db', 'sphinx', environment
154
- ).to_s
155
- @version = settings['version'] || '2.1.4'
158
+ def configure_searchd
159
+ searchd.socket = "#{settings["socket"]}:mysql41" if socket?
156
160
 
157
- if settings['common_sphinx_configuration']
158
- common.common_sphinx_configuration = true
159
- indexer.common_sphinx_configuration = true
161
+ if tcp?
162
+ searchd.address = settings['address'].presence || Defaults::ADDRESS
163
+ searchd.mysql41 = settings['mysql41'] || settings['port'] || Defaults::PORT
160
164
  end
161
165
 
162
- configure_searchd
166
+ searchd.mysql_version_string = '5.5.21' if RUBY_PLATFORM == 'java'
167
+ end
163
168
 
164
- apply_sphinx_settings!
169
+ def normalise
170
+ if settings['distributed_indices'].nil? || settings['distributed_indices']
171
+ ThinkingSphinx::Configuration::DistributedIndices.new(indices).reconcile
172
+ end
165
173
 
166
- @offsets = {}
174
+ ThinkingSphinx::Configuration::ConsistentIds.new(indices).reconcile
175
+ ThinkingSphinx::Configuration::MinimumFields.new(indices).reconcile
176
+ end
177
+
178
+ def reset
179
+ @settings = nil
180
+ setup
167
181
  end
168
182
 
169
- def tmp_path
170
- real_path 'tmp'
183
+ def socket?
184
+ settings["socket"].present?
171
185
  end
172
186
 
173
187
  def sphinx_sections
@@ -176,18 +190,20 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
176
190
  sections
177
191
  end
178
192
 
179
- def apply_sphinx_settings!
180
- sphinx_sections.each do |object|
181
- settings.each do |key, value|
182
- next unless object.class.settings.include?(key.to_sym)
193
+ def tcp?
194
+ settings["socket"].nil? ||
195
+ settings["address"].present? ||
196
+ settings["mysql41"].present? ||
197
+ settings["port"].present?
198
+ end
183
199
 
184
- object.send("#{key}=", value)
185
- end
186
- end
200
+ def verify
201
+ ThinkingSphinx::Configuration::DuplicateNames.new(indices).reconcile
187
202
  end
188
203
  end
189
204
 
190
205
  require 'thinking_sphinx/configuration/consistent_ids'
191
206
  require 'thinking_sphinx/configuration/defaults'
192
207
  require 'thinking_sphinx/configuration/distributed_indices'
208
+ require 'thinking_sphinx/configuration/duplicate_names'
193
209
  require 'thinking_sphinx/configuration/minimum_fields'
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Connection::Client
4
+ def initialize(options)
5
+ if options[:socket].present?
6
+ options[:socket] = options[:socket].remove /:mysql41$/
7
+
8
+ options.delete :host
9
+ options.delete :port
10
+ else
11
+ options.delete :socket
12
+
13
+ # If you use localhost, MySQL insists on a socket connection, but in this
14
+ # situation we want a TCP connection. Using 127.0.0.1 fixes that.
15
+ if options[:host].nil? || options[:host] == "localhost"
16
+ options[:host] = "127.0.0.1"
17
+ end
18
+ end
19
+
20
+ @options = options
21
+ end
22
+
23
+ def close
24
+ close! unless ThinkingSphinx::Connection.persistent?
25
+ end
26
+
27
+ def close!
28
+ client.close
29
+ end
30
+
31
+ def execute(statement)
32
+ check_and_perform(statement).first
33
+ end
34
+
35
+ def query_all(*statements)
36
+ check_and_perform statements.join('; ')
37
+ end
38
+
39
+ private
40
+
41
+ def check(statements)
42
+ if statements.length > maximum_statement_length
43
+ exception = ThinkingSphinx::QueryLengthError.new
44
+ exception.statement = statements
45
+ raise exception
46
+ end
47
+ end
48
+
49
+ def check_and_perform(statements)
50
+ check statements
51
+ perform statements
52
+ end
53
+
54
+ def close_and_clear
55
+ client.close
56
+ @client = nil
57
+ end
58
+
59
+ def maximum_statement_length
60
+ @maximum_statement_length ||= ThinkingSphinx::Configuration.instance.
61
+ settings['maximum_statement_length']
62
+ end
63
+
64
+ def perform(statements)
65
+ results_for statements
66
+ rescue => error
67
+ message = "#{error.message} - #{statements}"
68
+ wrapper = ThinkingSphinx::QueryExecutionError.new message
69
+ wrapper.statement = statements
70
+ raise wrapper
71
+ ensure
72
+ close_and_clear unless ThinkingSphinx::Connection.persistent?
73
+ end
74
+ end