thinking-sphinx 3.1.1 → 3.1.2

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