thinking-sphinx 3.0.6 → 3.1.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +11 -6
  3. data/Appraisals +5 -5
  4. data/Gemfile +3 -3
  5. data/HISTORY +32 -0
  6. data/README.textile +16 -5
  7. data/gemfiles/rails_3_2.gemfile +2 -1
  8. data/gemfiles/rails_4_0.gemfile +3 -2
  9. data/gemfiles/{rails_3_1.gemfile → rails_4_1.gemfile} +3 -2
  10. data/lib/thinking_sphinx.rb +5 -0
  11. data/lib/thinking_sphinx/active_record.rb +2 -1
  12. data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +1 -1
  13. data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +6 -7
  14. data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +2 -2
  15. data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +1 -1
  16. data/lib/thinking_sphinx/active_record/column_sql_presenter.rb +34 -0
  17. data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +4 -0
  18. data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +4 -0
  19. data/lib/thinking_sphinx/active_record/index.rb +2 -1
  20. data/lib/thinking_sphinx/active_record/interpreter.rb +7 -0
  21. data/lib/thinking_sphinx/active_record/property.rb +1 -1
  22. data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +10 -16
  23. data/lib/thinking_sphinx/active_record/sql_builder.rb +7 -1
  24. data/lib/thinking_sphinx/active_record/sql_builder/query.rb +0 -7
  25. data/lib/thinking_sphinx/active_record/sql_source.rb +20 -20
  26. data/lib/thinking_sphinx/active_record/sql_source/template.rb +1 -1
  27. data/lib/thinking_sphinx/capistrano.rb +6 -65
  28. data/lib/thinking_sphinx/capistrano/v2.rb +58 -0
  29. data/lib/thinking_sphinx/capistrano/v3.rb +101 -0
  30. data/lib/thinking_sphinx/configuration.rb +8 -3
  31. data/lib/thinking_sphinx/configuration/distributed_indices.rb +29 -0
  32. data/lib/thinking_sphinx/connection.rb +90 -34
  33. data/lib/thinking_sphinx/controller.rb +20 -0
  34. data/lib/thinking_sphinx/core/index.rb +4 -0
  35. data/lib/thinking_sphinx/deletion.rb +15 -11
  36. data/lib/thinking_sphinx/deltas.rb +9 -0
  37. data/lib/thinking_sphinx/deltas/default_delta.rb +2 -0
  38. data/lib/thinking_sphinx/distributed.rb +5 -0
  39. data/lib/thinking_sphinx/distributed/index.rb +24 -0
  40. data/lib/thinking_sphinx/excerpter.rb +7 -3
  41. data/lib/thinking_sphinx/facet_search.rb +1 -1
  42. data/lib/thinking_sphinx/index.rb +2 -6
  43. data/lib/thinking_sphinx/index_set.rb +10 -8
  44. data/lib/thinking_sphinx/middlewares.rb +0 -2
  45. data/lib/thinking_sphinx/middlewares/active_record_translator.rb +1 -0
  46. data/lib/thinking_sphinx/middlewares/geographer.rb +1 -1
  47. data/lib/thinking_sphinx/middlewares/sphinxql.rb +8 -6
  48. data/lib/thinking_sphinx/middlewares/utf8.rb +6 -1
  49. data/lib/thinking_sphinx/query.rb +9 -0
  50. data/lib/thinking_sphinx/railtie.rb +0 -13
  51. data/lib/thinking_sphinx/search/query.rb +3 -21
  52. data/lib/thinking_sphinx/sphinxql.rb +1 -1
  53. data/lib/thinking_sphinx/wildcard.rb +34 -0
  54. data/spec/acceptance/geosearching_spec.rb +13 -0
  55. data/spec/acceptance/indexing_spec.rb +27 -0
  56. data/spec/acceptance/remove_deleted_records_spec.rb +8 -0
  57. data/spec/acceptance/searching_with_sti_spec.rb +7 -0
  58. data/spec/acceptance/searching_within_a_model_spec.rb +8 -0
  59. data/spec/acceptance/sorting_search_results_spec.rb +1 -1
  60. data/spec/acceptance/spec_helper.rb +13 -0
  61. data/spec/acceptance/specifying_sql_spec.rb +2 -2
  62. data/spec/acceptance/support/sphinx_controller.rb +5 -5
  63. data/spec/acceptance/support/sphinx_helpers.rb +3 -0
  64. data/spec/acceptance/suspended_deltas_spec.rb +34 -0
  65. data/spec/internal/config/database.yml +1 -0
  66. data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +13 -6
  67. data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +2 -2
  68. data/spec/thinking_sphinx/active_record/database_adapters/mysql_adapter_spec.rb +7 -0
  69. data/spec/thinking_sphinx/active_record/database_adapters/postgresql_adapter_spec.rb +6 -0
  70. data/spec/thinking_sphinx/active_record/interpreter_spec.rb +27 -0
  71. data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +18 -7
  72. data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +17 -7
  73. data/spec/thinking_sphinx/active_record/sql_source_spec.rb +84 -82
  74. data/spec/thinking_sphinx/configuration_spec.rb +5 -4
  75. data/spec/thinking_sphinx/connection_spec.rb +1 -1
  76. data/spec/thinking_sphinx/deletion_spec.rb +10 -28
  77. data/spec/thinking_sphinx/excerpter_spec.rb +3 -3
  78. data/spec/thinking_sphinx/facet_search_spec.rb +13 -4
  79. data/spec/thinking_sphinx/index_set_spec.rb +9 -4
  80. data/spec/thinking_sphinx/middlewares/active_record_translator_spec.rb +8 -0
  81. data/spec/thinking_sphinx/middlewares/geographer_spec.rb +7 -7
  82. data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +17 -1
  83. data/spec/thinking_sphinx/search/query_spec.rb +10 -53
  84. data/spec/thinking_sphinx/wildcard_spec.rb +41 -0
  85. data/thinking-sphinx.gemspec +6 -5
  86. metadata +38 -14
  87. data/lib/thinking_sphinx/active_record/associations.rb +0 -98
  88. data/spec/thinking_sphinx/active_record/associations_spec.rb +0 -230
