thinking-sphinx 2.0.12 → 2.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +10 -0
- data/lib/thinking_sphinx/active_record/collection_proxy.rb +12 -5
- data/lib/thinking_sphinx/active_record/has_many_association.rb +13 -6
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +3 -3
- data/lib/thinking_sphinx/auto_version.rb +1 -1
- data/lib/thinking_sphinx/bundled_search.rb +10 -10
- data/lib/thinking_sphinx/configuration.rb +4 -1
- data/lib/thinking_sphinx/search.rb +34 -17
- data/lib/thinking_sphinx/version.rb +1 -1
- data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +74 -0
- data/spec/thinking_sphinx/auto_version_spec.rb +16 -0
- data/spec/thinking_sphinx/search_spec.rb +32 -0
- metadata +7 -7
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
|
-
|
6
|
-
|
7
|
-
options[:with].merge! default_filter
|
5
|
+
proxy_association.klass.search(*association_args(args))
|
6
|
+
end
|
8
7
|
|
9
|
-
|
10
|
-
proxy_association.klass.
|
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
|
-
|
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
|
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 >=
|
112
|
+
if server_version >= 80311
|
113
113
|
return
|
114
114
|
elsif server_version > 80200
|
115
115
|
execute <<-SQL
|
@@ -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
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
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
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
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
|
|
@@ -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:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 2.0.
|
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-
|
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:
|
43
|
+
hash: 5
|
44
44
|
segments:
|
45
45
|
- 1
|
46
46
|
- 5
|
47
|
-
-
|
48
|
-
version: 1.5.
|
47
|
+
- 3
|
48
|
+
version: 1.5.3
|
49
49
|
version_requirements: *id002
|
50
50
|
prerelease: false
|
51
51
|
name: riddle
|