thinking-sphinx 4.4.1 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +218 -0
- data/.travis.yml +12 -21
- data/Appraisals +16 -19
- data/CHANGELOG.markdown +93 -0
- data/README.textile +17 -17
- data/bin/loadsphinx +22 -4
- data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +1 -1
- data/lib/thinking_sphinx/active_record/attribute/sphinx_presenter.rb +1 -1
- data/lib/thinking_sphinx/active_record/base.rb +2 -6
- data/lib/thinking_sphinx/active_record/callbacks/association_delta_callbacks.rb +21 -0
- data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +6 -2
- 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/template.rb +2 -2
- data/lib/thinking_sphinx/active_record/sql_source.rb +12 -0
- data/lib/thinking_sphinx/active_record.rb +3 -0
- data/lib/thinking_sphinx/callbacks/appender.rb +63 -0
- data/lib/thinking_sphinx/callbacks.rb +9 -0
- data/lib/thinking_sphinx/connection/client.rb +6 -1
- data/lib/thinking_sphinx/connection.rb +4 -0
- data/lib/thinking_sphinx/core/index.rb +1 -2
- data/lib/thinking_sphinx/deletion.rb +18 -17
- data/lib/thinking_sphinx/errors.rb +1 -1
- data/lib/thinking_sphinx/index_set.rb +7 -3
- data/lib/thinking_sphinx/middlewares/sphinxql.rb +1 -1
- data/lib/thinking_sphinx/railtie.rb +20 -7
- data/lib/thinking_sphinx/real_time/index/template.rb +12 -0
- data/lib/thinking_sphinx/real_time/index.rb +5 -3
- data/lib/thinking_sphinx/real_time/interpreter.rb +8 -6
- data/lib/thinking_sphinx/real_time/populator.rb +4 -1
- data/lib/thinking_sphinx/real_time/transcriber.rb +41 -19
- data/lib/thinking_sphinx/real_time/translator.rb +1 -0
- data/lib/thinking_sphinx/search/stale_ids_exception.rb +2 -1
- data/lib/thinking_sphinx/search.rb +1 -1
- data/lib/thinking_sphinx/settings.rb +13 -9
- data/lib/thinking_sphinx/sinatra.rb +1 -1
- data/lib/thinking_sphinx/test.rb +1 -1
- data/lib/thinking_sphinx.rb +0 -3
- data/spec/acceptance/attribute_access_spec.rb +10 -2
- data/spec/acceptance/big_integers_spec.rb +1 -1
- data/spec/acceptance/geosearching_spec.rb +13 -3
- data/spec/acceptance/merging_spec.rb +1 -1
- data/spec/acceptance/paginating_search_results_spec.rb +18 -2
- data/spec/acceptance/real_time_updates_spec.rb +2 -2
- data/spec/acceptance/searching_with_filters_spec.rb +3 -3
- data/spec/acceptance/sql_deltas_spec.rb +16 -4
- data/spec/acceptance/support/sphinx_controller.rb +6 -4
- data/spec/acceptance/support/sphinx_helpers.rb +4 -4
- data/spec/acceptance/suspended_deltas_spec.rb +3 -3
- data/spec/internal/app/indices/article_index.rb +0 -1
- data/spec/internal/app/indices/colour_index.rb +7 -0
- 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/colour.rb +2 -0
- data/spec/internal/app/models/product.rb +1 -1
- data/spec/internal/app/models/tee.rb +5 -0
- data/spec/internal/app/models/user.rb +2 -0
- data/spec/internal/config/database.yml +6 -1
- data/spec/internal/db/schema.rb +1 -0
- data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +2 -1
- data/spec/thinking_sphinx/active_record/index_spec.rb +3 -13
- data/spec/thinking_sphinx/active_record/interpreter_spec.rb +15 -14
- data/spec/thinking_sphinx/active_record/sql_source_spec.rb +41 -3
- data/spec/thinking_sphinx/connection/mri_spec.rb +49 -0
- 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 +51 -13
- 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 -3
- metadata +17 -12
- data/spec/acceptance/connection_spec.rb +0 -25
@@ -3,7 +3,11 @@
|
|
3
3
|
class ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks <
|
4
4
|
ThinkingSphinx::Callbacks
|
5
5
|
|
6
|
-
callbacks :after_destroy, :after_rollback
|
6
|
+
callbacks :after_commit, :after_destroy, :after_rollback
|
7
|
+
|
8
|
+
def after_commit
|
9
|
+
delete_from_sphinx
|
10
|
+
end
|
7
11
|
|
8
12
|
def after_destroy
|
9
13
|
delete_from_sphinx
|
@@ -27,7 +31,7 @@ class ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks <
|
|
27
31
|
|
28
32
|
def indices
|
29
33
|
ThinkingSphinx::Configuration.instance.index_set_class.new(
|
30
|
-
:classes => [instance.class]
|
34
|
+
:instances => [instance], :classes => [instance.class]
|
31
35
|
).to_a
|
32
36
|
end
|
33
37
|
end
|
@@ -43,8 +43,9 @@ class ThinkingSphinx::ActiveRecord::Callbacks::DeltaCallbacks <
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def indices
|
46
|
-
@indices ||= config.index_set_class.new(
|
47
|
-
|
46
|
+
@indices ||= config.index_set_class.new(
|
47
|
+
:instances => [instance], :classes => [instance.class]
|
48
|
+
).select { |index| index.type == "plain" }
|
48
49
|
end
|
49
50
|
|
50
51
|
def new_or_changed?
|
@@ -13,15 +13,15 @@ class ThinkingSphinx::ActiveRecord::Interpreter <
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def has(*columns)
|
16
|
-
|
16
|
+
build_properties(
|
17
17
|
::ThinkingSphinx::ActiveRecord::Attribute, columns
|
18
|
-
)
|
18
|
+
).each { |attribute| __source.add_attribute attribute }
|
19
19
|
end
|
20
20
|
|
21
21
|
def indexes(*columns)
|
22
|
-
|
22
|
+
build_properties(
|
23
23
|
::ThinkingSphinx::ActiveRecord::Field, columns
|
24
|
-
)
|
24
|
+
).each { |field| __source.add_field field }
|
25
25
|
end
|
26
26
|
|
27
27
|
def join(*columns)
|
@@ -18,14 +18,14 @@ class ThinkingSphinx::ActiveRecord::SQLSource::Template
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def add_attribute(column, name, type, options = {})
|
21
|
-
source.
|
21
|
+
source.add_attribute ThinkingSphinx::ActiveRecord::Attribute.new(
|
22
22
|
source.model, ThinkingSphinx::ActiveRecord::Column.new(column),
|
23
23
|
options.merge(:as => name, :type => type)
|
24
24
|
)
|
25
25
|
end
|
26
26
|
|
27
27
|
def add_field(column, name, options = {})
|
28
|
-
source.
|
28
|
+
source.add_field ThinkingSphinx::ActiveRecord::Field.new(
|
29
29
|
source.model, ThinkingSphinx::ActiveRecord::Column.new(column),
|
30
30
|
options.merge(:as => name)
|
31
31
|
)
|
@@ -39,6 +39,18 @@ module ThinkingSphinx
|
|
39
39
|
@adapter ||= DatabaseAdapters.adapter_for(@model)
|
40
40
|
end
|
41
41
|
|
42
|
+
def add_attribute(attribute)
|
43
|
+
attributes.delete_if { |existing| existing.name == attribute.name }
|
44
|
+
|
45
|
+
attributes << attribute
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_field(field)
|
49
|
+
fields.delete_if { |existing| existing.name == field.name }
|
50
|
+
|
51
|
+
fields << field
|
52
|
+
end
|
53
|
+
|
42
54
|
def delta_processor
|
43
55
|
options[:delta_processor].try(:new, adapter, @options[:delta_options] || {})
|
44
56
|
end
|
@@ -29,6 +29,7 @@ require 'thinking_sphinx/active_record/source_joins'
|
|
29
29
|
require 'thinking_sphinx/active_record/sql_builder'
|
30
30
|
require 'thinking_sphinx/active_record/sql_source'
|
31
31
|
|
32
|
+
require 'thinking_sphinx/active_record/callbacks/association_delta_callbacks'
|
32
33
|
require 'thinking_sphinx/active_record/callbacks/delete_callbacks'
|
33
34
|
require 'thinking_sphinx/active_record/callbacks/delta_callbacks'
|
34
35
|
require 'thinking_sphinx/active_record/callbacks/update_callbacks'
|
@@ -39,3 +40,5 @@ require 'thinking_sphinx/active_record/depolymorph/conditions_reflection'
|
|
39
40
|
require 'thinking_sphinx/active_record/depolymorph/overridden_reflection'
|
40
41
|
require 'thinking_sphinx/active_record/depolymorph/scoped_reflection'
|
41
42
|
require 'thinking_sphinx/active_record/filter_reflection'
|
43
|
+
|
44
|
+
ActiveRecord::Base.include ThinkingSphinx::ActiveRecord::Base
|
@@ -0,0 +1,63 @@
|
|
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
|
+
add_core_callbacks
|
17
|
+
add_delta_callbacks if behaviours.include?(:deltas)
|
18
|
+
add_real_time_callbacks if behaviours.include?(:real_time)
|
19
|
+
add_update_callbacks if behaviours.include?(:updates)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :model, :reference, :options, :block
|
25
|
+
|
26
|
+
def add_core_callbacks
|
27
|
+
model.after_commit(
|
28
|
+
ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks,
|
29
|
+
on: :destroy
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_delta_callbacks
|
34
|
+
if path.empty?
|
35
|
+
model.before_save ThinkingSphinx::ActiveRecord::Callbacks::DeltaCallbacks
|
36
|
+
model.after_commit ThinkingSphinx::ActiveRecord::Callbacks::DeltaCallbacks
|
37
|
+
else
|
38
|
+
model.after_commit(
|
39
|
+
ThinkingSphinx::ActiveRecord::Callbacks::AssociationDeltaCallbacks
|
40
|
+
.new(path)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_real_time_callbacks
|
46
|
+
model.after_commit(
|
47
|
+
ThinkingSphinx::RealTime.callback_for(reference, path, &block),
|
48
|
+
on: [:create, :update]
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_update_callbacks
|
53
|
+
model.after_update ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks
|
54
|
+
end
|
55
|
+
|
56
|
+
def behaviours
|
57
|
+
options[:behaviours] || []
|
58
|
+
end
|
59
|
+
|
60
|
+
def path
|
61
|
+
options[:path] || []
|
62
|
+
end
|
63
|
+
end
|
@@ -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"
|
@@ -39,7 +39,7 @@ class ThinkingSphinx::Connection::Client
|
|
39
39
|
private
|
40
40
|
|
41
41
|
def check(statements)
|
42
|
-
if statements.length >
|
42
|
+
if statements.length > maximum_statement_length
|
43
43
|
exception = ThinkingSphinx::QueryLengthError.new
|
44
44
|
exception.statement = statements
|
45
45
|
raise exception
|
@@ -56,6 +56,11 @@ class ThinkingSphinx::Connection::Client
|
|
56
56
|
@client = nil
|
57
57
|
end
|
58
58
|
|
59
|
+
def maximum_statement_length
|
60
|
+
@maximum_statement_length ||= ThinkingSphinx::Configuration.instance.
|
61
|
+
settings['maximum_statement_length']
|
62
|
+
end
|
63
|
+
|
59
64
|
def perform(statements)
|
60
65
|
results_for statements
|
61
66
|
rescue => error
|
@@ -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'
|
@@ -40,7 +39,7 @@ module ThinkingSphinx::Core::Index
|
|
40
39
|
def interpret_definition!
|
41
40
|
table_exists = model.table_exists?
|
42
41
|
unless table_exists
|
43
|
-
Rails.logger.info "No table exists for #{model}. Index can not be created"
|
42
|
+
Rails.logger.info "No table exists for #{model}. Index can not be created"
|
44
43
|
return
|
45
44
|
end
|
46
45
|
return if @interpreted_definition
|
@@ -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
|
@@ -38,7 +38,7 @@ end
|
|
38
38
|
class ThinkingSphinx::QueryLengthError < ThinkingSphinx::SphinxError
|
39
39
|
def message
|
40
40
|
<<-MESSAGE
|
41
|
-
The supplied SphinxQL statement is #{statement.length} characters long. The maximum allowed length is #{ThinkingSphinx::
|
41
|
+
The supplied SphinxQL statement is #{statement.length} characters long. The maximum allowed length is #{ThinkingSphinx::Configuration.instance.settings['maximum_statement_length']}.
|
42
42
|
|
43
43
|
If this error has been raised during real-time index population, it's probably due to overly large batches of records being processed at once. The default is 1000, but you can lower it on a per-environment basis in config/thinking_sphinx.yml:
|
44
44
|
|
@@ -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
|
|
@@ -5,21 +5,34 @@ class ThinkingSphinx::Railtie < Rails::Railtie
|
|
5
5
|
ThinkingSphinx::Configuration.reset
|
6
6
|
end
|
7
7
|
|
8
|
+
config.after_initialize do
|
9
|
+
require 'thinking_sphinx/active_record'
|
10
|
+
end
|
11
|
+
|
8
12
|
initializer 'thinking_sphinx.initialisation' do
|
9
13
|
ActiveSupport.on_load(:active_record) do
|
10
|
-
|
14
|
+
require 'thinking_sphinx/active_record'
|
11
15
|
end
|
12
16
|
|
13
|
-
if
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
)
|
18
|
-
end
|
17
|
+
if zeitwerk?
|
18
|
+
ActiveSupport::Dependencies.autoload_paths.delete(
|
19
|
+
Rails.root.join("app", "indices").to_s
|
20
|
+
)
|
19
21
|
end
|
22
|
+
|
23
|
+
Rails.application.config.eager_load_paths -=
|
24
|
+
ThinkingSphinx::Configuration.instance.index_paths
|
25
|
+
Rails.application.config.eager_load_paths.freeze
|
20
26
|
end
|
21
27
|
|
22
28
|
rake_tasks do
|
23
29
|
load File.expand_path('../tasks.rb', __FILE__)
|
24
30
|
end
|
31
|
+
|
32
|
+
def zeitwerk?
|
33
|
+
return true if ActiveSupport::VERSION::MAJOR >= 7
|
34
|
+
return false if ActiveSupport::VERSION::MAJOR <= 5
|
35
|
+
|
36
|
+
Rails.application.config.autoloader == :zeitwerk
|
37
|
+
end
|
25
38
|
end
|
@@ -13,6 +13,10 @@ class ThinkingSphinx::RealTime::Index::Template
|
|
13
13
|
add_attribute primary_key, :sphinx_internal_id, :bigint
|
14
14
|
add_attribute class_column, :sphinx_internal_class, :string, :facet => true
|
15
15
|
add_attribute 0, :sphinx_deleted, :integer
|
16
|
+
|
17
|
+
if tidying?
|
18
|
+
add_attribute -> (_) { Time.current.to_i }, :sphinx_updated_at, :timestamp
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
private
|
@@ -34,7 +38,15 @@ class ThinkingSphinx::RealTime::Index::Template
|
|
34
38
|
[:class, :name]
|
35
39
|
end
|
36
40
|
|
41
|
+
def config
|
42
|
+
ThinkingSphinx::Configuration.instance
|
43
|
+
end
|
44
|
+
|
37
45
|
def primary_key
|
38
46
|
index.primary_key.to_sym
|
39
47
|
end
|
48
|
+
|
49
|
+
def tidying?
|
50
|
+
config.settings["real_time_tidy"]
|
51
|
+
end
|
40
52
|
end
|
@@ -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
|
|
@@ -61,12 +65,10 @@ class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
|
|
61
65
|
|
62
66
|
def collection_for(attribute)
|
63
67
|
case attribute.type
|
64
|
-
when :integer, :boolean
|
68
|
+
when :integer, :boolean, :timestamp
|
65
69
|
attribute.multi? ? @rt_attr_multi : @rt_attr_uint
|
66
70
|
when :string
|
67
71
|
@rt_attr_string
|
68
|
-
when :timestamp
|
69
|
-
@rt_attr_timestamp
|
70
72
|
when :float
|
71
73
|
@rt_attr_float
|
72
74
|
when :bigint
|
@@ -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
|
@@ -7,6 +7,7 @@ class ThinkingSphinx::RealTime::Populator
|
|
7
7
|
|
8
8
|
def initialize(index)
|
9
9
|
@index = index
|
10
|
+
@started_at = Time.current
|
10
11
|
end
|
11
12
|
|
12
13
|
def populate
|
@@ -17,12 +18,14 @@ class ThinkingSphinx::RealTime::Populator
|
|
17
18
|
instrument 'populated', :instances => instances
|
18
19
|
end
|
19
20
|
|
21
|
+
transcriber.clear_before(started_at) if configuration.settings["real_time_tidy"]
|
22
|
+
|
20
23
|
instrument 'finish_populating'
|
21
24
|
end
|
22
25
|
|
23
26
|
private
|
24
27
|
|
25
|
-
attr_reader :index
|
28
|
+
attr_reader :index, :started_at
|
26
29
|
|
27
30
|
delegate :controller, :batch_size, :to => :configuration
|
28
31
|
delegate :scope, :to => :index
|
@@ -5,31 +5,20 @@ class ThinkingSphinx::RealTime::Transcriber
|
|
5
5
|
@index = index
|
6
6
|
end
|
7
7
|
|
8
|
+
def clear_before(time)
|
9
|
+
execute <<~SQL.strip
|
10
|
+
DELETE FROM #{@index.name} WHERE sphinx_updated_at < #{time.to_i}
|
11
|
+
SQL
|
12
|
+
end
|
13
|
+
|
8
14
|
def copy(*instances)
|
9
15
|
items = instances.select { |instance|
|
10
16
|
instance.persisted? && copy?(instance)
|
11
17
|
}
|
12
18
|
return unless items.present?
|
13
19
|
|
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
|
20
|
+
delete_existing items
|
21
|
+
insert_replacements items
|
33
22
|
end
|
34
23
|
|
35
24
|
private
|
@@ -55,6 +44,27 @@ class ThinkingSphinx::RealTime::Transcriber
|
|
55
44
|
}
|
56
45
|
end
|
57
46
|
|
47
|
+
def delete_existing(instances)
|
48
|
+
ids = instances.collect(&index.primary_key.to_sym)
|
49
|
+
|
50
|
+
execute <<~SQL.strip
|
51
|
+
DELETE FROM #{@index.name} WHERE sphinx_internal_id IN (#{ids.join(', ')})
|
52
|
+
SQL
|
53
|
+
end
|
54
|
+
|
55
|
+
def execute(sphinxql)
|
56
|
+
ThinkingSphinx::Logger.log :query, sphinxql do
|
57
|
+
ThinkingSphinx::Connection.take do |connection|
|
58
|
+
connection.execute sphinxql
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def insert_replacements(instances)
|
64
|
+
insert = Riddle::Query::Insert.new index.name, columns, values(instances)
|
65
|
+
execute insert.replace!.to_sql
|
66
|
+
end
|
67
|
+
|
58
68
|
def instrument(message, options = {})
|
59
69
|
ActiveSupport::Notifications.instrument(
|
60
70
|
"#{message}.thinking_sphinx.real_time", options.merge(:index => index)
|
@@ -64,4 +74,16 @@ class ThinkingSphinx::RealTime::Transcriber
|
|
64
74
|
def properties
|
65
75
|
@properties ||= index.fields + index.attributes
|
66
76
|
end
|
77
|
+
|
78
|
+
def values(instances)
|
79
|
+
instances.each_with_object([]) do |instance, array|
|
80
|
+
begin
|
81
|
+
array << ThinkingSphinx::RealTime::TranscribeInstance.call(
|
82
|
+
instance, index, properties
|
83
|
+
)
|
84
|
+
rescue ThinkingSphinx::TranscriptionError => error
|
85
|
+
instrument 'error', :error => error
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
67
89
|
end
|
@@ -9,6 +9,7 @@ class ThinkingSphinx::Search::StaleIdsException < StandardError
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def message
|
12
|
-
"Record IDs found by Sphinx but not by ActiveRecord : #{ids.join(', ')}"
|
12
|
+
"Record IDs found by Sphinx but not by ActiveRecord : #{ids.join(', ')}\n" \
|
13
|
+
"https://freelancing-gods.com/thinking-sphinx/v5/common_issues.html#record-ids"
|
13
14
|
end
|
14
15
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class ThinkingSphinx::Search < Array
|
4
4
|
CORE_METHODS = %w( == class class_eval extend frozen? id instance_eval
|
5
|
-
instance_of? instance_values instance_variable_defined?
|
5
|
+
instance_exec instance_of? instance_values instance_variable_defined?
|
6
6
|
instance_variable_get instance_variable_set instance_variables is_a?
|
7
7
|
kind_of? member? method methods nil? object_id respond_to?
|
8
8
|
respond_to_missing? send should should_not type )
|