@@ -24,6 +24,12 @@ module ThinkingSphinx
24
24
  query.to_query
25
25
  end
26
26
 
27
+ def sql_query_post_index
28
+ return [] unless delta_processor && !source.delta?
29
+
30
+ [delta_processor.reset_query]
31
+ end
32
+
27
33
  private
28
34
 
29
35
  delegate :adapter, :model, :delta_processor, :to => :source
@@ -47,7 +53,7 @@ module ThinkingSphinx
47
53
  end
48
54
 
49
55
  def associations
50
- @associations ||= ThinkingSphinx::ActiveRecord::Associations.new(model).tap do |assocs|
56
+ @associations ||= Joiner::Joins.new(model).tap do |assocs|
51
57
  source.associations.reject(&:string?).each do |association|
52
58
  assocs.add_join_to association.stack
53
59
  end
@@ -18,17 +18,10 @@ module ThinkingSphinx
18
18
 
19
19
  def filter_by_query_pre
20
20
  scope_by_time_zone
21
- scope_by_delta_processor
22
21
  scope_by_session
23
22
  scope_by_utf8
24
23
  end
25
24
 
26
- def scope_by_delta_processor
27
- return unless delta_processor && !source.delta?
28
-
29
- self.scope << delta_processor.reset_query
30
- end
31
-
32
25
  def scope_by_session
33
26
  return unless max_len = source.options[:group_concat_max_len]
34
27
 
@@ -6,13 +6,13 @@ module ThinkingSphinx
6
6
  attr_accessor :fields, :attributes, :associations, :conditions,
7
7
  :groupings, :polymorphs
8
8
 
9
- OPTIONS = [:name, :offset, :delta_processor, :delta?, :disable_range?,
10
- :group_concat_max_len, :utf8?, :position, :minimal_group_by?]
9
+ OPTIONS = [:name, :offset, :delta_processor, :delta?, :delta_options,
10
+ :disable_range?, :group_concat_max_len, :utf8?, :position,
11
+ :minimal_group_by?]
11
12
 
12
13
  def initialize(model, options = {})
13
14
  @model = model
