thinking-sphinx 2.0.12 → 2.0.13

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.
data/HISTORY CHANGED
@@ -1,3 +1,6 @@
1
+ 2.0.13 - August 10th 2012
2
+ * 1.4.13 changes.
3
+
1
4
  2.0.12 - May 14th 2012
2
5
  * STI fix when generating WHERE clauses for sql_query.
3
6
  * 1.4.12 changes.
@@ -61,6 +64,13 @@
61
64
  * Rails 3 support.
62
65
  * 1.4.0 changes.
63
66
 
67
+ 1.4.13 - August 10th 2012
68
+ * Sphinx 2.0.5 support.
69
+ * Hard retries for Sphinx exceptions (Andrew Hunter).
70
+ * Add support for association-filtered faceting (Andrew White).
71
+ * Cast PostgreSQL timestamps to their floored integers (instead of rounding up).
72
+ * Don't add array_accum to PostgreSQL v80311 (8.3.?) or newer.
73
+
64
74
  1.4.12 - May 14th 2012
65
75
  * Updating Riddle references to 1.5.2.
66
76
  * Can explicitly specify available types for STI tables instead of automatically discovering them with "SELECT DISTINCT type FROM <table>" (Cedric Maion).
@@ -2,16 +2,23 @@ module ThinkingSphinx
2
2
  module ActiveRecord
3
3
  module CollectionProxy
4
4
  def search(*args)
5
- options = args.extract_options!
6
- options[:with] ||= {}
7
- options[:with].merge! default_filter
5
+ proxy_association.klass.search(*association_args(args))
6
+ end
8
7
 
9
- args << options
10
- proxy_association.klass.search(*args)
8
+ def facets(*args)
9
+ proxy_association.klass.facets(*association_args(args))
11
10
  end
12
11
 
13
12
  private
14
13
 
14
+ def association_args(args)
15
+ options = args.extract_options!
16
+ options[:with] ||= {}
17
+ options[:with].merge! default_filter
18
+
19
+ args + [options]
20
+ end
21
+
15
22
  def attribute_for_foreign_key
16
23
  if proxy_association.reflection.through_reflection
17
24
  foreign_key = proxy_association.reflection.through_reflection.foreign_key
@@ -2,16 +2,23 @@ module ThinkingSphinx
2
2
  module ActiveRecord
3
3
  module HasManyAssociation
4
4
  def search(*args)
5
- options = args.extract_options!
5
+ @reflection.klass.search(*association_args(args))
6
+ end
7
+
8
+ def facets(*args)
9
+ @reflection.klass.facets(*association_args(args))
10
+ end
11
+
12
+ private
13
+
14
+ def association_args(args)
15
+ options = args.extract_options!
6
16
  options[:with] ||= {}
7
17
  options[:with].merge! default_filter
8
-
9
- args << options
10
- @reflection.klass.search(*args)
18
+
19
+ args + [options]
11
20
  end
12
21
 
13
- private
14
-
15
22
  def attribute_for_foreign_key
16
23
  foreign_key = @reflection.primary_key_name
17
24
  stack = [@reflection.options[:through]].compact
@@ -33,9 +33,9 @@ module ThinkingSphinx
33
33
 
34
34
  def cast_to_datetime(clause)
35
35
  if ThinkingSphinx::Configuration.instance.use_64_bit
36
- "cast(extract(epoch from #{clause}) as bigint)"
36
+ "cast(floor(extract(epoch from #{clause})) as bigint)"
37
37
  else
38
- "cast(extract(epoch from #{clause}) as int)"
38
+ "cast(floor(extract(epoch from #{clause})) as int)"
39
39
  end
40
40
  end
41
41
 
@@ -109,7 +109,7 @@ module ThinkingSphinx
109
109
  end
110
110
 
111
111
  def create_array_accum_function
112
- if server_version >= 80400
112
+ if server_version >= 80311
113
113
  return
114
114
  elsif server_version > 80200
115
115
  execute <<-SQL
