thinking-sphinx 3.1.1 → 3.1.2

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -6
  3. data/HISTORY +21 -0
  4. data/lib/thinking_sphinx.rb +1 -0
  5. data/lib/thinking_sphinx/active_record.rb +1 -0
  6. data/lib/thinking_sphinx/active_record/base.rb +3 -2
  7. data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +4 -0
  8. data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +4 -0
  9. data/lib/thinking_sphinx/active_record/filtered_reflection.rb +10 -2
  10. data/lib/thinking_sphinx/active_record/interpreter.rb +2 -1
  11. data/lib/thinking_sphinx/active_record/join_association.rb +13 -0
  12. data/lib/thinking_sphinx/active_record/log_subscriber.rb +8 -3
  13. data/lib/thinking_sphinx/active_record/polymorpher.rb +8 -1
  14. data/lib/thinking_sphinx/active_record/sql_builder.rb +19 -4
  15. data/lib/thinking_sphinx/active_record/sql_builder/statement.rb +19 -12
  16. data/lib/thinking_sphinx/active_record/sql_source.rb +2 -2
  17. data/lib/thinking_sphinx/capistrano/v3.rb +1 -1
  18. data/lib/thinking_sphinx/configuration.rb +1 -1
  19. data/lib/thinking_sphinx/connection.rb +4 -2
  20. data/lib/thinking_sphinx/controller.rb +3 -13
  21. data/lib/thinking_sphinx/core/index.rb +13 -3
  22. data/lib/thinking_sphinx/core/interpreter.rb +4 -0
  23. data/lib/thinking_sphinx/deletion.rb +5 -3
  24. data/lib/thinking_sphinx/errors.rb +3 -0
  25. data/lib/thinking_sphinx/facet.rb +3 -2
  26. data/lib/thinking_sphinx/facet_search.rb +6 -2
  27. data/lib/thinking_sphinx/guard.rb +6 -0
  28. data/lib/thinking_sphinx/guard/file.rb +26 -0
  29. data/lib/thinking_sphinx/guard/files.rb +38 -0
  30. data/lib/thinking_sphinx/masks/group_enumerators_mask.rb +4 -4
  31. data/lib/thinking_sphinx/masks/weight_enumerator_mask.rb +1 -1
  32. data/lib/thinking_sphinx/middlewares/sphinxql.rb +2 -2
  33. data/lib/thinking_sphinx/panes/weight_pane.rb +1 -1
  34. data/lib/thinking_sphinx/rake_interface.rb +20 -1
  35. data/lib/thinking_sphinx/real_time.rb +2 -2
  36. data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +10 -4
  37. data/lib/thinking_sphinx/real_time/interpreter.rb +2 -1
  38. data/lib/thinking_sphinx/real_time/transcriber.rb +1 -1
  39. data/lib/thinking_sphinx/search.rb +4 -0
  40. data/lib/thinking_sphinx/search/merger.rb +4 -0
  41. data/lib/thinking_sphinx/sphinxql.rb +12 -6
  42. data/lib/thinking_sphinx/tasks.rb +13 -3
  43. data/spec/acceptance/attribute_access_spec.rb +2 -2
  44. data/spec/acceptance/big_integers_spec.rb +11 -0
  45. data/spec/acceptance/facets_spec.rb +4 -1
  46. data/spec/acceptance/indexing_spec.rb +9 -0
  47. data/spec/acceptance/specifying_sql_spec.rb +1 -1
  48. data/spec/acceptance/sphinx_scopes_spec.rb +9 -0
  49. data/spec/internal/app/indices/user_index.rb +2 -0
  50. data/spec/thinking_sphinx/active_record/base_spec.rb +3 -1
  51. data/spec/thinking_sphinx/active_record/index_spec.rb +8 -0
  52. data/spec/thinking_sphinx/active_record/interpreter_spec.rb +7 -1
  53. data/spec/thinking_sphinx/active_record/polymorpher_spec.rb +16 -3
  54. data/spec/thinking_sphinx/configuration_spec.rb +18 -0
  55. data/spec/thinking_sphinx/facet_search_spec.rb +6 -6
  56. data/spec/thinking_sphinx/masks/scopes_mask_spec.rb +6 -1
  57. data/spec/thinking_sphinx/panes/weight_pane_spec.rb +1 -1
  58. data/spec/thinking_sphinx/rake_interface_spec.rb +62 -3
  59. data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +38 -4
  60. data/spec/thinking_sphinx/real_time/index_spec.rb +4 -0
  61. data/spec/thinking_sphinx/real_time/interpreter_spec.rb +7 -1
  62. data/thinking-sphinx.gemspec +1 -1
  63. metadata +7 -3