14
- @database_settings = ::ActiveRecord::Base.connection.
15
- instance_variable_get(:@config).clone
15
+ @database_settings = model.connection.instance_variable_get(:@config).clone
16
16
  @options = {
17
17
  :utf8? => (@database_settings[:encoding] == 'utf8')
18
18
  }.merge options
@@ -38,7 +38,7 @@ module ThinkingSphinx
38
38
  end
39
39
 
40
40
  def delta_processor
41
- options[:delta_processor].try(:new, adapter)
41
+ options[:delta_processor].try(:new, adapter, @options[:delta_options] || {})
42
42
  end
43
43
 
44
44
  def delta?
@@ -67,6 +67,15 @@ module ThinkingSphinx
67
67
  super
68
68
  end
69
69
 
70
+ def set_database_settings(settings)
71
+ @sql_host ||= settings[:host] || 'localhost'
72
+ @sql_user ||= settings[:username] || settings[:user] || ENV['USER']
73
+ @sql_pass ||= settings[:password].to_s.gsub('#', '\#')
74
+ @sql_db ||= settings[:database]
75
+ @sql_port ||= settings[:port]
76
+ @sql_sock ||= settings[:socket]
77
+ end
78
+
70
79
  def type
71
80
  @type ||= case adapter
72
81
  when DatabaseAdapters::MySQLAdapter
@@ -107,10 +116,11 @@ module ThinkingSphinx
107
116
  end
108
117
 
109
118
  def build_sql_query
110
- @sql_query = builder.sql_query
111
- @sql_query_range ||= builder.sql_query_range
112
- @sql_query_info ||= builder.sql_query_info
113
- @sql_query_pre += builder.sql_query_pre
119
+ @sql_query = builder.sql_query
120
+ @sql_query_range ||= builder.sql_query_range
121
+ @sql_query_info ||= builder.sql_query_info
122
+ @sql_query_pre += builder.sql_query_pre
123
+ @sql_query_post_index += builder.sql_query_post_index
114
124
  end
115
125
 
116
126
  def config
@@ -121,7 +131,7 @@ module ThinkingSphinx
121
131
  polymorphs.each &:morph!
122
132
  append_presenter_to_attribute_array
123
133
 
124
- set_database_settings
134
+ set_database_settings database_settings
125
135
  build_sql_fields
126
136
  build_sql_query
127
137
 
@@ -131,16 +141,6 @@ module ThinkingSphinx
131
141
  def properties
132
142
  fields + attributes
133
143
  end
134
-
135
- def set_database_settings
136
- @sql_host ||= database_settings[:host] || 'localhost'
137
- @sql_user ||= database_settings[:username] || database_settings[:user] ||
138
- ENV['USER']
139
- @sql_pass ||= database_settings[:password].to_s.gsub('#', '\#')
140
- @sql_db ||= database_settings[:database]
141
- @sql_port ||= database_settings[:port]
142
- @sql_sock ||= database_settings[:socket]
143
- end
144
144
  end
145
145
  end
146
146
  end
@@ -33,7 +33,7 @@ class ThinkingSphinx::ActiveRecord::SQLSource::Template
33
33
  if inheriting?
34
34
  adapter = source.adapter
35
35
  quoted_column = "#{adapter.quoted_table_name}.#{adapter.quote(model.inheritance_column)}"
36
- source.adapter.convert_nulls quoted_column, "'#{model.sti_name}'"
36
+ source.adapter.convert_blank quoted_column, "'#{model.sti_name}'"
37
37
  else
38
38
  "'#{model.name}'"
39
39
  end
