thinking-sphinx 3.0.3 → 3.0.4

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 (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