@@ -24,4 +24,13 @@ describe 'Indexing', :live => true do
24
24
 
25
25
  FileUtils.rm path
26
26
  end
27
+
28
+ it "cleans up temp files even when an exception is raised" do
29
+ FileUtils.mkdir_p Rails.root.join('db/sphinx/test')
30
+
31
+ index 'article_core'
32
+
33
+ file = Rails.root.join('db/sphinx/test/ts-article_core.tmp')
34
+ File.exist?(file).should be_false
35
+ end
27
36
  end
@@ -57,7 +57,7 @@ describe 'specifying SQL for index definitions' do
57
57
  index.render
58
58
 
59
59
  query = index.sources.first.sql_query
60
- query.should match(/GROUP BY .articles.\..id., .articles.\..title., .articles.\..id., lat/)
60
+ query.should match(/GROUP BY .articles.\..id., .?articles.?\..title., .?articles.?\..id., lat/)
61
61
  end
62
62
 
63
63
  it "handles WHERE clauses" do
@@ -66,4 +66,13 @@ describe 'Sphinx scopes', :live => true do
66
66
 
67
67
  Book.by_query('gods').count.should == 2
68
68
  end
69
+
70
+ it 'raises an exception when trying to modify a populated request' do
71
+ request = Book.by_query('gods')
72
+ request.count
73
+
74
+ expect { request.search('foo') }.to raise_error(
75
+ ThinkingSphinx::PopulatedResultsError
76
+ )
77
+ end
69
78
  end
@@ -2,4 +2,6 @@ ThinkingSphinx::Index.define :user, :with => :active_record do
2
2
  indexes name
3
3
 
4
4
  has articles.taggings.tag_id, :as => :tag_ids, :facet => true
5
+
6
+ set_property :big_document_ids => true
5
7
  end
@@ -108,10 +108,12 @@ describe ThinkingSphinx::ActiveRecord::Base do
108
108
  end
109
109
 
110
110
  describe '.search_count' do
111
- let(:search) { double('search', :options => {}, :total_entries => 12) }
111
+ let(:search) { double('search', :options => {}, :total_entries => 12,
112
+ :populated? => false) }
112
113
 
113
114
  before :each do
114
115
  ThinkingSphinx.stub :search => search
116
+ FileUtils.stub :mkdir_p => true
115
117
  end
116
118
 
117
119
  it "returns the search object's total entries count" do
@@ -144,6 +144,10 @@ describe ThinkingSphinx::ActiveRecord::Index do
144
144
 
145
145
  describe '#morphology' do
146
146
  context 'with a render' do
147
+ before :each do
148
+ FileUtils.stub :mkdir_p => true
149
+ end
150
+
147
151
  it "defaults to nil" do
148
152
  begin
149
153
  index.render
@@ -195,6 +199,10 @@ describe ThinkingSphinx::ActiveRecord::Index do
195
199
  end
196
200
 
197
201
  describe '#render' do
202
+ before :each do
203
+ FileUtils.stub :mkdir_p => true
204
+ end
205
+
198
206
  it "interprets the provided definition" do
199
207
  index.should_receive(:interpret_definition!).at_least(:once)
200
208
 
@@ -5,7 +5,7 @@ describe ThinkingSphinx::ActiveRecord::Interpreter do
5
5
  ThinkingSphinx::ActiveRecord::Interpreter.new index, block
6
6
  }
7
7
  let(:model) { double('model') }
