thinking-sphinx 3.0.3 → 3.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -1
  3. data/HISTORY +25 -0
  4. data/lib/thinking_sphinx.rb +1 -0
  5. data/lib/thinking_sphinx/active_record/association_proxy.rb +13 -55
  6. data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +47 -0
  7. data/lib/thinking_sphinx/active_record/base.rb +16 -15
  8. data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +1 -12
  9. data/lib/thinking_sphinx/active_record/database_adapters.rb +41 -42
  10. data/lib/thinking_sphinx/active_record/polymorpher.rb +7 -2
  11. data/lib/thinking_sphinx/active_record/property_query.rb +23 -19
  12. data/lib/thinking_sphinx/active_record/sql_builder.rb +108 -129
  13. data/lib/thinking_sphinx/active_record/sql_builder/clause_builder.rb +28 -0
  14. data/lib/thinking_sphinx/active_record/sql_builder/query.rb +43 -0
  15. data/lib/thinking_sphinx/active_record/sql_builder/statement.rb +110 -0
  16. data/lib/thinking_sphinx/active_record/sql_source.rb +143 -138
  17. data/lib/thinking_sphinx/capistrano.rb +11 -8
  18. data/lib/thinking_sphinx/configuration.rb +57 -35
  19. data/lib/thinking_sphinx/connection.rb +15 -6
  20. data/lib/thinking_sphinx/core.rb +1 -0
  21. data/lib/thinking_sphinx/core/index.rb +18 -10
  22. data/lib/thinking_sphinx/core/settings.rb +9 -0
  23. data/lib/thinking_sphinx/deletion.rb +48 -0
  24. data/lib/thinking_sphinx/errors.rb +7 -0
  25. data/lib/thinking_sphinx/excerpter.rb +1 -0
  26. data/lib/thinking_sphinx/facet_search.rb +42 -19
  27. data/lib/thinking_sphinx/masks/scopes_mask.rb +7 -0
  28. data/lib/thinking_sphinx/middlewares.rb +27 -33
  29. data/lib/thinking_sphinx/middlewares/active_record_translator.rb +18 -18
  30. data/lib/thinking_sphinx/middlewares/geographer.rb +49 -16
  31. data/lib/thinking_sphinx/middlewares/sphinxql.rb +128 -58
  32. data/lib/thinking_sphinx/panes/excerpts_pane.rb +7 -3
  33. data/lib/thinking_sphinx/rake_interface.rb +10 -0
  34. data/lib/thinking_sphinx/real_time.rb +7 -1
  35. data/lib/thinking_sphinx/real_time/attribute.rb +4 -0
  36. data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +14 -10
  37. data/lib/thinking_sphinx/real_time/index.rb +20 -12
  38. data/lib/thinking_sphinx/search/glaze.rb +5 -0
  39. data/lib/thinking_sphinx/search/query.rb +4 -8
  40. data/lib/thinking_sphinx/tasks.rb +8 -0
  41. data/spec/acceptance/excerpts_spec.rb +22 -0
  42. data/spec/acceptance/remove_deleted_records_spec.rb +10 -0
  43. data/spec/acceptance/searching_across_models_spec.rb +10 -0
  44. data/spec/acceptance/searching_with_filters_spec.rb +15 -0
  45. data/spec/acceptance/specifying_sql_spec.rb +3 -3
  46. data/spec/acceptance/sphinx_scopes_spec.rb +11 -0
  47. data/spec/internal/app/indices/product_index.rb +2 -0
  48. data/spec/internal/app/models/categorisation.rb +6 -0
  49. data/spec/internal/app/models/category.rb +3 -0
  50. data/spec/internal/app/models/product.rb +4 -1
  51. data/spec/internal/db/schema.rb +10 -0
  52. data/spec/thinking_sphinx/active_record/base_spec.rb +33 -0
  53. data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +4 -35
  54. data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +5 -10
  55. data/spec/thinking_sphinx/active_record/sql_source_spec.rb +4 -3
  56. data/spec/thinking_sphinx/configuration_spec.rb +26 -6
  57. data/spec/thinking_sphinx/connection_spec.rb +4 -1
  58. data/spec/thinking_sphinx/deletion_spec.rb +76 -0
  59. data/spec/thinking_sphinx/facet_search_spec.rb +54 -5
  60. data/spec/thinking_sphinx/panes/excerpts_pane_spec.rb +4 -6
  61. data/spec/thinking_sphinx/rake_interface_spec.rb +35 -0
  62. data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +68 -28
  63. data/spec/thinking_sphinx/search/glaze_spec.rb +19 -0
  64. data/spec/thinking_sphinx/search/query_spec.rb +39 -2
  65. data/thinking-sphinx.gemspec +2 -2
  66. metadata +31 -45