@@ -1,67 +1,8 @@
1
- Capistrano::Configuration.instance(:must_exist).load do
2
- _cset(:thinking_sphinx_roles) { :db }
3
- _cset(:thinking_sphinx_options) { {:roles => fetch(:thinking_sphinx_roles)} }
4
-
5
- namespace :thinking_sphinx do
6
- desc 'Generate the Sphinx configuration file.'
7
- task :configure, fetch(:thinking_sphinx_options) do
8
- rake 'ts:configure'
9
- end
10
-
11
- desc 'Build Sphinx indexes into the shared path and symlink them into your release.'
12
- task :index, fetch(:thinking_sphinx_options) do
13
- rake 'ts:index'
14
- end
15
- after 'thinking_sphinx:index', 'thinking_sphinx:symlink_indexes'
16
-
17
- desc 'Start the Sphinx search daemon.'
18
- task :start, fetch(:thinking_sphinx_options) do
19
- rake 'ts:start'
20
- end
21
- before 'thinking_sphinx:start', 'thinking_sphinx:configure'
22
-
23
- desc 'Stop the Sphinx search daemon.'
24
- task :stop, fetch(:thinking_sphinx_options) do
25
- rake 'ts:stop'
26
- end
27
-
28
- desc 'Restart the Sphinx search daemon.'
29
- task :restart, fetch(:thinking_sphinx_options) do
30
- rake 'ts:stop ts:configure ts:start'
31
- end
32
-
33
- desc <<-DESC
34
- Stop, reindex, and then start the Sphinx search daemon. This task must be executed \
35
- if you alter the structure of your indexes.
36
- DESC
37
- task :rebuild, fetch(:thinking_sphinx_options) do
38
- rake 'ts:rebuild'
39
- end
40
- after 'thinking_sphinx:rebuild', 'thinking_sphinx:symlink_indexes'
41
-
42
- desc 'Create the shared folder for sphinx indexes.'
43
- task :shared_sphinx_folder, fetch(:thinking_sphinx_options) do
44
- rails_env = fetch(:rails_env, 'production')
45
- run "mkdir -p #{shared_path}/db/sphinx/#{rails_env}"
46
- end
47
-
48
- desc 'Symlink Sphinx indexes from the shared folder to the latest release.'
49
- task :symlink_indexes, fetch(:thinking_sphinx_options) do
50
- run "if [ -d #{release_path} ]; then ln -nfs #{shared_path}/db/sphinx #{release_path}/db/sphinx; else ln -nfs #{shared_path}/db/sphinx #{current_path}/db/sphinx; fi;"
51
- end
52
-
53
- # Logical flow for deploying an app
54
- after 'deploy:cold', 'thinking_sphinx:index'
55
- after 'deploy:cold', 'thinking_sphinx:start'
56
- after 'deploy:setup', 'thinking_sphinx:shared_sphinx_folder'
57
- after 'deploy:finalize_update', 'thinking_sphinx:symlink_indexes'
58
-
59
- def rake(tasks)
60
- rails_env = fetch(:rails_env, 'production')
61
- rake = fetch(:rake, 'rake')
62
- tasks += ' INDEX_ONLY=true' if ENV['INDEX_ONLY'] == 'true'
63
-
64
- run "if [ -d #{release_path} ]; then cd #{release_path}; else cd #{current_path}; fi; if [ -f Rakefile ]; then #{rake} RAILS_ENV=#{rails_env} #{tasks}; fi;"
65
- end
1
+ if defined?(Capistrano::VERSION)
2
+ if Gem::Version.new(Capistrano::VERSION).release >= Gem::Version.new('3.0.0')
3
+ recipe_version = 3
66
4
  end
67
5
  end