8
- let(:index) { double('index', :append_source => source) }
8
+ let(:index) { double('index', :append_source => source, :options => {}) }
9
9
  let(:source) {
10
10
  Struct.new(:attributes, :fields, :associations, :groupings, :conditions).
11
11
  new([], [], [], [], [])
@@ -251,6 +251,12 @@ describe ThinkingSphinx::ActiveRecord::Interpreter do
251
251
  source.class.stub :settings => [:mysql_ssl_cert]
252
252
  end
253
253
 
254
+ it 'saves other settings as index options' do
255
+ instance.set_property :field_weights => {:name => 10}
256
+
257
+ index.options[:field_weights].should == {:name => 10}
258
+ end
259
+
254
260
  context 'index settings' do
255
261
  it "sets the provided setting" do
256
262
  index.should_receive(:morphology=).with('stem_en')
@@ -23,6 +23,10 @@ describe ThinkingSphinx::ActiveRecord::Polymorpher do
23
23
  ThinkingSphinx::ActiveRecord::FilteredReflection.
24
24
  stub(:clone_with_filter).
25
25
  and_return(article_reflection, animal_reflection)
26
+
27
+ if ActiveRecord::Reflection.respond_to?(:add_reflection)
28
+ ActiveRecord::Reflection.stub :add_reflection
29
+ end
26
30
  end
27
31
 
28
32
  it "creates a new reflection for each class" do
@@ -42,10 +46,19 @@ describe ThinkingSphinx::ActiveRecord::Polymorpher do
42
46
  end
43
47
 
44
48
  it "adds the new reflections to the end-of-stack model" do
45
- polymorpher.morph!
49
+ if ActiveRecord::Reflection.respond_to?(:add_reflection)
50
+ ActiveRecord::Reflection.should_receive(:add_reflection).
51
+ with(model, :foo_article, article_reflection)
52
+ ActiveRecord::Reflection.should_receive(:add_reflection).
53
+ with(model, :foo_animal, animal_reflection)
54
+
55
+ polymorpher.morph!
56
+ else
57
+ polymorpher.morph!
46
58
 
47
- model.reflections[:foo_article].should == article_reflection
48
- model.reflections[:foo_animal].should == animal_reflection
59
+ expect(model.reflections[:foo_article]).to eq(article_reflection)
60
+ expect(model.reflections[:foo_animal]).to eq(animal_reflection)
61
+ end
49
62
  end
50
63
 
51
64
  it "rebases each field" do
@@ -322,6 +322,24 @@ describe ThinkingSphinx::Configuration do
322
322
 
323
323
  config.render_to_file
324
324
  end
325
+
326
+ it "creates a directory at the binlog_path" do
327
+ FileUtils.stub :mkdir_p => true
328
+ config.stub :searchd => double(:binlog_path => '/path/to/binlog')
329
+
330
+ FileUtils.should_receive(:mkdir_p).with('/path/to/binlog')
331
+
332
+ config.render_to_file
333
+ end
334
+
335
+ it "skips creating a directory when the binlog_path is blank" do
336
+ FileUtils.stub :mkdir_p => true
337
+ config.stub :searchd => double(:binlog_path => '')
338
+
339
+ FileUtils.should_not_receive(:mkdir_p)
340
+
341
+ config.render_to_file
342
+ end
325
343
  end
326
344
 
327
345
  describe '#searchd' do
@@ -26,12 +26,12 @@ describe ThinkingSphinx::FacetSearch do
26
26
  DumbSearch = ::Struct.new(:query, :options) do
27
27
  def raw
28
28
  [{
29
- 'sphinx_internal_class' => 'Foo',
30
- 'price_bracket' => 3,
31
- 'tag_ids' => '1,2',
32
- 'category_id' => 11,
33
- 'sphinx_internal_count' => 5,
34
- 'sphinx_internal_group' => 2
29
+ 'sphinx_internal_class' => 'Foo',
30
+ 'price_bracket' => 3,
31
+ 'tag_ids' => '1,2',
32
+ 'category_id' => 11,
33
+ ThinkingSphinx::SphinxQL.count[:column] => 5,
34
+ ThinkingSphinx::SphinxQL.group_by[:column] => 2
35
35
  }]
36
36
  end
37
37
  end
@@ -5,9 +5,14 @@ end
5
5
  require 'thinking_sphinx/masks/scopes_mask'
6
6
 
7
7
  describe ThinkingSphinx::Masks::ScopesMask do
8
- let(:search) { double('search', :options => {}, :per_page => 20) }
8
+ let(:search) { double('search', :options => {}, :per_page => 20,
9
+ :populated? => false) }
9
10
  let(:mask) { ThinkingSphinx::Masks::ScopesMask.new search }
10
11
 
12
+ before :each do
13
+ FileUtils.stub :mkdir_p => true
14
+ end
15
+
11
16
  describe '#search' do
12
17
  it "replaces the query if one is supplied" do
13
18
  search.should_receive(:query=).with('bar')
@@ -12,7 +12,7 @@ describe ThinkingSphinx::Panes::WeightPane do
12
12
 
13
13
  describe '#weight' do
14
14
  it "returns the object's weight by default" do
15
- raw[ThinkingSphinx::SphinxQL.weight] = 101
15
+ raw[ThinkingSphinx::SphinxQL.weight[:column]] = 101
16
16
 
17
17
  pane.weight.should == 101
18
18
  end
@@ -9,7 +9,7 @@ describe ThinkingSphinx::RakeInterface do
9
9
  interface.stub(:puts => nil)
10
10
  end
11
11
 
12
- describe '#clear' do
12
+ describe '#clear_all' do
13
13
  let(:controller) { double 'controller' }
14
14
 
15
15
  before :each do
@@ -25,13 +25,50 @@ describe ThinkingSphinx::RakeInterface do
25
25
  it "removes the directory for the index files" do
26
26
  FileUtils.should_receive(:rm_r).with('/path/to/indices')
27
27
 
28
- interface.clear
28
+ interface.clear_all
29
29
  end
30
30
 
31
31
  it "removes the directory for the binlog files" do
32
32
  FileUtils.should_receive(:rm_r).with('/path/to/binlog')
33
33
 
34
- interface.clear
34
+ interface.clear_all
35
+ end
36
+ end
37
+
38
+ describe '#clear_real_time' do
39
+ let(:controller) { double 'controller' }
40
+ let(:index) {
41
+ double(:type => 'rt', :render => true, :path => '/path/to/my/index')
42
+ }
43
+
44
+ before :each do
45
+ configuration.stub(
46
+ :indices => [double(:type => 'plain'), index],
47
+ :searchd => double(:binlog_path => '/path/to/binlog')
48
+ )
49
+
50
+ Dir.stub :[] => ['foo.a', 'foo.b']
51
+ FileUtils.stub :rm_r => true, :rm => true
52
+ File.stub :exists? => true
53
+ end
54
+
55
+ it 'finds each file for real-time indices' do
56
+ Dir.should_receive(:[]).with('/path/to/my/index.*').and_return([])
57
+
58
+ interface.clear_real_time
59
+ end
60
+
61
+ it "removes each file for real-time indices" do
62
+ FileUtils.should_receive(:rm).with('foo.a')
63
+ FileUtils.should_receive(:rm).with('foo.b')
64
+
65
+ interface.clear_real_time
66
+ end
67
+
68
+ it "removes the directory for the binlog files" do
69
+ FileUtils.should_receive(:rm_r).with('/path/to/binlog')
70
+
71
+ interface.clear_real_time
35
72
  end
36
73
  end
37
74
 
@@ -195,4 +232,26 @@ describe ThinkingSphinx::RakeInterface do
195
232
  interface.stop
196
233
  end
197
234
  end
235
+
236
+ describe '#status' do
237
+ let(:controller) { double('controller') }
238
+
239
+ it "reports when the daemon is running" do
240
+ controller.stub :running? => true
241
+
242
+ interface.should_receive(:puts).
243
+ with('The Sphinx daemon searchd is currently running.')
244
+
245
+ interface.status
246
+ end
247
+
248
+ it "reports when the daemon is not running" do
249
+ controller.stub :running? => false
250
+
251
+ interface.should_receive(:puts).
252
+ with('The Sphinx daemon searchd is not currently running.')
253
+
254
+ interface.status
255
+ end
256
+ end
198
257
  end
@@ -4,7 +4,7 @@ describe ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks do
4
4
  let(:callbacks) {
5
5
  ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks.new :article
6
6
  }
7
- let(:instance) { double('instance', :id => 12) }
7
+ let(:instance) { double('instance', :id => 12, :persisted? => true) }
8
8
  let(:config) { double('config', :indices_for_references => [index],
9
9
  :settings => {}) }
10
10
  let(:index) { double('index', :name => 'my_index', :is_a? => true,
@@ -58,7 +58,7 @@ describe ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks do
58
58
  )
59
59
  }
60
60
  let(:instance) { double('instance', :id => 12, :user => user) }
61
- let(:user) { double('user', :id => 13) }
61
+ let(:user) { double('user', :id => 13, :persisted? => true) }
62
62
 
63
63
  it "creates an insert statement with all fields and attributes" do
64
64
  Riddle::Query::Insert.should_receive(:new).
@@ -89,8 +89,42 @@ describe ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks do
89
89
  }
