thinking-sphinx 4.3.2 → 5.2.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/.circleci/config.yml +152 -0
- data/.travis.yml +16 -21
- data/Appraisals +11 -20
- data/CHANGELOG.markdown +79 -0
- data/README.textile +15 -17
- data/bin/loadsphinx +30 -7
- data/lib/thinking_sphinx.rb +5 -4
- data/lib/thinking_sphinx/active_record.rb +1 -0
- 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/attribute/sphinx_presenter.rb +1 -1
- data/lib/thinking_sphinx/active_record/base.rb +17 -6
- data/lib/thinking_sphinx/active_record/callbacks/association_delta_callbacks.rb +21 -0
- 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 +59 -0
- data/lib/thinking_sphinx/commands/index_real_time.rb +1 -3
- data/lib/thinking_sphinx/connection.rb +4 -0
- data/lib/thinking_sphinx/connection/client.rb +6 -1
- data/lib/thinking_sphinx/core/index.rb +5 -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 +14 -1
- data/lib/thinking_sphinx/real_time.rb +17 -0
- data/lib/thinking_sphinx/real_time/index.rb +5 -3
- data/lib/thinking_sphinx/real_time/index/template.rb +12 -0
- data/lib/thinking_sphinx/real_time/interpreter.rb +8 -6
- data/lib/thinking_sphinx/real_time/populator.rb +5 -2
- data/lib/thinking_sphinx/real_time/processor.rb +36 -0
- data/lib/thinking_sphinx/real_time/transcriber.rb +41 -19
- data/lib/thinking_sphinx/real_time/translator.rb +1 -0
- data/lib/thinking_sphinx/settings.rb +13 -9
- data/lib/thinking_sphinx/subscribers/populator_subscriber.rb +0 -4
- 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/real_time_updates_spec.rb +2 -2
- data/spec/acceptance/sql_deltas_spec.rb +15 -3
- 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 +0 -12
- 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 -5
- metadata +17 -11
- data/spec/acceptance/connection_spec.rb +0 -25
data/lib/thinking_sphinx.rb
CHANGED
@@ -17,8 +17,6 @@ require 'active_support/core_ext/module/delegation'
|
|
17
17
|
require 'active_support/core_ext/module/attribute_accessors'
|
18
18
|
|
19
19
|
module ThinkingSphinx
|
20
|
-
MAXIMUM_STATEMENT_LENGTH = (2 ** 23) - 5
|
21
|
-
|
22
20
|
def self.count(query = '', options = {})
|
23
21
|
search_for_ids(query, options).total_entries
|
24
22
|
end
|
@@ -98,12 +96,15 @@ require 'thinking_sphinx/test'
|
|
98
96
|
require 'thinking_sphinx/utf8'
|
99
97
|
require 'thinking_sphinx/wildcard'
|
100
98
|
# Extended
|
101
|
-
require 'thinking_sphinx/active_record'
|
102
99
|
require 'thinking_sphinx/deltas'
|
103
100
|
require 'thinking_sphinx/distributed'
|
104
101
|
require 'thinking_sphinx/logger'
|
105
102
|
require 'thinking_sphinx/real_time'
|
106
103
|
|
107
|
-
|
104
|
+
if defined?(Rails::Railtie)
|
105
|
+
require 'thinking_sphinx/railtie'
|
106
|
+
else
|
107
|
+
require 'thinking_sphinx/active_record'
|
108
|
+
end
|
108
109
|
|
109
110
|
ThinkingSphinx.before_index_hooks << ThinkingSphinx::Hooks::GuardPresence
|
@@ -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'
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ThinkingSphinx::ActiveRecord::AssociationProxy
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
4
|
def search(query = nil, options = {})
|
7
5
|
perform_search super(*normalise_search_arguments(query, options))
|
8
6
|
end
|
@@ -12,6 +10,7 @@ module ThinkingSphinx::ActiveRecord::AssociationProxy
|
|
12
10
|
end
|
13
11
|
|
14
12
|
private
|
13
|
+
|
15
14
|
def normalise_search_arguments(query, options)
|
16
15
|
query, options = nil, query if query.is_a?(Hash)
|
17
16
|
options[:ignore_scopes] = true
|
@@ -31,7 +31,7 @@ class ThinkingSphinx::ActiveRecord::AssociationProxy::AttributeFinder
|
|
31
31
|
@indices ||= begin
|
32
32
|
configuration.preload_indices
|
33
33
|
configuration.indices_for_references(
|
34
|
-
*
|
34
|
+
*configuration.index_set_class.reference_name(@association.klass)
|
35
35
|
).reject &:distributed?
|
36
36
|
end
|
37
37
|
end
|
@@ -4,13 +4,24 @@ module ThinkingSphinx::ActiveRecord::Base
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
included do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
if ActiveRecord::VERSION::STRING.to_i >= 5
|
8
|
+
[
|
9
|
+
::ActiveRecord::Reflection::HasManyReflection,
|
10
|
+
::ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
11
|
+
].each do |reflection_class|
|
12
|
+
reflection_class.include DefaultReflectionAssociations
|
13
|
+
end
|
14
|
+
else
|
15
|
+
::ActiveRecord::Associations::CollectionProxy.include(
|
16
|
+
ThinkingSphinx::ActiveRecord::AssociationProxy
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
11
20
|
|
12
|
-
|
13
|
-
|
21
|
+
module DefaultReflectionAssociations
|
22
|
+
def extensions
|
23
|
+
super + [ThinkingSphinx::ActiveRecord::AssociationProxy]
|
24
|
+
end
|
14
25
|
end
|
15
26
|
|
16
27
|
module ClassMethods
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ThinkingSphinx::ActiveRecord::Callbacks::AssociationDeltaCallbacks
|
4
|
+
def initialize(path)
|
5
|
+
@path = path
|
6
|
+
end
|
7
|
+
|
8
|
+
def after_commit(instance)
|
9
|
+
Array(objects_for(instance)).each do |object|
|
10
|
+
object.update :delta => true unless object.frozen?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :path
|
17
|
+
|
18
|
+
def objects_for(instance)
|
19
|
+
path.inject(instance) { |object, method| object.send method }
|
20
|
+
end
|
21
|
+
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)
|
@@ -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
|
@@ -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
|
)
|
@@ -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,59 @@
|
|
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_destroy ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_delta_callbacks
|
31
|
+
if path.empty?
|
32
|
+
model.before_save ThinkingSphinx::ActiveRecord::Callbacks::DeltaCallbacks
|
33
|
+
model.after_commit ThinkingSphinx::ActiveRecord::Callbacks::DeltaCallbacks
|
34
|
+
else
|
35
|
+
model.after_commit(
|
36
|
+
ThinkingSphinx::ActiveRecord::Callbacks::AssociationDeltaCallbacks
|
37
|
+
.new(path)
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_real_time_callbacks
|
43
|
+
model.after_save ThinkingSphinx::RealTime.callback_for(
|
44
|
+
reference, path, &block
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_update_callbacks
|
49
|
+
model.after_update ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks
|
50
|
+
end
|
51
|
+
|
52
|
+
def behaviours
|
53
|
+
options[:behaviours] || []
|
54
|
+
end
|
55
|
+
|
56
|
+
def path
|
57
|
+
options[:path] || []
|
58
|
+
end
|
59
|
+
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
|
@@ -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'
|
@@ -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
|
@@ -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
|
|