thinking-sphinx 4.4.1 → 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 +12 -21
- data/Appraisals +1 -16
- data/CHANGELOG.markdown +20 -0
- data/README.textile +14 -16
- data/bin/loadsphinx +5 -0
- data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +1 -1
- data/lib/thinking_sphinx/active_record/base.rb +2 -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/core/index.rb +1 -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 +1 -1
- 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/transcriber.rb +35 -19
- 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/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 -3
- metadata +8 -7
|
@@ -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,7 @@ 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
11
|
end
|
|
12
12
|
|
|
13
13
|
if ActiveSupport::VERSION::MAJOR > 5
|
|
@@ -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
|
|
@@ -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
|
|
@@ -52,7 +52,7 @@ describe '64 bit document ids', :live => true do
|
|
|
52
52
|
context 'with Real-Time' do
|
|
53
53
|
it 'handles large 32 bit integers with an offset multiplier' do
|
|
54
54
|
product = Product.create! :name => "Widget"
|
|
55
|
-
product.
|
|
55
|
+
product.update :id => 980190962
|
|
56
56
|
expect(
|
|
57
57
|
Product.search('widget', :indices => ['product_core']).to_a
|
|
58
58
|
).to eq([product])
|
|
@@ -34,7 +34,7 @@ describe "Merging deltas", :live => true do
|
|
|
34
34
|
Book.search("Space", :indices => ["book_core"]).to_a
|
|
35
35
|
).to eq([race])
|
|
36
36
|
|
|
37
|
-
race.reload.
|
|
37
|
+
race.reload.update :title => "The Hate Race"
|
|
38
38
|
sleep 0.25
|
|
39
39
|
expect(
|
|
40
40
|
Book.search("Race", :indices => ["book_delta"]).to_a
|
|
@@ -11,7 +11,7 @@ describe 'Updates to records in real-time indices', :live => true do
|
|
|
11
11
|
|
|
12
12
|
it "handles attributes for sortable fields accordingly" do
|
|
13
13
|
product = Product.create! :name => 'Red Fish'
|
|
14
|
-
product.
|
|
14
|
+
product.update :name => 'Blue Fish'
|
|
15
15
|
|
|
16
16
|
expect(Product.search('blue fish', :indices => ['product_core']).to_a).
|
|
17
17
|
to eq([product])
|
|
@@ -22,7 +22,7 @@ describe 'Updates to records in real-time indices', :live => true do
|
|
|
22
22
|
|
|
23
23
|
expect(Admin::Person.search('Death').to_a).to eq([person])
|
|
24
24
|
|
|
25
|
-
person.
|
|
25
|
+
person.update :name => 'Mort'
|
|
26
26
|
|
|
27
27
|
expect(Admin::Person.search('Death').to_a).to be_empty
|
|
28
28
|
expect(Admin::Person.search('Mort').to_a).to eq([person])
|
|
@@ -25,7 +25,7 @@ describe 'SQL delta indexing', :live => true do
|
|
|
25
25
|
|
|
26
26
|
expect(Book.search('Harry').to_a).to eq([book])
|
|
27
27
|
|
|
28
|
-
book.reload.
|
|
28
|
+
book.reload.update(:author => 'Terry Pratchett')
|
|
29
29
|
sleep 0.25
|
|
30
30
|
|
|
31
31
|
expect(Book.search('Terry').to_a).to eq([book])
|
|
@@ -37,7 +37,7 @@ describe 'SQL delta indexing', :live => true do
|
|
|
37
37
|
|
|
38
38
|
expect(Book.search('Harry').to_a).to eq([book])
|
|
39
39
|
|
|
40
|
-
book.reload.
|
|
40
|
+
book.reload.update(:author => 'Terry Pratchett')
|
|
41
41
|
sleep 0.25
|
|
42
42
|
|
|
43
43
|
expect(Book.search('Harry')).to be_empty
|
|
@@ -49,7 +49,7 @@ describe 'SQL delta indexing', :live => true do
|
|
|
49
49
|
|
|
50
50
|
expect(Album.search('Whitloms').to_a).to eq([album])
|
|
51
51
|
|
|
52
|
-
album.reload.
|
|
52
|
+
album.reload.update(:artist => 'The Whitlams')
|
|
53
53
|
sleep 0.25
|
|
54
54
|
|
|
55
55
|
expect(Book.search('Whitloms')).to be_empty
|
|
@@ -10,7 +10,7 @@ describe 'Suspend deltas for a given action', :live => true do
|
|
|
10
10
|
expect(Book.search('Harry').to_a).to eq([book])
|
|
11
11
|
|
|
12
12
|
ThinkingSphinx::Deltas.suspend :book do
|
|
13
|
-
book.reload.
|
|
13
|
+
book.reload.update(:author => 'Terry Pratchett')
|
|
14
14
|
sleep 0.25
|
|
15
15
|
|
|
16
16
|
expect(Book.search('Terry').to_a).to eq([])
|
|
@@ -27,7 +27,7 @@ describe 'Suspend deltas for a given action', :live => true do
|
|
|
27
27
|
expect(Book.search('Harry').to_a).to eq([book])
|
|
28
28
|
|
|
29
29
|
ThinkingSphinx::Deltas.suspend :book do
|
|
30
|
-
book.reload.
|
|
30
|
+
book.reload.update(:author => 'Terry Pratchett')
|
|
31
31
|
sleep 0.25
|
|
32
32
|
|
|
33
33
|
expect(Book.search('Terry').to_a).to eq([])
|
|
@@ -44,7 +44,7 @@ describe 'Suspend deltas for a given action', :live => true do
|
|
|
44
44
|
expect(Book.search('Harry').to_a).to eq([book])
|
|
45
45
|
|
|
46
46
|
ThinkingSphinx::Deltas.suspend_and_update :book do
|
|
47
|
-
book.reload.
|
|
47
|
+
book.reload.update(:author => 'Terry Pratchett')
|
|
48
48
|
sleep 0.25
|
|
49
49
|
|
|
50
50
|
expect(Book.search('Terry').to_a).to eq([])
|
|
@@ -6,7 +6,9 @@ class Album < ActiveRecord::Base
|
|
|
6
6
|
before_validation :set_id, :on => :create
|
|
7
7
|
before_validation :set_integer_id, :on => :create
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
ThinkingSphinx::Callbacks.append(
|
|
10
|
+
self, :behaviours => [:sql, :real_time, :deltas]
|
|
11
|
+
)
|
|
10
12
|
|
|
11
13
|
validates :id, :presence => true, :uniqueness => true
|
|
12
14
|
validates :integer_id, :presence => true, :uniqueness => true
|
|
@@ -19,12 +19,13 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks do
|
|
|
19
19
|
let(:klass) { double(:name => 'Article') }
|
|
20
20
|
let(:configuration) { double('configuration',
|
|
21
21
|
:settings => {'attribute_updates' => true},
|
|
22
|
-
:indices_for_references => [index]) }
|
|
22
|
+
:indices_for_references => [index], :index_set_class => set_class) }
|
|
23
23
|
let(:connection) { double('connection', :execute => '') }
|
|
24
24
|
let(:index) { double 'index', :name => 'article_core',
|
|
25
25
|
:sources => [source], :document_id_for_key => 3, :distributed? => false,
|
|
26
26
|
:type => 'plain', :primary_key => :id}
|
|
27
27
|
let(:source) { double('source', :attributes => []) }
|
|
28
|
+
let(:set_class) { double(:reference_name => :article) }
|
|
28
29
|
|
|
29
30
|
before :each do
|
|
30
31
|
stub_const 'ThinkingSphinx::Configuration',
|
|
@@ -94,18 +94,6 @@ describe ThinkingSphinx::ActiveRecord::Index do
|
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
describe '#docinfo' do
|
|
98
|
-
it "defaults to extern" do
|
|
99
|
-
expect(index.docinfo).to eq(:extern)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it "can be disabled" do
|
|
103
|
-
config.settings["skip_docinfo"] = true
|
|
104
|
-
|
|
105
|
-
expect(index.docinfo).to be_nil
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
97
|
describe '#document_id_for_key' do
|
|
110
98
|
it "calculates the document id based on offset and number of indices" do
|
|
111
99
|
allow(config).to receive_message_chain(:indices, :count).and_return(5)
|
|
@@ -15,8 +15,13 @@ describe ThinkingSphinx::ActiveRecord::Interpreter do
|
|
|
15
15
|
let(:block) { Proc.new { } }
|
|
16
16
|
|
|
17
17
|
before :each do
|
|
18
|
-
allow(ThinkingSphinx::ActiveRecord::SQLSource).to receive_messages
|
|
19
|
-
|
|
18
|
+
allow(ThinkingSphinx::ActiveRecord::SQLSource).to receive_messages(
|
|
19
|
+
:new => source
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
allow(source).to receive_messages(
|
|
23
|
+
:model => model, :add_attribute => nil, :add_field => nil
|
|
24
|
+
)
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
describe '.translate!' do
|
|
@@ -94,17 +99,15 @@ describe ThinkingSphinx::ActiveRecord::Interpreter do
|
|
|
94
99
|
end
|
|
95
100
|
|
|
96
101
|
it "adds an attribute to the source" do
|
|
97
|
-
|
|
102
|
+
expect(source).to receive(:add_attribute).with(attribute)
|
|
98
103
|
|
|
99
|
-
|
|
104
|
+
instance.has column
|
|
100
105
|
end
|
|
101
106
|
|
|
102
107
|
it "adds multiple attributes when passed multiple columns" do
|
|
103
|
-
|
|
108
|
+
expect(source).to receive(:add_attribute).with(attribute).twice
|
|
104
109
|
|
|
105
|
-
|
|
106
|
-
saved_attribute == attribute
|
|
107
|
-
}.length).to eq(2)
|
|
110
|
+
instance.has column, column
|
|
108
111
|
end
|
|
109
112
|
end
|
|
110
113
|
|
|
@@ -144,17 +147,15 @@ describe ThinkingSphinx::ActiveRecord::Interpreter do
|
|
|
144
147
|
end
|
|
145
148
|
|
|
146
149
|
it "adds a field to the source" do
|
|
147
|
-
|
|
150
|
+
expect(source).to receive(:add_field).with(field)
|
|
148
151
|
|
|
149
|
-
|
|
152
|
+
instance.indexes column
|
|
150
153
|
end
|
|
151
154
|
|
|
152
155
|
it "adds multiple fields when passed multiple columns" do
|
|
153
|
-
|
|
156
|
+
expect(source).to receive(:add_field).with(field).twice
|
|
154
157
|
|
|
155
|
-
|
|
156
|
-
saved_field == field
|
|
157
|
-
}.length).to eq(2)
|
|
158
|
+
instance.indexes column, column
|
|
158
159
|
end
|
|
159
160
|
end
|
|
160
161
|
|