6
+
7
+ recipe_version ||= 2
8
+ require_relative "capistrano/v#{recipe_version}"
@@ -0,0 +1,58 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+ _cset(:thinking_sphinx_roles) { :db }
3
+ _cset(:thinking_sphinx_options) { {:roles => fetch(:thinking_sphinx_roles)} }
4
+
5
+ namespace :thinking_sphinx do
6
+ desc 'Generate the Sphinx configuration file.'
7
+ task :configure, fetch(:thinking_sphinx_options) do
8
+ rake 'ts:configure'
9
+ end
10
+
11
+ desc 'Build Sphinx indexes into the shared path.'
12
+ task :index, fetch(:thinking_sphinx_options) do
13
+ rake 'ts:index'
14
+ end
15
+
16
+ desc 'Generate Sphinx indexes into the shared path.'
17
+ task :generate, fetch(:thinking_sphinx_options) do
18
+ rake 'ts:generate'
19
+ end
20
+
21
+ desc 'Start the Sphinx search daemon.'
22
+ task :start, fetch(:thinking_sphinx_options) do
23
+ rake 'ts:start'
24
+ end
25
+ before 'thinking_sphinx:start', 'thinking_sphinx:configure'
26
+
27
+ desc 'Stop the Sphinx search daemon.'
28
+ task :stop, fetch(:thinking_sphinx_options) do
29
+ rake 'ts:stop'
30
+ end
31
+
32
+ desc 'Restart the Sphinx search daemon.'
33
+ task :restart, fetch(:thinking_sphinx_options) do
34
+ rake 'ts:stop ts:configure ts:start'
35
+ end
36
+
37
+ desc <<-DESC
38
+ Stop, reindex, and then start the Sphinx search daemon. This task must be executed \
39
+ if you alter the structure of your indexes.
40
+ DESC
41
+ task :rebuild, fetch(:thinking_sphinx_options) do
42
+ rake 'ts:rebuild'
43
+ end
44
+
45
+ desc 'Stop Sphinx, clear Sphinx index files, generate configuration file, start Sphinx, repopulate all data.'
46
+ task :regenerate, fetch(:thinking_sphinx_options) do
47
+ rake 'ts:regenerate'
48
+ end
49
+
50
+ def rake(tasks)
51
+ rails_env = fetch(:rails_env, 'production')
52
+ rake = fetch(:rake, 'rake')
53
+ tasks += ' INDEX_ONLY=true' if ENV['INDEX_ONLY'] == 'true'
54
+
55
+ run "if [ -d #{release_path} ]; then cd #{release_path}; else cd #{current_path}; fi; if [ -f Rakefile ]; then #{rake} RAILS_ENV=#{rails_env} #{tasks}; fi;"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,101 @@
1
+ namespace :load do
2
+ task :defaults do
3
+ set :thinking_sphinx_roles, :db
4
+ end
5
+ end
6
+
7
+ namespace :thinking_sphinx do
8
+ desc <<-DESC
9
+ Stop, reindex, and then start the Sphinx search daemon. This task must be executed \
10
+ if you alter the structure of your indexes.
11
+ DESC
12
+ task :rebuild do
13
+ on roles fetch(:thinking_sphinx_roles) do
14
+ within current_path do
15
+ with rails_env: fetch(:stage) do
16
+ execute :rake, "ts:rebuild"
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ desc 'Stop Sphinx, clear Sphinx index files, generate configuration file, start Sphinx, repopulate all data.'
23
+ task :regenerate do
24
+ on roles fetch(:thinking_sphinx_options) do
25
+ within current_path do
26
+ with rails_env: fetch(:stage) do
27
+ execute :rake, 'ts:regenerate'
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ desc 'Build Sphinx indexes into the shared path.'
34
+ task :index do
35
+ on roles fetch(:thinking_sphinx_roles) do
36
+ within current_path do
37
+ with rails_env: fetch(:stage) do
38
+ execute :rake, 'ts:index'
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ desc 'Generate Sphinx indexes into the shared path.'
45
+ task :generate do
46
+ on roles fetch(:thinking_sphinx_options) do
47
+ within current_path do
48
+ with rails_env: fetch(:stage) do
49
+ execute :rake, 'ts:generate'
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ desc 'Restart the Sphinx search daemon.'
56
+ task :restart do
57
+ on roles fetch(:thinking_sphinx_roles) do
58
+ within current_path do
59
+ with rails_env: fetch(:stage) do
60
+ %w(stop configure start).each do |task|
61
+ execute :rake, "ts:#{task}"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ desc 'Start the Sphinx search daemon.'
69
+ task :start do
70
+ on roles fetch(:thinking_sphinx_roles) do
71
+ within current_path do
72
+ with rails_env: fetch(:stage) do
73
+ execute :rake, 'ts:start'
74
+ end
75
+ end
76
+ end
77
+ end
78
+ before :start, 'thinking_sphinx:configure'
79
+
80
+ desc 'Generate the Sphinx configuration file.'
81
+ task :configure do
82
+ on roles fetch(:thinking_sphinx_roles) do
83
+ within current_path do
84
+ with rails_env: fetch(:stage) do
85
+ execute :rake, 'ts:configure'
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ desc 'Stop the Sphinx search daemon.'
92
+ task :stop do
93
+ on roles fetch(:thinking_sphinx_roles) do
94
+ within current_path do
95
+ with rails_env: fetch(:stage) do
96
+ execute :rake, 'ts:stop'
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -27,7 +27,7 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
27
27
 