@@ -1,143 +1,148 @@
1
- class ThinkingSphinx::ActiveRecord::SQLSource < Riddle::Configuration::SQLSource
2
- attr_reader :model, :database_settings, :options
3
- attr_accessor :fields, :attributes, :associations, :conditions, :groupings,
4
- :polymorphs
5
-
6
- OPTIONS = [:name, :offset, :delta_processor, :delta?, :disable_range?,
7
- :group_concat_max_len, :utf8?, :position]
8
-
9
- def initialize(model, options = {})
10
- @model = model
11
- @database_settings = model.connection.instance_variable_get(:@config).clone
12
- @options = {
13
- :utf8? => (@database_settings[:encoding] == 'utf8')
14
- }.merge options
15
-
16
- @fields = []
17
- @attributes = []
18
- @associations = []
19
- @conditions = []
20
- @groupings = []
21
- @polymorphs = []
22
-
23
- Template.new(self).apply
24
-
25
- name = "#{options[:name] || model.name.downcase}_#{options[:position]}"
26
-
27
- super name, type
28
-
29
- apply_defaults
30
- end
31
-
32
- def adapter
33
- @adapter ||= ThinkingSphinx::ActiveRecord::DatabaseAdapters.
34
- adapter_for(@model)
35
- end
36
-
37
- def delta_processor
38
- options[:delta_processor].try(:new, adapter)
39
- end
40
-
41
- def delta?
42
- options[:delta?]
43
- end
44
-
45
- def disable_range?
46
- options[:disable_range?]
47
- end
48
-
49
- def facets
50
- properties.select(&:facet?)
51
- end
52
-
53
- def offset
54
- options[:offset]
55
- end
56
-
57
- def primary_key
58
- options[:primary_key]
59
- end
60
-
61
- def render
62
- prepare_for_render unless @prepared
63
-
64
- super
65
- end
66
-
67
- def type
68
- @type ||= case adapter
69
- when ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter
70
- 'mysql'
71
- when ThinkingSphinx::ActiveRecord::DatabaseAdapters::PostgreSQLAdapter
72
- 'pgsql'
73
- else
74
- raise "Unknown Adapter Type: #{adapter.class.name}"
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ class SQLSource < Riddle::Configuration::SQLSource
4
+ include ThinkingSphinx::Core::Settings
5
+ attr_reader :model, :database_settings, :options
6
+ attr_accessor :fields, :attributes, :associations, :conditions, :groupings,
7
+ :polymorphs
8
+
9
+ OPTIONS = [:name, :offset, :delta_processor, :delta?, :disable_range?,
10
+ :group_concat_max_len, :utf8?, :position]
11
+
12
+ def initialize(model, options = {})
13
+ @model = model
14
+ @database_settings = ::ActiveRecord::Base.connection.
15
+ instance_variable_get(:@config).clone
16
+ @options = {
17
+ :utf8? => (@database_settings[:encoding] == 'utf8')
18
+ }.merge options
19
+
20
+ @fields = []
21
+ @attributes = []
22
+ @associations = []
23
+ @conditions = []
24
+ @groupings = []
25
+ @polymorphs = []
26
+
27
+ Template.new(self).apply
28
+
29
+ name = "#{options[:name] || model.name.downcase}_#{options[:position]}"
30
+
31
+ super name, type
32
+
33
+ apply_defaults!
34
+ end
35
+
36
+ def adapter
37
+ @adapter ||= DatabaseAdapters.adapter_for(@model)
38
+ end
39
+
40
+ def delta_processor
41
+ options[:delta_processor].try(:new, adapter)
42
+ end
43
+
44
+ def delta?
45
+ options[:delta?]
46
+ end
47
+
48
+ def disable_range?
49
+ options[:disable_range?]
50
+ end
51
+
52
+ def facets
53
+ properties.select(&:facet?)
54
+ end
55
+
56
+ def offset
57
+ options[:offset]
58
+ end
59
+
60
+ def primary_key
61
+ options[:primary_key]
62
+ end
63
+
64
+ def render
65
+ prepare_for_render unless @prepared
66
+
67
+ super
68
+ end
69
+
70
+ def type
71
+ @type ||= case adapter
72
+ when DatabaseAdapters::MySQLAdapter
73
+ 'mysql'
74
+ when DatabaseAdapters::PostgreSQLAdapter
75
+ 'pgsql'
76
+ else
77
+ raise "Unknown Adapter Type: #{adapter.class.name}"
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def append_presenter_to_attribute_array
84
+ attributes.each do |attribute|
85
+ presenter = Attribute::SphinxPresenter.new(attribute, self)
86
+
87
+ attribute_array_for(presenter.collection_type) << presenter.declaration
88
+ end
89
+ end
90
+
91
+ def attribute_array_for(type)
92
+ instance_variable_get "@sql_attr_#{type}".to_sym
93
+ end
94
+
95
+ def builder
96
+ @builder ||= SQLBuilder.new self
97
+ end
98
+
99
+ def build_sql_fields
100
+ fields.each do |field|
101
+ @sql_field_string << field.name if field.with_attribute?
102
+ @sql_field_str2wordcount << field.name if field.wordcount?
103
+ @sql_file_field << field.name if field.file?
104
+
105
+ @sql_joined_field << PropertyQuery.new(field, self).to_s if field.source_type
106
+ end
107
+ end
108
+
109
+ 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
114
+ end
115
+
116
+ def config
117
+ ThinkingSphinx::Configuration.instance
118
+ end
119
+
120
+ def prepare_for_render
121
+ polymorphs.each &:morph!
122
+ append_presenter_to_attribute_array
123
+
124
+ set_database_settings
125
+ build_sql_fields
126
+ build_sql_query
127
+
128
+ @prepared = true
129
+ end
130
+
131
+ def properties
132
+ fields + attributes
133
+ 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
75
144
  end