90
90
  let(:instance) { double('instance', :id => 12,
91
91
  :readers => [user_a, user_b]) }
92
- let(:user_a) { double('user', :id => 13) }
93
- let(:user_b) { double('user', :id => 14) }
92
+ let(:user_a) { double('user', :id => 13, :persisted? => true) }
93
+ let(:user_b) { double('user', :id => 14, :persisted? => true) }
94
+
95
+ it "creates insert statements with all fields and attributes" do
96
+ Riddle::Query::Insert.should_receive(:new).twice.
97
+ with('my_index', ['id', 'name', 'created_at'], [123, 'Foo', time]).
98
+ and_return(insert)
99
+
100
+ callbacks.after_save instance
101
+ end
102
+
103
+ it "gets the document id for each reader" do
104
+ index.should_receive(:document_id_for_key).with(13).and_return(123)
105
+ index.should_receive(:document_id_for_key).with(14).and_return(123)
106
+
107
+ callbacks.after_save instance
108
+ end
109
+
110
+ it "translates values for each reader" do
111
+ field.should_receive(:translate).with(user_a).and_return('Foo')
112
+ field.should_receive(:translate).with(user_b).and_return('Foo')
113
+
114
+ callbacks.after_save instance
115
+ end
116
+ end
117
+
118
+ context 'with a block instead of a path' do
119
+ let(:callbacks) {
120
+ ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks.new(
121
+ :article
122
+ ) { |object| object.readers }
123
+ }
124
+ let(:instance) { double('instance', :id => 12,
125
+ :readers => [user_a, user_b]) }
126
+ let(:user_a) { double('user', :id => 13, :persisted? => true) }
127
+ let(:user_b) { double('user', :id => 14, :persisted? => true) }
94
128
 
