thinking-sphinx 4.3.0 → 5.0.0
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 +4 -4
- data/.travis.yml +16 -21
- data/Appraisals +2 -17
- data/CHANGELOG.markdown +62 -1
- data/README.textile +14 -16
- data/bin/loadsphinx +20 -5
- data/lib/thinking_sphinx/active_record/association_proxy.rb +1 -2
- data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +1 -1
- data/lib/thinking_sphinx/active_record/base.rb +17 -6
- data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +1 -1
- data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +3 -2
- data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +1 -1
- data/lib/thinking_sphinx/active_record/interpreter.rb +4 -4
- data/lib/thinking_sphinx/active_record/sql_source.rb +12 -0
- data/lib/thinking_sphinx/active_record/sql_source/template.rb +2 -2
- data/lib/thinking_sphinx/callbacks.rb +9 -0
- data/lib/thinking_sphinx/callbacks/appender.rb +47 -0
- data/lib/thinking_sphinx/commands/index_real_time.rb +1 -3
- data/lib/thinking_sphinx/configuration.rb +9 -3
- data/lib/thinking_sphinx/core/index.rb +5 -2
- data/lib/thinking_sphinx/deletion.rb +18 -17
- data/lib/thinking_sphinx/index_set.rb +7 -3
- data/lib/thinking_sphinx/middlewares/sphinxql.rb +1 -1
- data/lib/thinking_sphinx/railtie.rb +9 -1
- data/lib/thinking_sphinx/real_time.rb +17 -0
- data/lib/thinking_sphinx/real_time/index.rb +4 -0
- data/lib/thinking_sphinx/real_time/interpreter.rb +8 -6
- data/lib/thinking_sphinx/real_time/populator.rb +1 -1
- data/lib/thinking_sphinx/real_time/processor.rb +36 -0
- data/lib/thinking_sphinx/real_time/transcriber.rb +35 -19
- data/lib/thinking_sphinx/subscribers/populator_subscriber.rb +0 -4
- data/spec/acceptance/big_integers_spec.rb +1 -1
- data/spec/acceptance/merging_spec.rb +1 -1
- data/spec/acceptance/real_time_updates_spec.rb +2 -2
- data/spec/acceptance/sql_deltas_spec.rb +3 -3
- data/spec/acceptance/suspended_deltas_spec.rb +3 -3
- data/spec/internal/app/models/admin/person.rb +3 -1
- data/spec/internal/app/models/album.rb +3 -1
- data/spec/internal/app/models/animal.rb +1 -0
- data/spec/internal/app/models/article.rb +2 -0
- data/spec/internal/app/models/bird.rb +1 -0
- data/spec/internal/app/models/book.rb +2 -0
- data/spec/internal/app/models/car.rb +1 -1
- data/spec/internal/app/models/city.rb +2 -0
- data/spec/internal/app/models/product.rb +1 -1
- data/spec/internal/app/models/tee.rb +2 -0
- data/spec/internal/app/models/user.rb +2 -0
- data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +2 -1
- data/spec/thinking_sphinx/active_record/index_spec.rb +0 -12
- data/spec/thinking_sphinx/active_record/interpreter_spec.rb +15 -14
- data/spec/thinking_sphinx/active_record/sql_source_spec.rb +38 -0
- data/spec/thinking_sphinx/configuration_spec.rb +17 -16
- data/spec/thinking_sphinx/deletion_spec.rb +5 -4
- data/spec/thinking_sphinx/index_set_spec.rb +28 -12
- data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +2 -1
- data/spec/thinking_sphinx/real_time/index_spec.rb +38 -12
- data/spec/thinking_sphinx/real_time/interpreter_spec.rb +14 -14
- data/spec/thinking_sphinx/real_time/transcriber_spec.rb +9 -1
- data/thinking-sphinx.gemspec +3 -5
- metadata +9 -8
@@ -3,6 +3,13 @@
|
|
3
3
|
class ThinkingSphinx::Callbacks
|
4
4
|
attr_reader :instance
|
5
5
|
|
6
|
+
def self.append(model, reference = nil, options, &block)
|
7
|
+
reference ||= ThinkingSphinx::Configuration.instance.index_set_class.
|
8
|
+
reference_name(model)
|
9
|
+
|
10
|
+
ThinkingSphinx::Callbacks::Appender.call(model, reference, options, &block)
|
11
|
+
end
|
12
|
+
|
6
13
|
def self.callbacks(*methods)
|
7
14
|
mod = Module.new
|
8
15
|
methods.each do |method|
|
@@ -33,3 +40,5 @@ class ThinkingSphinx::Callbacks
|
|
33
40
|
@instance = instance
|
34
41
|
end
|
35
42
|
end
|
43
|
+
|
44
|
+
require "thinking_sphinx/callbacks/appender"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ThinkingSphinx::Callbacks::Appender
|
4
|
+
def self.call(model, reference, options, &block)
|
5
|
+
new(model, reference, options, &block).call
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(model, reference, options, &block)
|
9
|
+
@model = model
|
10
|
+
@reference = reference
|
11
|
+
@options = options
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
model.after_destroy ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks
|
17
|
+
|
18
|
+
if behaviours.include?(:deltas)
|
19
|
+
model.before_save ThinkingSphinx::ActiveRecord::Callbacks::DeltaCallbacks
|
20
|
+
model.after_commit ThinkingSphinx::ActiveRecord::Callbacks::DeltaCallbacks
|
21
|
+
end
|
22
|
+
|
23
|
+
if behaviours.include?(:real_time)
|
24
|
+
model.after_save ThinkingSphinx::RealTime.callback_for(
|
25
|
+
reference, path, &block
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
if behaviours.include?(:updates)
|
30
|
+
model.after_update(
|
31
|
+
ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :model, :reference, :options, :block
|
39
|
+
|
40
|
+
def behaviours
|
41
|
+
options[:behaviours] || []
|
42
|
+
end
|
43
|
+
|
44
|
+
def path
|
45
|
+
options[:path] || []
|
46
|
+
end
|
47
|
+
end
|
@@ -2,9 +2,7 @@
|
|
2
2
|
|
3
3
|
class ThinkingSphinx::Commands::IndexRealTime < ThinkingSphinx::Commands::Base
|
4
4
|
def call
|
5
|
-
options[:indices]
|
6
|
-
ThinkingSphinx::RealTime::Populator.populate index
|
7
|
-
|
5
|
+
ThinkingSphinx::RealTime.processor.call options[:indices] do
|
8
6
|
command :rotate
|
9
7
|
end
|
10
8
|
end
|
@@ -87,9 +87,7 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
|
|
87
87
|
return if @preloaded_indices
|
88
88
|
|
89
89
|
index_paths.each do |path|
|
90
|
-
Dir["#{path}/**/*.rb"].sort.each
|
91
|
-
ActiveSupport::Dependencies.require_or_load file
|
92
|
-
end
|
90
|
+
Dir["#{path}/**/*.rb"].sort.each { |file| preload_index file }
|
93
91
|
end
|
94
92
|
|
95
93
|
normalise
|
@@ -99,6 +97,14 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
|
|
99
97
|
end
|
100
98
|
end
|
101
99
|
|
100
|
+
def preload_index(file)
|
101
|
+
if ActiveRecord::VERSION::MAJOR <= 5
|
102
|
+
ActiveSupport::Dependencies.require_or_load file
|
103
|
+
else
|
104
|
+
load file
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
102
108
|
def render
|
103
109
|
preload_indices
|
104
110
|
|
@@ -11,7 +11,6 @@ module ThinkingSphinx::Core::Index
|
|
11
11
|
|
12
12
|
def initialize(reference, options = {})
|
13
13
|
@reference = reference.to_sym
|
14
|
-
@docinfo = :extern unless config.settings["skip_docinfo"]
|
15
14
|
@options = options
|
16
15
|
@offset = config.next_offset(options[:offset_as] || reference)
|
17
16
|
@type = 'plain'
|
@@ -38,7 +37,11 @@ module ThinkingSphinx::Core::Index
|
|
38
37
|
end
|
39
38
|
|
40
39
|
def interpret_definition!
|
41
|
-
|
40
|
+
table_exists = model.table_exists?
|
41
|
+
unless table_exists
|
42
|
+
Rails.logger.info "No table exists for #{model}. Index can not be created"
|
43
|
+
return
|
44
|
+
end
|
42
45
|
return if @interpreted_definition
|
43
46
|
|
44
47
|
apply_defaults!
|
@@ -22,10 +22,6 @@ class ThinkingSphinx::Deletion
|
|
22
22
|
|
23
23
|
attr_reader :index, :ids
|
24
24
|
|
25
|
-
def document_ids_for_keys
|
26
|
-
ids.collect { |id| index.document_id_for_key id }
|
27
|
-
end
|
28
|
-
|
29
25
|
def execute(statement)
|
30
26
|
statement = statement.gsub(/\s*\n\s*/, ' ').strip
|
31
27
|
|
@@ -36,11 +32,28 @@ class ThinkingSphinx::Deletion
|
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
35
|
+
class PlainDeletion < ThinkingSphinx::Deletion
|
36
|
+
def perform
|
37
|
+
ids.each_slice(1000) do |some_ids|
|
38
|
+
execute <<-SQL
|
39
|
+
UPDATE #{name}
|
40
|
+
SET sphinx_deleted = 1
|
41
|
+
WHERE sphinx_internal_id IN (#{some_ids.join(', ')})
|
42
|
+
SQL
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
39
47
|
class RealtimeDeletion < ThinkingSphinx::Deletion
|
40
48
|
def perform
|
41
49
|
return unless callbacks_enabled?
|
42
50
|
|
43
|
-
|
51
|
+
ids.each_slice(1000) do |some_ids|
|
52
|
+
execute <<-SQL
|
53
|
+
DELETE FROM #{name}
|
54
|
+
WHERE sphinx_internal_id IN (#{some_ids.join(', ')})
|
55
|
+
SQL
|
56
|
+
end
|
44
57
|
end
|
45
58
|
|
46
59
|
private
|
@@ -54,16 +67,4 @@ class ThinkingSphinx::Deletion
|
|
54
67
|
ThinkingSphinx::Configuration.instance
|
55
68
|
end
|
56
69
|
end
|
57
|
-
|
58
|
-
class PlainDeletion < ThinkingSphinx::Deletion
|
59
|
-
def perform
|
60
|
-
document_ids_for_keys.each_slice(1000) do |document_ids|
|
61
|
-
execute <<-SQL
|
62
|
-
UPDATE #{name}
|
63
|
-
SET sphinx_deleted = 1
|
64
|
-
WHERE id IN (#{document_ids.join(', ')})
|
65
|
-
SQL
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
70
|
end
|
@@ -34,11 +34,11 @@ class ThinkingSphinx::IndexSet
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def classes
|
37
|
-
options[:classes] ||
|
37
|
+
options[:classes] || instances.collect(&:class)
|
38
38
|
end
|
39
39
|
|
40
40
|
def classes_specified?
|
41
|
-
classes.any? || references_specified?
|
41
|
+
instances.any? || classes.any? || references_specified?
|
42
42
|
end
|
43
43
|
|
44
44
|
def classes_and_ancestors
|
@@ -68,6 +68,10 @@ class ThinkingSphinx::IndexSet
|
|
68
68
|
all_indices.select { |index| references.include? index.reference }
|
69
69
|
end
|
70
70
|
|
71
|
+
def instances
|
72
|
+
options[:instances] || []
|
73
|
+
end
|
74
|
+
|
71
75
|
def mti_classes
|
72
76
|
classes.reject { |klass|
|
73
77
|
klass.column_names.include?(klass.inheritance_column)
|
@@ -76,7 +80,7 @@ class ThinkingSphinx::IndexSet
|
|
76
80
|
|
77
81
|
def references
|
78
82
|
options[:references] || classes_and_ancestors.collect { |klass|
|
79
|
-
|
83
|
+
self.class.reference_name(klass)
|
80
84
|
}
|
81
85
|
end
|
82
86
|
|
@@ -133,7 +133,7 @@ class ThinkingSphinx::Middlewares::SphinxQL <
|
|
133
133
|
|
134
134
|
def indices_match_classes?
|
135
135
|
indices.collect(&:reference).uniq.sort == classes.collect { |klass|
|
136
|
-
|
136
|
+
configuration.index_set_class.reference_name(klass)
|
137
137
|
}.sort
|
138
138
|
end
|
139
139
|
|
@@ -7,7 +7,15 @@ class ThinkingSphinx::Railtie < Rails::Railtie
|
|
7
7
|
|
8
8
|
initializer 'thinking_sphinx.initialisation' do
|
9
9
|
ActiveSupport.on_load(:active_record) do
|
10
|
-
ActiveRecord::Base.
|
10
|
+
ActiveRecord::Base.include ThinkingSphinx::ActiveRecord::Base
|
11
|
+
end
|
12
|
+
|
13
|
+
if ActiveSupport::VERSION::MAJOR > 5
|
14
|
+
if Rails.application.config.autoloader == :zeitwerk
|
15
|
+
ActiveSupport::Dependencies.autoload_paths.delete(
|
16
|
+
Rails.root.join("app", "indices").to_s
|
17
|
+
)
|
18
|
+
end
|
11
19
|
end
|
12
20
|
end
|
13
21
|
|
@@ -8,6 +8,22 @@ module ThinkingSphinx::RealTime
|
|
8
8
|
def self.callback_for(reference, path = [], &block)
|
9
9
|
Callbacks::RealTimeCallbacks.new reference.to_sym, path, &block
|
10
10
|
end
|
11
|
+
|
12
|
+
def self.populator
|
13
|
+
@populator ||= ThinkingSphinx::RealTime::Populator
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.populator=(value)
|
17
|
+
@populator = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.processor
|
21
|
+
@processor ||= ThinkingSphinx::RealTime::Processor
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.processor=(value)
|
25
|
+
@processor = value
|
26
|
+
end
|
11
27
|
end
|
12
28
|
|
13
29
|
require 'thinking_sphinx/real_time/property'
|
@@ -16,6 +32,7 @@ require 'thinking_sphinx/real_time/field'
|
|
16
32
|
require 'thinking_sphinx/real_time/index'
|
17
33
|
require 'thinking_sphinx/real_time/interpreter'
|
18
34
|
require 'thinking_sphinx/real_time/populator'
|
35
|
+
require 'thinking_sphinx/real_time/processor'
|
19
36
|
require 'thinking_sphinx/real_time/transcribe_instance'
|
20
37
|
require 'thinking_sphinx/real_time/transcriber'
|
21
38
|
require 'thinking_sphinx/real_time/translator'
|
@@ -16,10 +16,14 @@ class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def add_attribute(attribute)
|
19
|
+
@attributes.delete_if { |existing| existing.name == attribute.name }
|
20
|
+
|
19
21
|
@attributes << attribute
|
20
22
|
end
|
21
23
|
|
22
24
|
def add_field(field)
|
25
|
+
@fields.delete_if { |existing| existing.name == field.name }
|
26
|
+
|
23
27
|
@fields << field
|
24
28
|
end
|
25
29
|
|
@@ -5,16 +5,18 @@ class ThinkingSphinx::RealTime::Interpreter <
|
|
5
5
|
|
6
6
|
def has(*columns)
|
7
7
|
options = columns.extract_options!
|
8
|
-
|
8
|
+
|
9
|
+
columns.collect { |column|
|
9
10
|
::ThinkingSphinx::RealTime::Attribute.new column, options
|
10
|
-
}
|
11
|
+
}.each { |attribute| @index.add_attribute attribute }
|
11
12
|
end
|
12
13
|
|
13
14
|
def indexes(*columns)
|
14
15
|
options = columns.extract_options!
|
15
|
-
|
16
|
+
|
17
|
+
columns.collect { |column|
|
16
18
|
::ThinkingSphinx::RealTime::Field.new column, options
|
17
|
-
}
|
19
|
+
}.each { |field| @index.add_field field }
|
18
20
|
|
19
21
|
append_sortable_attributes columns, options if options[:sortable]
|
20
22
|
end
|
@@ -39,7 +41,7 @@ class ThinkingSphinx::RealTime::Interpreter <
|
|
39
41
|
def append_sortable_attributes(columns, options)
|
40
42
|
options = options.except(:sortable).merge(:type => :string)
|
41
43
|
|
42
|
-
|
44
|
+
columns.collect { |column|
|
43
45
|
aliased_name = options[:as]
|
44
46
|
aliased_name ||= column.__name.to_sym if column.respond_to?(:__name)
|
45
47
|
aliased_name ||= column
|
@@ -47,6 +49,6 @@ class ThinkingSphinx::RealTime::Interpreter <
|
|
47
49
|
options[:as] = "#{aliased_name}_sort".to_sym
|
48
50
|
|
49
51
|
::ThinkingSphinx::RealTime::Attribute.new column, options
|
50
|
-
}
|
52
|
+
}.each { |attribute| @index.add_attribute attribute }
|
51
53
|
end
|
52
54
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ThinkingSphinx::RealTime::Processor
|
4
|
+
def self.call(indices, &block)
|
5
|
+
new(indices).call(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(indices)
|
9
|
+
@indices = indices
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(&block)
|
13
|
+
subscribe_to_progress
|
14
|
+
|
15
|
+
indices.each do |index|
|
16
|
+
ThinkingSphinx::RealTime.populator.populate index
|
17
|
+
|
18
|
+
block.call
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :indices
|
25
|
+
|
26
|
+
def command
|
27
|
+
ThinkingSphinx::Commander.call(
|
28
|
+
command, configuration, options, stream
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def subscribe_to_progress
|
33
|
+
ThinkingSphinx::Subscribers::PopulatorSubscriber.
|
34
|
+
attach_to 'thinking_sphinx.real_time'
|
35
|
+
end
|
36
|
+
end
|
@@ -11,25 +11,8 @@ class ThinkingSphinx::RealTime::Transcriber
|
|
11
11
|
}
|
12
12
|
return unless items.present?
|
13
13
|
|
14
|
-
|
15
|
-
items
|
16
|
-
begin
|
17
|
-
values << ThinkingSphinx::RealTime::TranscribeInstance.call(
|
18
|
-
instance, index, properties
|
19
|
-
)
|
20
|
-
rescue ThinkingSphinx::TranscriptionError => error
|
21
|
-
instrument 'error', :error => error
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
insert = Riddle::Query::Insert.new index.name, columns, values
|
26
|
-
sphinxql = insert.replace!.to_sql
|
27
|
-
|
28
|
-
ThinkingSphinx::Logger.log :query, sphinxql do
|
29
|
-
ThinkingSphinx::Connection.take do |connection|
|
30
|
-
connection.execute sphinxql
|
31
|
-
end
|
32
|
-
end
|
14
|
+
delete_existing items
|
15
|
+
insert_replacements items
|
33
16
|
end
|
34
17
|
|
35
18
|
private
|
@@ -55,6 +38,27 @@ class ThinkingSphinx::RealTime::Transcriber
|
|
55
38
|
}
|
56
39
|
end
|
57
40
|
|
41
|
+
def delete_existing(instances)
|
42
|
+
ids = instances.collect(&index.primary_key.to_sym)
|
43
|
+
|
44
|
+
execute <<~SQL.strip
|
45
|
+
DELETE FROM #{@index.name} WHERE sphinx_internal_id IN (#{ids.join(', ')})
|
46
|
+
SQL
|
47
|
+
end
|
48
|
+
|
49
|
+
def execute(sphinxql)
|
50
|
+
ThinkingSphinx::Logger.log :query, sphinxql do
|
51
|
+
ThinkingSphinx::Connection.take do |connection|
|
52
|
+
connection.execute sphinxql
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def insert_replacements(instances)
|
58
|
+
insert = Riddle::Query::Insert.new index.name, columns, values(instances)
|
59
|
+
execute insert.replace!.to_sql
|
60
|
+
end
|
61
|
+
|
58
62
|
def instrument(message, options = {})
|
59
63
|
ActiveSupport::Notifications.instrument(
|
60
64
|
"#{message}.thinking_sphinx.real_time", options.merge(:index => index)
|
@@ -64,4 +68,16 @@ class ThinkingSphinx::RealTime::Transcriber
|
|
64
68
|
def properties
|
65
69
|
@properties ||= index.fields + index.attributes
|
66
70
|
end
|
71
|
+
|
72
|
+
def values(instances)
|
73
|
+
instances.each_with_object([]) do |instance, array|
|
74
|
+
begin
|
75
|
+
array << ThinkingSphinx::RealTime::TranscribeInstance.call(
|
76
|
+
instance, index, properties
|
77
|
+
)
|
78
|
+
rescue ThinkingSphinx::TranscriptionError => error
|
79
|
+
instrument 'error', :error => error
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
67
83
|
end
|