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.
- checksums.yaml +7 -0
- data/.gitignore +2 -1
- data/HISTORY +25 -0
- data/lib/thinking_sphinx.rb +1 -0
- data/lib/thinking_sphinx/active_record/association_proxy.rb +13 -55
- data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +47 -0
- data/lib/thinking_sphinx/active_record/base.rb +16 -15
- data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +1 -12
- data/lib/thinking_sphinx/active_record/database_adapters.rb +41 -42
- data/lib/thinking_sphinx/active_record/polymorpher.rb +7 -2
- data/lib/thinking_sphinx/active_record/property_query.rb +23 -19
- data/lib/thinking_sphinx/active_record/sql_builder.rb +108 -129
- data/lib/thinking_sphinx/active_record/sql_builder/clause_builder.rb +28 -0
- data/lib/thinking_sphinx/active_record/sql_builder/query.rb +43 -0
- data/lib/thinking_sphinx/active_record/sql_builder/statement.rb +110 -0
- data/lib/thinking_sphinx/active_record/sql_source.rb +143 -138
- data/lib/thinking_sphinx/capistrano.rb +11 -8
- data/lib/thinking_sphinx/configuration.rb +57 -35
- data/lib/thinking_sphinx/connection.rb +15 -6
- data/lib/thinking_sphinx/core.rb +1 -0
- data/lib/thinking_sphinx/core/index.rb +18 -10
- data/lib/thinking_sphinx/core/settings.rb +9 -0
- data/lib/thinking_sphinx/deletion.rb +48 -0
- data/lib/thinking_sphinx/errors.rb +7 -0
- data/lib/thinking_sphinx/excerpter.rb +1 -0
- data/lib/thinking_sphinx/facet_search.rb +42 -19
- data/lib/thinking_sphinx/masks/scopes_mask.rb +7 -0
- data/lib/thinking_sphinx/middlewares.rb +27 -33
- data/lib/thinking_sphinx/middlewares/active_record_translator.rb +18 -18
- data/lib/thinking_sphinx/middlewares/geographer.rb +49 -16
- data/lib/thinking_sphinx/middlewares/sphinxql.rb +128 -58
- data/lib/thinking_sphinx/panes/excerpts_pane.rb +7 -3
- data/lib/thinking_sphinx/rake_interface.rb +10 -0
- data/lib/thinking_sphinx/real_time.rb +7 -1
- data/lib/thinking_sphinx/real_time/attribute.rb +4 -0
- data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +14 -10
- data/lib/thinking_sphinx/real_time/index.rb +20 -12
- data/lib/thinking_sphinx/search/glaze.rb +5 -0
- data/lib/thinking_sphinx/search/query.rb +4 -8
- data/lib/thinking_sphinx/tasks.rb +8 -0
- data/spec/acceptance/excerpts_spec.rb +22 -0
- data/spec/acceptance/remove_deleted_records_spec.rb +10 -0
- data/spec/acceptance/searching_across_models_spec.rb +10 -0
- data/spec/acceptance/searching_with_filters_spec.rb +15 -0
- data/spec/acceptance/specifying_sql_spec.rb +3 -3
- data/spec/acceptance/sphinx_scopes_spec.rb +11 -0
- data/spec/internal/app/indices/product_index.rb +2 -0
- data/spec/internal/app/models/categorisation.rb +6 -0
- data/spec/internal/app/models/category.rb +3 -0
- data/spec/internal/app/models/product.rb +4 -1
- data/spec/internal/db/schema.rb +10 -0
- data/spec/thinking_sphinx/active_record/base_spec.rb +33 -0
- data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +4 -35
- data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +5 -10
- data/spec/thinking_sphinx/active_record/sql_source_spec.rb +4 -3
- data/spec/thinking_sphinx/configuration_spec.rb +26 -6
- data/spec/thinking_sphinx/connection_spec.rb +4 -1
- data/spec/thinking_sphinx/deletion_spec.rb +76 -0
- data/spec/thinking_sphinx/facet_search_spec.rb +54 -5
- data/spec/thinking_sphinx/panes/excerpts_pane_spec.rb +4 -6
- data/spec/thinking_sphinx/rake_interface_spec.rb +35 -0
- data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +68 -28
- data/spec/thinking_sphinx/search/glaze_spec.rb +19 -0
- data/spec/thinking_sphinx/search/query_spec.rb +39 -2
- data/thinking-sphinx.gemspec +2 -2
- metadata +31 -45
@@ -1,143 +1,148 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
128
|
+
framework_root.join 'config', 'thinking_sphinx.yml'
|
100
129
|
end
|
101
130
|
|
102
131
|
def setup
|
103
|
-
@
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
@
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
searchd.
|
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
|
|