95
129
  it "creates insert statements with all fields and attributes" do
96
130
  Riddle::Query::Insert.should_receive(:new).twice.
@@ -138,6 +138,10 @@ describe ThinkingSphinx::RealTime::Index do
138
138
  end
139
139
 
140
140
  describe '#render' do
141
+ before :each do
142
+ FileUtils.stub :mkdir_p => true
143
+ end
144
+
141
145
  it "interprets the provided definition" do
142
146
  index.should_receive(:interpret_definition!).at_least(:once)
143
147
 
@@ -5,7 +5,7 @@ describe ThinkingSphinx::RealTime::Interpreter do
5
5
  ThinkingSphinx::RealTime::Interpreter.new index, block
6
6
  }
7
7
  let(:model) { double('model') }
8
- let(:index) { Struct.new(:attributes, :fields).new([], []) }
8
+ let(:index) { Struct.new(:attributes, :fields, :options).new([], [], {}) }
9
9
  let(:block) { Proc.new { } }
10
10
 
11
11
  describe '.translate!' do
@@ -172,6 +172,12 @@ describe ThinkingSphinx::RealTime::Interpreter do
172
172
  index.class.stub :settings => [:morphology]
173
173
  end
174
174
 
175
+ it 'saves other settings as index options' do
176
+ instance.set_property :field_weights => {:name => 10}
177
+
178
+ index.options[:field_weights].should == {:name => 10}
179
+ end
180
+
175
181
  context 'index settings' do
176
182
  it "sets the provided setting" do
177
183
  index.should_receive(:morphology=).with('stem_en')
@@ -3,7 +3,7 @@ $:.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'thinking-sphinx'
6
- s.version = '3.1.1'
6
+ s.version = '3.1.2'
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Pat Allan"]
9
9
  s.email = ["pat@freelancing-gods.com"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Allan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-21 00:00:00.000000000 Z
11
+ date: 2014-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -196,6 +196,7 @@ files:
196
196
  - lib/thinking_sphinx/active_record/filtered_reflection.rb
197
197
  - lib/thinking_sphinx/active_record/index.rb
198
198
  - lib/thinking_sphinx/active_record/interpreter.rb
199
+ - lib/thinking_sphinx/active_record/join_association.rb
199
200
  - lib/thinking_sphinx/active_record/log_subscriber.rb
200
201
  - lib/thinking_sphinx/active_record/polymorpher.rb
201
202
  - lib/thinking_sphinx/active_record/property.rb
@@ -241,6 +242,9 @@ files:
241
242
  - lib/thinking_sphinx/frameworks.rb
242
243
  - lib/thinking_sphinx/frameworks/plain.rb
243
244
  - lib/thinking_sphinx/frameworks/rails.rb
245
+ - lib/thinking_sphinx/guard.rb
246
+ - lib/thinking_sphinx/guard/file.rb
247
+ - lib/thinking_sphinx/guard/files.rb
244
248
  - lib/thinking_sphinx/index.rb
245
249
  - lib/thinking_sphinx/index_set.rb
246
250
  - lib/thinking_sphinx/logger.rb
@@ -435,7 +439,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
435
439
  version: '0'
436
440
  requirements: []
437
441
  rubyforge_project: thinking-sphinx
438
- rubygems_version: 2.2.0
442
+ rubygems_version: 2.3.0
439
443
  signing_key:
440
444
  specification_version: 4
441
445
  summary: A smart wrapper over Sphinx for ActiveRecord