76
145
  end
77
-
78
- private
79
-
80
- def apply_defaults
81
- self.class.settings.each do |setting|
82
- value = config.settings[setting.to_s]
83
- send("#{setting}=", value) unless value.nil?
84
- end
85
- end
86
-
87
- def attribute_array_for(type)
88
- instance_variable_get "@sql_attr_#{type}".to_sym
89
- end
90
-
91
- def builder
92
- @builder ||= ThinkingSphinx::ActiveRecord::SQLBuilder.new self
93
- end
94
-
95
- def config
96
- ThinkingSphinx::Configuration.instance
97
- end
98
-
99
- def prepare_for_render
100
- polymorphs.each &:morph!
101
-
102
- set_database_settings
103
-
104
- fields.each do |field|
105
- @sql_field_string << field.name if field.with_attribute?
106
- @sql_field_str2wordcount << field.name if field.wordcount?
107
- @sql_file_field << field.name if field.file?
108
-
109
- @sql_joined_field << ThinkingSphinx::ActiveRecord::PropertyQuery.new(
110
- field, self
111
- ).to_s if field.source_type
112
- end
113
-
114
- attributes.each do |attribute|
115
- presenter = ThinkingSphinx::ActiveRecord::Attribute::SphinxPresenter.new(attribute, self)
116
-
117
- attribute_array_for(presenter.collection_type) << presenter.declaration
118
- end
119
-
120
- @sql_query = builder.sql_query
121
- @sql_query_range = builder.sql_query_range
122
- @sql_query_info = builder.sql_query_info
123
- @sql_query_pre += builder.sql_query_pre
124
-
125
- @prepared = true
126
- end
127
-
128
- def properties
129
- fields + attributes
130
- end
131
-
132
- def set_database_settings
133
- @sql_host ||= database_settings[:host] || 'localhost'
134
- @sql_user ||= database_settings[:username] || database_settings[:user] ||
135
- ENV['USER']
136
- @sql_pass ||= database_settings[:password].to_s.gsub('#', '\#')
137
- @sql_db ||= database_settings[:database]
138
- @sql_port ||= database_settings[:port]
139
- @sql_sock ||= database_settings[:socket]
140
- end
141
146
  end