28
28
  def controller
29
29
  @controller ||= begin
30
- rc = Riddle::Controller.new self, configuration_file
30
+ rc = ThinkingSphinx::Controller.new self, configuration_file
31
31
  rc.bin_path = bin_path.gsub(/([^\/])$/, '\1/') if bin_path.present?
32
32
  rc
33
33
  end
@@ -50,8 +50,9 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
50
50
  end
51
51
 
52
52
  def engine_indice_paths
53
- Rails::Engine::Railties.engines.collect do |engine|
54
- engine.paths['app/indices'].existent if engine.paths['app/indices']
53
+ Rails::Engine.subclasses.collect(&:instance).collect do |engine|
54
+ engine.paths.add 'app/indices' unless engine.paths['app/indices']
55
+ engine.paths['app/indices'].existent
55
56
  end
56
57
  end
57
58
 
@@ -73,6 +74,8 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
73
74
  end
74
75
  end
75
76
 
77
+ ThinkingSphinx::Configuration::DistributedIndices.new(indices).reconcile
78
+
76
79
  @preloaded_indices = true
77
80
  end
78
81
 
@@ -104,6 +107,7 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
104
107
  searchd.address = settings['address'].presence || Defaults::ADDRESS
105
108
  searchd.mysql41 = settings['mysql41'] || settings['port'] || Defaults::PORT
106
109
  searchd.workers = 'threads'
110
+ searchd.mysql_version_string = '5.5.21' if RUBY_PLATFORM == 'java'
107
111
  end
108
112
 
109
113
  def configure_searchd_log_files
@@ -165,4 +169,5 @@ end
165
169
 
166
170
  require 'thinking_sphinx/configuration/consistent_ids'
167
171
  require 'thinking_sphinx/configuration/defaults'
172
+ require 'thinking_sphinx/configuration/distributed_indices'
168
173
  require 'thinking_sphinx/configuration/minimum_fields'
@@ -0,0 +1,29 @@
1
+ class ThinkingSphinx::Configuration::DistributedIndices
2
+ def initialize(indices)
3
+ @indices = indices
4
+ end
5
+
6
+ def reconcile
7
+ grouped_indices.each do |reference, indices|
8
+ append distributed_index(reference, indices)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :indices
15
+
16
+ def append(index)
17
+ ThinkingSphinx::Configuration.instance.indices << index
18
+ end
19
+
20
+ def distributed_index(reference, indices)
21
+ index = ThinkingSphinx::Distributed::Index.new reference
22
+ index.local_indices += indices.collect &:name
23
+ index
24
+ end
25
+
26
+ def grouped_indices
27
+ indices.group_by &:reference
28
+ end
29
+ end
@@ -16,7 +16,6 @@ module ThinkingSphinx::Connection
16
16
  end
17
17
 
18
18
  def self.connection_class
19
- raise "Sphinx's MySQL protocol does not work with JDBC." if RUBY_PLATFORM == 'java'
20
19
  return ThinkingSphinx::Connection::JRuby if RUBY_PLATFORM == 'java'
21
20
 
22
21
  ThinkingSphinx::Connection::MRI
@@ -36,7 +35,7 @@ module ThinkingSphinx::Connection
36
35
  pool.take do |connection|
37
36
  begin
38
37
  yield connection
39
- rescue ThinkingSphinx::QueryExecutionError, Mysql2::Error => error
38
+ rescue ThinkingSphinx::QueryExecutionError, connection.base_error => error
40
39
  original = ThinkingSphinx::SphinxError.new_from_mysql error
41
40
  raise original if original.is_a?(ThinkingSphinx::QueryError)
42
41
  raise Innertube::Pool::BadResource
@@ -53,65 +52,122 @@ module ThinkingSphinx::Connection
53
52
  end
54
53
  end
55
54
 
56
- class MRI
57
- attr_reader :client
55
+ def self.persistent?
56
+ @persistent
57
+ end
58
58
 
