thinking-sphinx 4.4.1 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|