142
147
 
143
148
  require 'thinking_sphinx/active_record/sql_source/template'
@@ -1,29 +1,32 @@
1
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
+
2
5
  namespace :thinking_sphinx do
3
6
  desc 'Generate the Sphinx configuration file.'
4
- task :configure do
7
+ task :configure, fetch(:thinking_sphinx_options) do
5
8
  rake 'ts:configure'
6
9
  end
7
10
 
8
11
  desc 'Build Sphinx indexes into the shared path and symlink them into your release.'
9
- task :index do
12
+ task :index, fetch(:thinking_sphinx_options) do
10
13
  rake 'ts:index'
11
14
  end
12
15
  after 'thinking_sphinx:index', 'thinking_sphinx:symlink_indexes'
13
16
 
14
17
  desc 'Start the Sphinx search daemon.'
15
- task :start do
18
+ task :start, fetch(:thinking_sphinx_options) do
16
19
  rake 'ts:start'
17
20
  end
18
21
  before 'thinking_sphinx:start', 'thinking_sphinx:configure'
19
22
 
20
23
  desc 'Stop the Sphinx search daemon.'
21
- task :stop do
24
+ task :stop, fetch(:thinking_sphinx_options) do
22
25
  rake 'ts:stop'
23
26
  end
24
27
 
25
28
  desc 'Restart the Sphinx search daemon.'
26
- task :restart do
29
+ task :restart, fetch(:thinking_sphinx_options) do
27
30
  rake 'ts:stop ts:configure ts:start'
28
31
  end
29
32
 
@@ -31,19 +34,19 @@ Capistrano::Configuration.instance(:must_exist).load do
31
34
  Stop, reindex, and then start the Sphinx search daemon. This task must be executed \
32
35
  if you alter the structure of your indexes.
33
36
  DESC
34
- task :rebuild do
37
+ task :rebuild, fetch(:thinking_sphinx_options) do
35
38
  rake 'ts:rebuild'
36
39
  end
37
40
  after 'thinking_sphinx:rebuild', 'thinking_sphinx:symlink_indexes'
38
41
 
39
42
  desc 'Create the shared folder for sphinx indexes.'
40
- task :shared_sphinx_folder do
43
+ task :shared_sphinx_folder, fetch(:thinking_sphinx_options) do
41
44
  rails_env = fetch(:rails_env, 'production')
42
45
  run "mkdir -p #{shared_path}/db/sphinx/#{rails_env}"
43
46
  end
44
47
 
45
48
  desc 'Symlink Sphinx indexes from the shared folder to the latest release.'
46
- task :symlink_indexes do
49
+ task :symlink_indexes, fetch(:thinking_sphinx_options) do
47
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;"
48
51
  end
49
52
 
@@ -1,3 +1,5 @@
1
+ require 'pathname'
2
+
1
3
  class ThinkingSphinx::Configuration < Riddle::Configuration
2
4
  attr_accessor :configuration_file, :indices_location, :version
3
5
  attr_reader :index_paths
@@ -19,12 +21,14 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
19
21
  @instance = nil
20
22
  end
21
23
 
24
+ def bin_path
25
+ settings['bin_path']
26
+ end
27
+
22
28
  def controller
23
29
  @controller ||= begin
24
30
  rc = Riddle::Controller.new self, configuration_file
25
- if settings['bin_path'].present?
26
- rc.bin_path = settings['bin_path'].gsub(/([^\/])$/, '\1/')
27
- end
31
+ rc.bin_path = bin_path.gsub(/([^\/])$/, '\1/') if bin_path.present?
28
32
  rc
29
33
  end
30
34
  end
@@ -42,11 +46,13 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
42
46
  def engine_index_paths
43
47
  return [] unless defined?(Rails)
44
48
 
