thinking-sphinx 3.0.6 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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