@@ -9,7 +9,7 @@ module ThinkingSphinx
9
9
  require 'riddle/1.10'
10
10
  when /2.0.[12]/
11
11
  require 'riddle/2.0.1'
12
- when /2.0.3/, /2.0.4/, /2.1.\d/
12
+ when /2.0.[^12]/, /2.1.\d/
13
13
  require 'riddle/2.1.0'
14
14
  else
15
15
  documentation_link = %Q{
@@ -1,41 +1,41 @@
1
1
  module ThinkingSphinx
2
2
  class BundledSearch
3
3
  attr_reader :client
4
-
4
+
5
5
  def initialize
6
6
  @searches = []
7
7
  end
8
-
8
+
9
9
  def search(*args)
10
10
  @searches << ThinkingSphinx.search(*args)
11
11
  @searches.last.append_to client
12
12
  end
13
-
13
+
14
14
  def search_for_ids(*args)
15
15
  @searches << ThinkingSphinx.search_for_ids(*args)
16
16
  @searches.last.append_to client
17
17
  end
18
-
18
+
19
19
  def searches
20
20
  populate
21
21
  @searches
22
22
  end
23
-
23
+
24
24
  private
25
-
25
+
26
26
  def client
27
27
  @client ||= ThinkingSphinx::Configuration.instance.client
28
28
  end
29
-
29
+
30
30
  def populated?
31
31
  @populated
32
32
  end
33
-
33
+
34
34
  def populate
35
35
  return if populated?
36
-
36
+
37
37
  @populated = true
38
-
38
+
39
39
  client.run.each_with_index do |results, index|
40
40
  searches[index].populate_from_queue results
41
41
  end
@@ -28,6 +28,7 @@ module ThinkingSphinx
28
28
  # html remove elements:: ''
29
29
  # searchd_binary_name:: searchd
30
30
  # indexer_binary_name:: indexer
31
+ # hard_retry_count:: 0
31
32
  #
32
33
  # If you want to change these settings, create a YAML file at
33
34
  # config/sphinx.yml with settings for each environment, in a similar
@@ -64,7 +65,8 @@ module ThinkingSphinx
64
65
 
65
66
  attr_accessor :searchd_file_path, :allow_star, :app_root,
66
67
  :model_directories, :delayed_job_priority, :indexed_models, :use_64_bit,
67
- :touched_reindex_file, :stop_timeout, :version, :shuffle
68
+ :touched_reindex_file, :stop_timeout, :version, :shuffle,
69
+ :hard_retry_count
68
70
 
69
71
  attr_accessor :source_options, :index_options
70
72
 
@@ -111,6 +113,7 @@ module ThinkingSphinx
111
113
  self.delayed_job_priority = 0
112
114
  self.indexed_models = []
113
115
  self.shuffle = false
116
+ self.hard_retry_count = 0
114
117
 
115
118
  self.source_options = {}
116
119
  self.index_options = {
@@ -420,27 +420,40 @@ module ThinkingSphinx
420
420
  def populate
421
421
  return if @populated
422
422
  @populated = true
423
+ retries = hard_retries
423
424
 
424
- retry_on_stale_index do
425
- begin
426
- log query do
427
- @results = client.query query, indexes, comment
425
+ begin
426
+ retry_on_stale_index do
427
+ begin
428
+ log query do
429
+ @results = client.query query, indexes, comment
430
+ end
431
+ total = @results[:total_found].to_i
432
+ log "Found #{total} result#{'s' unless total == 1}"
433
+
434
+ log "Sphinx Daemon returned warning: #{warning}" if warning?
435
+
436
+ if error?
437
+ log "Sphinx Daemon returned error: #{error}"
438
+ raise SphinxError.new(error, @results) unless options[:ignore_errors]
439
+ end
440
+ rescue Errno::ECONNREFUSED => err
441
+ raise ThinkingSphinx::ConnectionError,
442
+ 'Connection to Sphinx Daemon (searchd) failed.'
428
443
  end
429
- total = @results[:total_found].to_i
430
- log "Found #{total} result#{'s' unless total == 1}"
431
-
432
- log "Sphinx Daemon returned warning: #{warning}" if warning?
433
444
 
434
- if error?
435
- log "Sphinx Daemon returned error: #{error}"
436
- raise SphinxError.new(error, @results) unless options[:ignore_errors]
437
- end
438
- rescue Errno::ECONNREFUSED => err
439
- raise ThinkingSphinx::ConnectionError,
440
- 'Connection to Sphinx Daemon (searchd) failed.'
445
+ compose_results
446
+ end
447
+ rescue => e
448
+ log 'Caught Sphinx exception: %s (%s %s left)' % [
449
+ e.message, retries, (retries == 1 ? 'try' : 'tries')
450
+ ]
451
+ retries -= 1
452
+ if retries >= 0
453
+ retry
454
+ else
455
+ raise e
441
456
  end
442
-
443
- compose_results
444
457
  end
445
458
  end
446
459
 
@@ -861,6 +874,10 @@ module ThinkingSphinx
861
874
  end
862
875
  end
863
876
 
877
+ def hard_retries
878
+ options[:hard_retry_count] || config.hard_retry_count
879
+ end
880
+
864
881
  def include_for_class(klass)
865
882
  includes = options[:include] || klass.sphinx_index_options[:include]
866
883
 
@@ -1,3 +1,3 @@
1
1
  module ThinkingSphinx
2
- Version = '2.0.12'
2
+ Version = '2.0.13'
3
3
  end
@@ -41,6 +41,46 @@ describe 'ThinkingSphinx::ActiveRecord::HasManyAssociation' do
41
41
  end
42
42
  end
43
43
 
44
+ describe "facets method" do
45
+ before :each do
46
+ Friendship.stub!(:facets => true)
47
+
48
+ @person = Person.find(:first)
49
+ @index = Friendship.sphinx_indexes.first
50
+ end
51
+
52
+ it "should raise an error if the required attribute doesn't exist" do
53
+ @index.stub!(:attributes => [])
54
+
55
+ lambda { @person.friendships.facets "test" }.should raise_error(RuntimeError)
56
+ end
57
+
58
+ it "should add a filter for the attribute into a normal facets call" do
59
+ Friendship.should_receive(:facets) do |query, options|
60
+ options[:with][:person_id].should == @person.id
61
+ end
62
+
63
+ @person.friendships.facets "test"
64
+ end
65
+
66
+ it "should add a filter for an aliased attribute into a normal facets call" do
67
+ @team = CricketTeam.new
68
+ @team.stub!(:id => 1)
69
+
70
+ Person.should_receive(:facets).with do |query, options|
71
+ options[:with][:team_id].should == @team.id
72
+ end
73
+
74
+ @team.people.facets "test"
75
+ end
76
+
77
+ it "should define indexes for the reflection class" do
78
+ Friendship.should_receive(:define_indexes)
79
+
80
+ @person.friendships.facets 'test'
81
+ end
82
+ end
83
+
44
84
  describe "search method for has_many :through" do
45
85
  before :each do
46
86
  Person.stub!(:search => true)
@@ -75,6 +115,40 @@ describe 'ThinkingSphinx::ActiveRecord::HasManyAssociation' do
75
115
  end
76
116
  end
77
117
 
118
+ describe "facets method for has_many :through" do
119
+ before :each do
120
+ Person.stub!(:facets => true)
121
+
122
+ @person = Person.find(:first)
123
+ @index = Person.sphinx_indexes.first
124
+ end
125
+
126
+ it "should raise an error if the required attribute doesn't exist" do
127
+ @index.stub!(:attributes => [])
128
+
129
+ lambda { @person.friends.facets "test" }.should raise_error(RuntimeError)
130
+ end
131
+
132
+ it "should add a filter for the attribute into a normal facets call" do
133
+ Person.should_receive(:facets).with do |query, options|
134
+ options[:with][:friendly_ids].should == @person.id
135
+ end
136
+
137
+ @person.friends.facets "test"
138
+ end
139
+
140
+ it "should add a filter for an aliased attribute into a normal facets call" do
141
+ @team = FootballTeam.new
142
+ @team.stub!(:id => 1)
143
+
144
+ Person.should_receive(:facets).with do |query, options|
145
+ options[:with][:football_team_id].should == @team.id
146
+ end
147
+
148
+ @team.people.facets "test"
149
+ end
150
+ end
151
+
78
152
  describe 'filtering sphinx scopes' do
79
153
  before :each do
80
154
  Friendship.stub!(:search => Friendship)
@@ -62,6 +62,22 @@ describe ThinkingSphinx::AutoVersion do
62
62
  ThinkingSphinx::AutoVersion.detect
63
63
  end
64
64
 
65
+ it "should require 2.1.0 if using Sphinx 2.0.3" do
66
+ ThinkingSphinx::AutoVersion.should_receive(:require).
67
+ with('riddle/2.1.0')
68
+
69
+ @config.stub!(:version => '2.0.4-release')
70
+ ThinkingSphinx::AutoVersion.detect
71
+ end
72
+
73
+ it "should require 2.1.0 if using Sphinx 2.0.3" do
74
+ ThinkingSphinx::AutoVersion.should_receive(:require).
75
+ with('riddle/2.1.0')
76
+
77
+ @config.stub!(:version => '2.0.5-release')
78
+ ThinkingSphinx::AutoVersion.detect
79
+ end
80
+
65
81
  it "should require 2.1.0 if using Sphinx 2.1.0 dev" do
66
82
  ThinkingSphinx::AutoVersion.should_receive(:require).
67
83
  with('riddle/2.1.0')
@@ -411,6 +411,38 @@ describe ThinkingSphinx::Search do
411
411
  'baz @foo bar @(foo,bar) baz', :star => true
412
412
  ).first
413
413
  end
414
+
415
+ it "should try retry query up to the hard_retry_count option times if it catches an exception" do
416
+ @client.should_receive(:query).exactly(4).and_raise("Test Exception")
417
+
418
+ expect { ThinkingSphinx::Search.new(:hard_retry_count => 3).first }.
419
+ to raise_error("Test Exception")
420
+ end
421
+
422
+ it "should not retry query if hard_retry_count option is not set" do
423
+ @client.should_receive(:query).exactly(1).and_raise("Test Exception")
424
+
425
+ expect { ThinkingSphinx::Search.new.first }.
426
+ to raise_error("Test Exception")
427
+ end
428
+
429
+ it "should allow the hard_retry_count to be globally set as a configuration option" do
430
+ @config.hard_retry_count = 2
431
+
432
+ @client.should_receive(:query).exactly(3).and_raise("Test Exception")
433
+
434
+ expect { ThinkingSphinx::Search.new.first }.
435
+ to raise_error("Test Exception")
436
+ end
437
+
438
+ it "should give priority to the hard_retry_count search option over the globally configured option" do
439
+ @config.hard_retry_count = 4
440
+
441
+ @client.should_receive(:query).exactly(2).and_raise("Test Exception")
442
+
443
+ expect { ThinkingSphinx::Search.new(:hard_retry_count => 1).first }.
444
+ to raise_error("Test Exception")
445
+ end
414
446
  end
415
447
 
416
448
  describe 'comment' do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
8
  - 0
9
- - 12
10
- version: 2.0.12
9
+ - 13
10
+ version: 2.0.13
11
11
  platform: ruby
12
12
  authors:
13
13
  - Pat Allan
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-05-14 00:00:00 Z
18
+ date: 2012-08-10 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  type: :runtime
@@ -40,12 +40,12 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- hash: 7
43
+ hash: 5
44
44
  segments:
45
45
  - 1
46
46
  - 5
47
- - 2
48
- version: 1.5.2
47
+ - 3
48
+ version: 1.5.3
49
49
  version_requirements: *id002
50
50
  prerelease: false
51
51
  name: riddle