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