45
- Rails::Engine::Railties.engines.select { |engine|
46
- engine.paths['app/indices']
47
- }.collect { |engine|
48
- engine.paths['app/indices'].existent
49
- }.flatten
49
+ engine_indice_paths.flatten.compact
50
+ end
51
+
52
+ def engine_indice_paths
53
+ Rails::Engine::Railties.engines.collect do |engine|
54
+ engine.paths['app/indices'].existent if engine.paths['app/indices']
55
+ end
50
56
  end
51
57
 
52
58
  def indices_for_references(*references)
@@ -90,48 +96,64 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
90
96
 
91
97
  private
92
98
 
99
+ def configure_searchd
100
+ configure_searchd_log_files
101
+
102
+ searchd.binlog_path = framework_root.join('tmp', 'binlog',environment).to_s
103
+ searchd.address = settings['address'].presence || Defaults::ADDRESS
104
+ searchd.mysql41 = settings['mysql41'] || settings['port'] || Defaults::PORT
105
+ searchd.workers = 'threads'
106
+ end
107
+
108
+ def configure_searchd_log_files
109
+ searchd.pid_file = log_root.join("#{environment}.sphinx.pid").to_s
110
+ searchd.log = log_root.join("#{environment}.searchd.log").to_s
111
+ searchd.query_log = log_root.join("#{environment}.searchd.query.log").to_s
112
+ end
113
+
114
+ def log_root
115
+ framework_root.join('log')
116
+ end
117
+
118
+ def framework_root
119
+ Pathname.new(framework.root)
120
+ end
121
+
93
122
  def settings_to_hash
94
123
  contents = YAML.load(ERB.new(File.read(settings_file)).result)
95
124
  contents && contents[environment] || {}
96
125
  end
97
126
 
98
127
  def settings_file
99
- File.join framework.root, 'config', 'thinking_sphinx.yml'
128
+ framework_root.join 'config', 'thinking_sphinx.yml'
100
129
  end
101
130
 
102
131
  def setup
103
- @configuration_file = settings['configuration_file'] ||
104
- File.join(framework.root, 'config', "#{environment}.sphinx.conf")
105
- @index_paths = engine_index_paths +
106
- [File.join(framework.root, 'app', 'indices')]
107
- @indices_location = settings['indices_location'] ||
108
- File.join(framework.root, 'db', 'sphinx', environment)
109
- @version = settings['version'] || '2.0.6'
110
-
111
- searchd.pid_file = File.join framework.root, 'log',
112
- "#{environment}.sphinx.pid"
113
- searchd.log = File.join framework.root, 'log',
114
- "#{environment}.searchd.log"
115
- searchd.query_log = File.join framework.root, 'log',
116
- "#{environment}.searchd.query.log"
117
- searchd.binlog_path = File.join framework.root, 'tmp', 'binlog',
118
- environment
119
-
120
- searchd.address = settings['address']
121
- searchd.address = Defaults::ADDRESS unless searchd.address.present?
122
- searchd.mysql41 = settings['mysql41'] || settings['port'] ||
123
- Defaults::PORT
124
- searchd.workers = 'threads'
125
-
126
- [indexer, searchd].each do |object|
132
+ @settings = nil
133
+ @configuration_file = settings['configuration_file'] || framework_root.join(
134
+ 'config', "#{environment}.sphinx.conf"
135
+ ).to_s
136
+ @index_paths = engine_index_paths + [framework_root.join('app', 'indices').to_s]
137
+ @indices_location = settings['indices_location'] || framework_root.join(
138
+ 'db', 'sphinx', environment
139
+ ).to_s
140
+ @version = settings['version'] || '2.0.6'
141
+
142
+ configure_searchd
143
+
144
+ apply_sphinx_settings!
145
+
146
+ @offsets = {}
147
+ end
148
+
149
+ def apply_sphinx_settings!
150
+ [indexer, searchd].each do |object|
127
151
  settings.each do |key, value|
128
152
  next unless object.class.settings.include?(key.to_sym)
129
153
 
130
154
  object.send("#{key}=", value)
131
155
  end
132
156
  end
133
-
134
- @offsets = {}
135
157
  end
136
158
  end
137
159