59
- def initialize(address, port, options)
60
- @client = Mysql2::Client.new({
61
- :host => address,
62
- :port => port,
63
- :flags => Mysql2::Client::MULTI_STATEMENTS
64
- }.merge(options))
65
- rescue Mysql2::Error => error
66
- raise ThinkingSphinx::SphinxError.new_from_mysql error
67
- end
59
+ def self.persistent=(persist)
60
+ @persistent = persist
61
+ end
68
62
 
63
+ @persistent = true
64
+
65
+ class Client
69
66
  def close
70
- client.close
67
+ client.close unless ThinkingSphinx::Connection.persistent?
71
68
  end
72
69
 
73
70
  def execute(statement)
74
- client.query statement
71
+ query(statement).first
72
+ end
73
+
74
+ def query_all(*statements)
75
+ query *statements
76
+ end
77
+
78
+ private
79
+
80
+ def close_and_clear
81
+ client.close
82
+ @client = nil
83
+ end
84
+
85
+ def query(*statements)
86
+ results_for *statements
75
87
  rescue => error
76
88
  wrapper = ThinkingSphinx::QueryExecutionError.new error.message
77
- wrapper.statement = statement
89
+ wrapper.statement = statements.join('; ')
78
90
  raise wrapper
91
+ ensure
92
+ close_and_clear unless ThinkingSphinx::Connection.persistent?
79
93
  end
94
+ end
80
95
 
81
- def query(statement)
82
- client.query statement
96
+ class MRI < Client
97
+ def initialize(address, port, options)
98
+ @address, @port, @options = address, port, options
83
99
  end
84
100
 
85
- def query_all(*statements)
101
+ def base_error
102
+ Mysql2::Error
103
+ end
104
+
105
+ private
106
+
107
+ attr_reader :address, :port, :options
108
+
109
+ def client
110
+ @client ||= Mysql2::Client.new({
111
+ :host => address,
112
+ :port => port,
113
+ :flags => Mysql2::Client::MULTI_STATEMENTS
114
+ }.merge(options))
115
+ rescue base_error => error
116
+ raise ThinkingSphinx::SphinxError.new_from_mysql error
117
+ end
118
+
119
+ def results_for(*statements)
86
120
  results = [client.query(statements.join('; '))]
87
121
  results << client.store_result while client.next_result
88
122
  results
89
- rescue => error
90
- wrapper = ThinkingSphinx::QueryExecutionError.new error.message
91
- wrapper.statement = statements.join('; ')
92
- raise wrapper
93
123
  end
94
124
  end
95
125
 
96
- class JRuby
97
- attr_reader :client
126
+ class JRuby < Client
127
+ attr_reader :address, :options
98
128
 
99
129
  def initialize(address, port, options)
100
- address = "jdbc:mysql://#{address}:#{searchd.mysql41}"
101
- @client = java.sql.DriverManager.getConnection address,
102
- options[:username], options[:password]
130
+ @address = "jdbc:mysql://#{address}:#{port}?allowMultiQueries=true"
131
+ @options = options
103
132
  end
104
133
 
105
- def execute(statement)
106
- client.createStatement.execute statement
134
+ def base_error
135
+ Java::JavaSql::SQLException
107
136
  end
108
137
 
109
- def query(statement)
110
- #
138
+ private
139
+
140
+ def client
141
+ @client ||= java.sql.DriverManager.getConnection address,
142
+ options[:username], options[:password]
143
+ rescue base_error => error
144
+ raise ThinkingSphinx::SphinxError.new_from_mysql error
111
145
  end
112
146
 
113
- def query_all(*statements)
114
- #
147
+ def results_for(*statements)
148
+ statement = client.createStatement
149
+ statement.execute statements.join('; ')
150
+
151
+ results = [set_to_array(statement.getResultSet)]
152
+ results << set_to_array(statement.getResultSet) while statement.getMoreResults
153
+ results.compact
154
+ end
155
+
156
+ def set_to_array(set)
157
+ return nil if set.nil?
158
+
159
+ meta = set.meta_data
160
+ rows = []
161
+
162
+ while set.next
163
+ rows << (1..meta.column_count).inject({}) do |row, index|
164
+ name = meta.column_name index
165
+ row[name] = set.get_object(index)
166
+ row
167
+ end
168
+ end
169
+
170
+ rows
115
171
  end
116
172
  end
117
173
  end