hariton-thinking-sphinx 1.2.7.0
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/LICENCE +20 -0
- data/README.textile +156 -0
- data/VERSION.yml +4 -0
- data/lib/thinking_sphinx.rb +210 -0
- data/lib/thinking_sphinx/active_record.rb +298 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
- data/lib/thinking_sphinx/active_record/delta.rb +87 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +29 -0
- data/lib/thinking_sphinx/active_record/scopes.rb +39 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +42 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +54 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +136 -0
- data/lib/thinking_sphinx/association.rb +164 -0
- data/lib/thinking_sphinx/attribute.rb +329 -0
- data/lib/thinking_sphinx/class_facet.rb +15 -0
- data/lib/thinking_sphinx/configuration.rb +282 -0
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas.rb +30 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +68 -0
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +30 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
- data/lib/thinking_sphinx/deploy/capistrano.rb +100 -0
- data/lib/thinking_sphinx/excerpter.rb +22 -0
- data/lib/thinking_sphinx/facet.rb +108 -0
- data/lib/thinking_sphinx/facet_search.rb +134 -0
- data/lib/thinking_sphinx/field.rb +82 -0
- data/lib/thinking_sphinx/index.rb +99 -0
- data/lib/thinking_sphinx/index/builder.rb +287 -0
- data/lib/thinking_sphinx/index/faux_column.rb +110 -0
- data/lib/thinking_sphinx/property.rb +160 -0
- data/lib/thinking_sphinx/rails_additions.rb +150 -0
- data/lib/thinking_sphinx/search.rb +671 -0
- data/lib/thinking_sphinx/search_methods.rb +421 -0
- data/lib/thinking_sphinx/source.rb +150 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
- data/lib/thinking_sphinx/source/sql.rb +128 -0
- data/lib/thinking_sphinx/tasks.rb +165 -0
- data/rails/init.rb +14 -0
- data/spec/lib/thinking_sphinx/active_record/delta_spec.rb +136 -0
- data/spec/lib/thinking_sphinx/active_record/has_many_association_spec.rb +53 -0
- data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +96 -0
- data/spec/lib/thinking_sphinx/active_record_spec.rb +354 -0
- data/spec/lib/thinking_sphinx/association_spec.rb +246 -0
- data/spec/lib/thinking_sphinx/attribute_spec.rb +465 -0
- data/spec/lib/thinking_sphinx/configuration_spec.rb +268 -0
- data/spec/lib/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/lib/thinking_sphinx/excerpter_spec.rb +49 -0
- data/spec/lib/thinking_sphinx/facet_search_spec.rb +176 -0
- data/spec/lib/thinking_sphinx/facet_spec.rb +302 -0
- data/spec/lib/thinking_sphinx/field_spec.rb +154 -0
- data/spec/lib/thinking_sphinx/index/builder_spec.rb +355 -0
- data/spec/lib/thinking_sphinx/index/faux_column_spec.rb +30 -0
- data/spec/lib/thinking_sphinx/index_spec.rb +45 -0
- data/spec/lib/thinking_sphinx/rails_additions_spec.rb +203 -0
- data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
- data/spec/lib/thinking_sphinx/search_spec.rb +993 -0
- data/spec/lib/thinking_sphinx/source_spec.rb +217 -0
- data/spec/lib/thinking_sphinx_spec.rb +161 -0
- data/tasks/distribution.rb +49 -0
- data/tasks/rails.rake +1 -0
- data/tasks/testing.rb +78 -0
- data/vendor/after_commit/LICENSE +20 -0
- data/vendor/after_commit/README +16 -0
- data/vendor/after_commit/Rakefile +22 -0
- data/vendor/after_commit/init.rb +8 -0
- data/vendor/after_commit/lib/after_commit.rb +45 -0
- data/vendor/after_commit/lib/after_commit/active_record.rb +114 -0
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
- data/vendor/after_commit/test/after_commit_test.rb +53 -0
- data/vendor/delayed_job/lib/delayed/job.rb +251 -0
- data/vendor/delayed_job/lib/delayed/message_sending.rb +7 -0
- data/vendor/delayed_job/lib/delayed/performable_method.rb +55 -0
- data/vendor/delayed_job/lib/delayed/worker.rb +54 -0
- data/vendor/riddle/lib/riddle.rb +30 -0
- data/vendor/riddle/lib/riddle/client.rb +719 -0
- data/vendor/riddle/lib/riddle/client/filter.rb +53 -0
- data/vendor/riddle/lib/riddle/client/message.rb +70 -0
- data/vendor/riddle/lib/riddle/client/response.rb +94 -0
- data/vendor/riddle/lib/riddle/configuration.rb +33 -0
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +49 -0
- data/vendor/riddle/lib/riddle/configuration/index.rb +146 -0
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +46 -0
- data/vendor/riddle/lib/riddle/configuration/section.rb +43 -0
- data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
- data/vendor/riddle/lib/riddle/configuration/sql_source.rb +39 -0
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
- data/vendor/riddle/lib/riddle/controller.rb +54 -0
- metadata +169 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::SearchMethods do
|
|
4
|
+
it "should be included into models with indexes" do
|
|
5
|
+
Alpha.included_modules.should include(ThinkingSphinx::SearchMethods)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "should not be included into models that don't have indexes" do
|
|
9
|
+
Gamma.included_modules.should_not include(ThinkingSphinx::SearchMethods)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '.search_context' do
|
|
13
|
+
it "should return nil if not within a model" do
|
|
14
|
+
ThinkingSphinx.search_context.should be_nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should return the model if within one" do
|
|
18
|
+
Alpha.search_context.should == Alpha
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe '.search' do
|
|
23
|
+
it "should return an instance of ThinkingSphinx::Search" do
|
|
24
|
+
Alpha.search.class.should == ThinkingSphinx::Search
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should set the classes option if not already set" do
|
|
28
|
+
search = Alpha.search
|
|
29
|
+
search.options[:classes].should == [Alpha]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "shouldn't set the classes option if already defined" do
|
|
33
|
+
search = Alpha.search :classes => [Beta]
|
|
34
|
+
search.options[:classes].should == [Beta]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should default to nil for the classes options" do
|
|
38
|
+
ThinkingSphinx.search.options[:classes].should be_nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '.search_for_ids' do
|
|
43
|
+
it "should return an instance of ThinkingSphinx::Search" do
|
|
44
|
+
Alpha.search.class.should == ThinkingSphinx::Search
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should set the classes option if not already set" do
|
|
48
|
+
search = Alpha.search_for_ids
|
|
49
|
+
search.options[:classes].should == [Alpha]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "shouldn't set the classes option if already defined" do
|
|
53
|
+
search = Alpha.search_for_ids :classes => [Beta]
|
|
54
|
+
search.options[:classes].should == [Beta]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should set ids_only to true" do
|
|
58
|
+
search = Alpha.search_for_ids
|
|
59
|
+
search.options[:ids_only].should be_true
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '.search_for_id' do
|
|
64
|
+
before :each do
|
|
65
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
66
|
+
@client = Riddle::Client.new
|
|
67
|
+
|
|
68
|
+
@config.stub!(:client => @client)
|
|
69
|
+
@client.stub!(:query => {:matches => [], :total_found => 0})
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should set the id range to the given id value" do
|
|
73
|
+
ThinkingSphinx.search_for_id(101, 'alpha_core')
|
|
74
|
+
|
|
75
|
+
@client.id_range.should == (101..101)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should not make any calls to the database" do
|
|
79
|
+
Alpha.should_not_receive(:find)
|
|
80
|
+
|
|
81
|
+
ThinkingSphinx.search_for_id(101, 'alpha_core', :classes => [Alpha])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "should return true if there is a record" do
|
|
85
|
+
@client.stub!(:query => {:matches => [
|
|
86
|
+
{:attributes => {'sphinx_internal_id' => 100}}
|
|
87
|
+
], :total_found => 1})
|
|
88
|
+
|
|
89
|
+
ThinkingSphinx.search_for_id(101, 'alpha_core').should be_true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should return false if there isn't a record" do
|
|
93
|
+
ThinkingSphinx.search_for_id(101, 'alpha_core').should be_false
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe '.count' do
|
|
98
|
+
before :each do
|
|
99
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
100
|
+
@client = Riddle::Client.new
|
|
101
|
+
|
|
102
|
+
@config.stub!(:client => @client)
|
|
103
|
+
@client.stub!(:query => {:matches => [], :total_found => 42})
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should fall through to ActiveRecord if called on a class" do
|
|
107
|
+
@client.should_not_receive(:query)
|
|
108
|
+
|
|
109
|
+
Alpha.count
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "should return the total number of results if called globally" do
|
|
113
|
+
ThinkingSphinx.count.should == 42
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe '.search_count' do
|
|
118
|
+
before :each do
|
|
119
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
120
|
+
@client = Riddle::Client.new
|
|
121
|
+
|
|
122
|
+
@config.stub!(:client => @client)
|
|
123
|
+
@client.stub!(:query => {:matches => [], :total_found => 42})
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "should return the total number of results" do
|
|
127
|
+
Alpha.search_count.should == 42
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "should not make any calls to the database" do
|
|
131
|
+
Alpha.should_not_receive(:find)
|
|
132
|
+
|
|
133
|
+
Alpha.search_count
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe '.facets' do
|
|
138
|
+
it "should return a FacetSearch instance" do
|
|
139
|
+
Alpha.facets.should be_a(ThinkingSphinx::FacetSearch)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "should set the classes option if not already set" do
|
|
143
|
+
facets = Alpha.facets
|
|
144
|
+
facets.options[:classes].should == [Alpha]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it "shouldn't set the classes option if already defined" do
|
|
148
|
+
facets = Alpha.facets :classes => [Beta]
|
|
149
|
+
facets.options[:classes].should == [Beta]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,993 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
require 'will_paginate/collection'
|
|
3
|
+
|
|
4
|
+
describe ThinkingSphinx::Search do
|
|
5
|
+
before :each do
|
|
6
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
7
|
+
@client = Riddle::Client.new
|
|
8
|
+
|
|
9
|
+
@config.stub!(:client => @client)
|
|
10
|
+
@client.stub!(:query => {:matches => [], :total_found => 41, :total => 41})
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "not request results from the client if not accessing items" do
|
|
14
|
+
@config.should_not_receive(:client)
|
|
15
|
+
|
|
16
|
+
ThinkingSphinx::Search.new.class
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should request results if access is required" do
|
|
20
|
+
@config.should_receive(:client)
|
|
21
|
+
|
|
22
|
+
ThinkingSphinx::Search.new.first
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#respond_to?' do
|
|
26
|
+
it "should respond to Array methods" do
|
|
27
|
+
ThinkingSphinx::Search.new.respond_to?(:each).should be_true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should respond to Search methods" do
|
|
31
|
+
ThinkingSphinx::Search.new.respond_to?(:per_page).should be_true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '#populated?' do
|
|
36
|
+
before :each do
|
|
37
|
+
@search = ThinkingSphinx::Search.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should be false if the client request has not been made" do
|
|
41
|
+
@search.populated?.should be_false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should be true once the client request has been made" do
|
|
45
|
+
@search.first
|
|
46
|
+
@search.populated?.should be_true
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe '#results' do
|
|
51
|
+
it "should populate search results before returning" do
|
|
52
|
+
@search = ThinkingSphinx::Search.new
|
|
53
|
+
@search.populated?.should be_false
|
|
54
|
+
|
|
55
|
+
@search.results
|
|
56
|
+
@search.populated?.should be_true
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe '#method_missing' do
|
|
61
|
+
before :each do
|
|
62
|
+
Alpha.sphinx_scope(:by_name) { |name|
|
|
63
|
+
{:conditions => {:name => name}}
|
|
64
|
+
}
|
|
65
|
+
Alpha.sphinx_scope(:ids_only) { {:ids_only => true} }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
after :each do
|
|
69
|
+
Alpha.remove_sphinx_scopes
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should handle Array methods" do
|
|
73
|
+
ThinkingSphinx::Search.new.private_methods.should be_an(Array)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should raise a NoMethodError exception if unknown method" do
|
|
77
|
+
lambda {
|
|
78
|
+
ThinkingSphinx::Search.new.foo
|
|
79
|
+
}.should raise_error(NoMethodError)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should not request results from client if method does not exist" do
|
|
83
|
+
@client.should_not_receive(:query)
|
|
84
|
+
|
|
85
|
+
lambda {
|
|
86
|
+
ThinkingSphinx::Search.new.foo
|
|
87
|
+
}.should raise_error(NoMethodError)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "should accept sphinx scopes" do
|
|
91
|
+
search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
|
92
|
+
|
|
93
|
+
lambda {
|
|
94
|
+
search.by_name('Pat')
|
|
95
|
+
}.should_not raise_error(NoMethodError)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "should return itself when using a sphinx scope" do
|
|
99
|
+
search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
|
100
|
+
search.by_name('Pat').object_id.should == search.object_id
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should keep the same search object when chaining multiple scopes" do
|
|
104
|
+
search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
|
105
|
+
search.by_name('Pat').ids_only.object_id.should == search.object_id
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe '.search' do
|
|
110
|
+
it "return the output of ThinkingSphinx.search" do
|
|
111
|
+
@results = [] # to confirm same object
|
|
112
|
+
ThinkingSphinx.stub!(:search => @results)
|
|
113
|
+
|
|
114
|
+
ThinkingSphinx::Search.search.object_id.should == @results.object_id
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe '.search_for_ids' do
|
|
119
|
+
it "return the output of ThinkingSphinx.search_for_ids" do
|
|
120
|
+
@results = [] # to confirm same object
|
|
121
|
+
ThinkingSphinx.stub!(:search_for_ids => @results)
|
|
122
|
+
|
|
123
|
+
ThinkingSphinx::Search.search_for_ids.object_id.
|
|
124
|
+
should == @results.object_id
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe '.search_for_id' do
|
|
129
|
+
it "return the output of ThinkingSphinx.search_for_ids" do
|
|
130
|
+
@results = [] # to confirm same object
|
|
131
|
+
ThinkingSphinx.stub!(:search_for_id => @results)
|
|
132
|
+
|
|
133
|
+
ThinkingSphinx::Search.search_for_id.object_id.
|
|
134
|
+
should == @results.object_id
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe '.count' do
|
|
139
|
+
it "return the output of ThinkingSphinx.search" do
|
|
140
|
+
@results = [] # to confirm same object
|
|
141
|
+
ThinkingSphinx.stub!(:count => @results)
|
|
142
|
+
|
|
143
|
+
ThinkingSphinx::Search.count.object_id.should == @results.object_id
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
describe '.facets' do
|
|
148
|
+
it "return the output of ThinkingSphinx.facets" do
|
|
149
|
+
@results = [] # to confirm same object
|
|
150
|
+
ThinkingSphinx.stub!(:facets => @results)
|
|
151
|
+
|
|
152
|
+
ThinkingSphinx::Search.facets.object_id.should == @results.object_id
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
describe '#populate' do
|
|
157
|
+
before :each do
|
|
158
|
+
@alpha_a, @alpha_b = Alpha.new, Alpha.new
|
|
159
|
+
@beta_a, @beta_b = Beta.new, Beta.new
|
|
160
|
+
|
|
161
|
+
@alpha_a.stub! :id => 1, :read_attribute => 1
|
|
162
|
+
@alpha_b.stub! :id => 2, :read_attribute => 2
|
|
163
|
+
@beta_a.stub! :id => 1, :read_attribute => 1
|
|
164
|
+
@beta_b.stub! :id => 2, :read_attribute => 2
|
|
165
|
+
|
|
166
|
+
@client.stub! :query => {
|
|
167
|
+
:matches => minimal_result_hashes(@alpha_a, @beta_b, @alpha_b, @beta_a)
|
|
168
|
+
}
|
|
169
|
+
Alpha.stub! :find => [@alpha_a, @alpha_b]
|
|
170
|
+
Beta.stub! :find => [@beta_a, @beta_b]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "should issue only one select per model" do
|
|
174
|
+
Alpha.should_receive(:find).once.and_return([@alpha_a, @alpha_b])
|
|
175
|
+
Beta.should_receive(:find).once.and_return([@beta_a, @beta_b])
|
|
176
|
+
|
|
177
|
+
ThinkingSphinx::Search.new.first
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "should mix the results from different models" do
|
|
181
|
+
search = ThinkingSphinx::Search.new
|
|
182
|
+
search[0].should be_a(Alpha)
|
|
183
|
+
search[1].should be_a(Beta)
|
|
184
|
+
search[2].should be_a(Alpha)
|
|
185
|
+
search[3].should be_a(Beta)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "should maintain the Xoopit ordering for results" do
|
|
189
|
+
search = ThinkingSphinx::Search.new
|
|
190
|
+
search[0].id.should == 1
|
|
191
|
+
search[1].id.should == 2
|
|
192
|
+
search[2].id.should == 2
|
|
193
|
+
search[3].id.should == 1
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
describe 'query' do
|
|
197
|
+
it "should concatenate arguments with spaces" do
|
|
198
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
199
|
+
query.should == 'two words'
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
ThinkingSphinx::Search.new('two', 'words').first
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it "should append conditions to the query" do
|
|
206
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
207
|
+
query.should == 'general @focused specific'
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
|
211
|
+
:focused => 'specific'
|
|
212
|
+
}).first
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it "append multiple conditions together" do
|
|
216
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
217
|
+
query.should match(/general.+@foo word/)
|
|
218
|
+
query.should match(/general.+@bar word/)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
|
222
|
+
:foo => 'word', :bar => 'word'
|
|
223
|
+
}).first
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
it "should apply stars if requested, and handle full extended syntax" do
|
|
227
|
+
input = %{a b* c (d | e) 123 5&6 (f_f g) !h "i j" "k l"~10 "m n"/3 @o p -(q|r)}
|
|
228
|
+
expected = %{*a* b* *c* (*d* | *e*) *123* *5*&*6* (*f_f* *g*) !*h* "i j" "k l"~10 "m n"/3 @o *p* -(*q*|*r*)}
|
|
229
|
+
|
|
230
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
231
|
+
query.should == expected
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
ThinkingSphinx::Search.new(input, :star => true).first
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it "should default to /\w+/ as token for auto-starring" do
|
|
238
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
239
|
+
query.should == '*foo*@*bar*.*com*'
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
ThinkingSphinx::Search.new('foo@bar.com', :star => true).first
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it "should honour custom star tokens" do
|
|
246
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
247
|
+
query.should == '*foo@bar.com* -*foo-bar*'
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
ThinkingSphinx::Search.new(
|
|
251
|
+
'foo@bar.com -foo-bar', :star => /[\w@.-]+/u
|
|
252
|
+
).first
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
describe 'comment' do
|
|
257
|
+
it "should add comment if explicitly provided" do
|
|
258
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
259
|
+
comment.should == 'custom log'
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
ThinkingSphinx::Search.new(:comment => 'custom log').first
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "should default to a blank comment" do
|
|
266
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
267
|
+
comment.should == ''
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
ThinkingSphinx::Search.new.first
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
describe 'match mode' do
|
|
275
|
+
it "should default to :all" do
|
|
276
|
+
ThinkingSphinx::Search.new.first
|
|
277
|
+
|
|
278
|
+
@client.match_mode.should == :all
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it "should default to :extended if conditions are supplied" do
|
|
282
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
|
283
|
+
:foo => 'word', :bar => 'word'
|
|
284
|
+
}).first
|
|
285
|
+
|
|
286
|
+
@client.match_mode.should == :extended
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
it "should use explicit match modes" do
|
|
290
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
|
291
|
+
:foo => 'word', :bar => 'word'
|
|
292
|
+
}, :match_mode => :extended2).first
|
|
293
|
+
|
|
294
|
+
@client.match_mode.should == :extended2
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
describe 'pagination' do
|
|
299
|
+
it "should set the limit using per_page" do
|
|
300
|
+
ThinkingSphinx::Search.new(:per_page => 30).first
|
|
301
|
+
@client.limit.should == 30
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
it "should set the offset if pagination is requested" do
|
|
305
|
+
ThinkingSphinx::Search.new(:page => 3).first
|
|
306
|
+
@client.offset.should == 40
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
it "should set the offset by the per_page value" do
|
|
310
|
+
ThinkingSphinx::Search.new(:page => 3, :per_page => 30).first
|
|
311
|
+
@client.offset.should == 60
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
describe 'filters' do
|
|
316
|
+
it "should filter out deleted values by default" do
|
|
317
|
+
ThinkingSphinx::Search.new.first
|
|
318
|
+
|
|
319
|
+
filter = @client.filters.last
|
|
320
|
+
filter.values.should == [0]
|
|
321
|
+
filter.attribute.should == 'sphinx_deleted'
|
|
322
|
+
filter.exclude?.should be_false
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
it "should add class filters for explicit classes" do
|
|
326
|
+
ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).first
|
|
327
|
+
|
|
328
|
+
filter = @client.filters.last
|
|
329
|
+
filter.values.should == [Alpha.to_crc32, Beta.to_crc32]
|
|
330
|
+
filter.attribute.should == 'class_crc'
|
|
331
|
+
filter.exclude?.should be_false
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
it "should add class filters for subclasses of requested classes" do
|
|
335
|
+
ThinkingSphinx::Search.new(:classes => [Person]).first
|
|
336
|
+
|
|
337
|
+
filter = @client.filters.last
|
|
338
|
+
filter.values.should == [
|
|
339
|
+
Parent.to_crc32, Admin::Person.to_crc32,
|
|
340
|
+
Child.to_crc32, Person.to_crc32
|
|
341
|
+
]
|
|
342
|
+
filter.attribute.should == 'class_crc'
|
|
343
|
+
filter.exclude?.should be_false
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it "should append inclusive filters of integers" do
|
|
347
|
+
ThinkingSphinx::Search.new(:with => {:int => 1}).first
|
|
348
|
+
|
|
349
|
+
filter = @client.filters.last
|
|
350
|
+
filter.values.should == [1]
|
|
351
|
+
filter.attribute.should == 'int'
|
|
352
|
+
filter.exclude?.should be_false
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "should append inclusive filters of floats" do
|
|
356
|
+
ThinkingSphinx::Search.new(:with => {:float => 1.5}).first
|
|
357
|
+
|
|
358
|
+
filter = @client.filters.last
|
|
359
|
+
filter.values.should == [1.5]
|
|
360
|
+
filter.attribute.should == 'float'
|
|
361
|
+
filter.exclude?.should be_false
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
it "should append inclusive filters of booleans" do
|
|
365
|
+
ThinkingSphinx::Search.new(:with => {:boolean => true}).first
|
|
366
|
+
|
|
367
|
+
filter = @client.filters.last
|
|
368
|
+
filter.values.should == [true]
|
|
369
|
+
filter.attribute.should == 'boolean'
|
|
370
|
+
filter.exclude?.should be_false
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
it "should append inclusive filters of arrays" do
|
|
374
|
+
ThinkingSphinx::Search.new(:with => {:ints => [1, 2, 3]}).first
|
|
375
|
+
|
|
376
|
+
filter = @client.filters.last
|
|
377
|
+
filter.values.should == [1, 2, 3]
|
|
378
|
+
filter.attribute.should == 'ints'
|
|
379
|
+
filter.exclude?.should be_false
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
it "should append inclusive filters of time ranges" do
|
|
383
|
+
first, last = 1.week.ago, Time.now
|
|
384
|
+
ThinkingSphinx::Search.new(:with => {
|
|
385
|
+
:time => first..last
|
|
386
|
+
}).first
|
|
387
|
+
|
|
388
|
+
filter = @client.filters.last
|
|
389
|
+
filter.values.should == (first.to_i..last.to_i)
|
|
390
|
+
filter.attribute.should == 'time'
|
|
391
|
+
filter.exclude?.should be_false
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
it "should append exclusive filters of integers" do
|
|
395
|
+
ThinkingSphinx::Search.new(:without => {:int => 1}).first
|
|
396
|
+
|
|
397
|
+
filter = @client.filters.last
|
|
398
|
+
filter.values.should == [1]
|
|
399
|
+
filter.attribute.should == 'int'
|
|
400
|
+
filter.exclude?.should be_true
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
it "should append exclusive filters of floats" do
|
|
404
|
+
ThinkingSphinx::Search.new(:without => {:float => 1.5}).first
|
|
405
|
+
|
|
406
|
+
filter = @client.filters.last
|
|
407
|
+
filter.values.should == [1.5]
|
|
408
|
+
filter.attribute.should == 'float'
|
|
409
|
+
filter.exclude?.should be_true
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
it "should append exclusive filters of booleans" do
|
|
413
|
+
ThinkingSphinx::Search.new(:without => {:boolean => true}).first
|
|
414
|
+
|
|
415
|
+
filter = @client.filters.last
|
|
416
|
+
filter.values.should == [true]
|
|
417
|
+
filter.attribute.should == 'boolean'
|
|
418
|
+
filter.exclude?.should be_true
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
it "should append exclusive filters of arrays" do
|
|
422
|
+
ThinkingSphinx::Search.new(:without => {:ints => [1, 2, 3]}).first
|
|
423
|
+
|
|
424
|
+
filter = @client.filters.last
|
|
425
|
+
filter.values.should == [1, 2, 3]
|
|
426
|
+
filter.attribute.should == 'ints'
|
|
427
|
+
filter.exclude?.should be_true
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
it "should append exclusive filters of time ranges" do
|
|
431
|
+
first, last = 1.week.ago, Time.now
|
|
432
|
+
ThinkingSphinx::Search.new(:without => {
|
|
433
|
+
:time => first..last
|
|
434
|
+
}).first
|
|
435
|
+
|
|
436
|
+
filter = @client.filters.last
|
|
437
|
+
filter.values.should == (first.to_i..last.to_i)
|
|
438
|
+
filter.attribute.should == 'time'
|
|
439
|
+
filter.exclude?.should be_true
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
it "should add separate filters for each item in a with_all value" do
|
|
443
|
+
ThinkingSphinx::Search.new(:with_all => {:ints => [1, 2, 3]}).first
|
|
444
|
+
|
|
445
|
+
filters = @client.filters[-3, 3]
|
|
446
|
+
filters.each do |filter|
|
|
447
|
+
filter.attribute.should == 'ints'
|
|
448
|
+
filter.exclude?.should be_false
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
filters[0].values.should == [1]
|
|
452
|
+
filters[1].values.should == [2]
|
|
453
|
+
filters[2].values.should == [3]
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
it "should filter out specific ids using :without_ids" do
|
|
457
|
+
ThinkingSphinx::Search.new(:without_ids => [4, 5, 6]).first
|
|
458
|
+
|
|
459
|
+
filter = @client.filters.last
|
|
460
|
+
filter.values.should == [4, 5, 6]
|
|
461
|
+
filter.attribute.should == 'sphinx_internal_id'
|
|
462
|
+
filter.exclude?.should be_true
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
describe 'in :conditions' do
|
|
466
|
+
it "should add as filters for known attributes in :conditions option" do
|
|
467
|
+
ThinkingSphinx::Search.new('general',
|
|
468
|
+
:conditions => {:word => 'specific', :lat => 1.5},
|
|
469
|
+
:classes => [Alpha]
|
|
470
|
+
).first
|
|
471
|
+
|
|
472
|
+
filter = @client.filters.last
|
|
473
|
+
filter.values.should == [1.5]
|
|
474
|
+
filter.attribute.should == 'lat'
|
|
475
|
+
filter.exclude?.should be_false
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
it "should not add the filter to the query string" do
|
|
479
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
480
|
+
query.should == 'general @word specific'
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
ThinkingSphinx::Search.new('general',
|
|
484
|
+
:conditions => {:word => 'specific', :lat => 1.5},
|
|
485
|
+
:classes => [Alpha]
|
|
486
|
+
).first
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
describe 'sort mode' do
|
|
492
|
+
it "should use :relevance as a default" do
|
|
493
|
+
ThinkingSphinx::Search.new.first
|
|
494
|
+
@client.sort_mode.should == :relevance
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
it "should use :attr_asc if a symbol is supplied to :order" do
|
|
498
|
+
ThinkingSphinx::Search.new(:order => :created_at).first
|
|
499
|
+
@client.sort_mode.should == :attr_asc
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
it "should use :attr_desc if :desc is the mode" do
|
|
503
|
+
ThinkingSphinx::Search.new(
|
|
504
|
+
:order => :created_at, :sort_mode => :desc
|
|
505
|
+
).first
|
|
506
|
+
@client.sort_mode.should == :attr_desc
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
it "should use :extended if a string is supplied to :order" do
|
|
510
|
+
ThinkingSphinx::Search.new(:order => "created_at ASC").first
|
|
511
|
+
@client.sort_mode.should == :extended
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
it "should use :expr if explicitly requested" do
|
|
515
|
+
ThinkingSphinx::Search.new(
|
|
516
|
+
:order => "created_at ASC", :sort_mode => :expr
|
|
517
|
+
).first
|
|
518
|
+
@client.sort_mode.should == :expr
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
it "should use :attr_desc if explicitly requested" do
|
|
522
|
+
ThinkingSphinx::Search.new(
|
|
523
|
+
:order => "created_at", :sort_mode => :desc
|
|
524
|
+
).first
|
|
525
|
+
@client.sort_mode.should == :attr_desc
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
describe 'sort by' do
|
|
530
|
+
it "should presume order symbols are attributes" do
|
|
531
|
+
ThinkingSphinx::Search.new(:order => :created_at).first
|
|
532
|
+
@client.sort_by.should == 'created_at'
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
it "replace field names with their sortable attributes" do
|
|
536
|
+
ThinkingSphinx::Search.new(:order => :name, :classes => [Alpha]).first
|
|
537
|
+
@client.sort_by.should == 'name_sort'
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
it "should replace field names in strings" do
|
|
541
|
+
ThinkingSphinx::Search.new(
|
|
542
|
+
:order => "created_at ASC, name DESC", :classes => [Alpha]
|
|
543
|
+
).first
|
|
544
|
+
@client.sort_by.should == 'created_at ASC, name_sort DESC'
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
describe 'max matches' do
|
|
549
|
+
it "should use the global setting by default" do
|
|
550
|
+
ThinkingSphinx::Search.new.first
|
|
551
|
+
@client.max_matches.should == 1000
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
it "should use explicit setting" do
|
|
555
|
+
ThinkingSphinx::Search.new(:max_matches => 2000).first
|
|
556
|
+
@client.max_matches.should == 2000
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
describe 'field weights' do
|
|
561
|
+
it "should set field weights as provided" do
|
|
562
|
+
ThinkingSphinx::Search.new(
|
|
563
|
+
:field_weights => {'foo' => 10, 'bar' => 5}
|
|
564
|
+
).first
|
|
565
|
+
|
|
566
|
+
@client.field_weights.should == {
|
|
567
|
+
'foo' => 10, 'bar' => 5
|
|
568
|
+
}
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
it "should use field weights set in the index" do
|
|
572
|
+
ThinkingSphinx::Search.new(:classes => [Alpha]).first
|
|
573
|
+
|
|
574
|
+
@client.field_weights.should == {'name' => 10}
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
describe 'index weights' do
|
|
579
|
+
it "should send index weights through to the client" do
|
|
580
|
+
ThinkingSphinx::Search.new(:index_weights => {'foo' => 100}).first
|
|
581
|
+
@client.index_weights.should == {'foo' => 100}
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
it "should convert classes to their core and delta index names" do
|
|
585
|
+
ThinkingSphinx::Search.new(:index_weights => {Alpha => 100}).first
|
|
586
|
+
@client.index_weights.should == {
|
|
587
|
+
'alpha_core' => 100,
|
|
588
|
+
'alpha_delta' => 100
|
|
589
|
+
}
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
describe 'grouping' do
|
|
594
|
+
it "should convert group into group_by and group_function" do
|
|
595
|
+
ThinkingSphinx::Search.new(:group => :edition).first
|
|
596
|
+
|
|
597
|
+
@client.group_function.should == :attr
|
|
598
|
+
@client.group_by.should == "edition"
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
it "should pass on explicit grouping arguments" do
|
|
602
|
+
ThinkingSphinx::Search.new(
|
|
603
|
+
:group_by => 'created_at',
|
|
604
|
+
:group_function => :attr,
|
|
605
|
+
:group_clause => 'clause',
|
|
606
|
+
:group_distinct => 'distinct'
|
|
607
|
+
).first
|
|
608
|
+
|
|
609
|
+
@client.group_by.should == 'created_at'
|
|
610
|
+
@client.group_function.should == :attr
|
|
611
|
+
@client.group_clause.should == 'clause'
|
|
612
|
+
@client.group_distinct.should == 'distinct'
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
describe 'anchor' do
|
|
617
|
+
it "should detect lat and lng attributes on the given model" do
|
|
618
|
+
ThinkingSphinx::Search.new(
|
|
619
|
+
:geo => [1.0, -1.0],
|
|
620
|
+
:classes => [Alpha]
|
|
621
|
+
).first
|
|
622
|
+
|
|
623
|
+
@client.anchor[:latitude_attribute].should == 'lat'
|
|
624
|
+
@client.anchor[:longitude_attribute].should == 'lng'
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
it "should detect lat and lon attributes on the given model" do
|
|
628
|
+
ThinkingSphinx::Search.new(
|
|
629
|
+
:geo => [1.0, -1.0],
|
|
630
|
+
:classes => [Beta]
|
|
631
|
+
).first
|
|
632
|
+
|
|
633
|
+
@client.anchor[:latitude_attribute].should == 'lat'
|
|
634
|
+
@client.anchor[:longitude_attribute].should == 'lon'
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
it "should detect latitude and longitude attributes on the given model" do
|
|
638
|
+
ThinkingSphinx::Search.new(
|
|
639
|
+
:geo => [1.0, -1.0],
|
|
640
|
+
:classes => [Person]
|
|
641
|
+
).first
|
|
642
|
+
|
|
643
|
+
@client.anchor[:latitude_attribute].should == 'latitude'
|
|
644
|
+
@client.anchor[:longitude_attribute].should == 'longitude'
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
it "should accept manually defined latitude and longitude attributes" do
|
|
648
|
+
ThinkingSphinx::Search.new(
|
|
649
|
+
:geo => [1.0, -1.0],
|
|
650
|
+
:classes => [Alpha],
|
|
651
|
+
:latitude_attr => :updown,
|
|
652
|
+
:longitude_attr => :leftright
|
|
653
|
+
).first
|
|
654
|
+
|
|
655
|
+
@client.anchor[:latitude_attribute].should == 'updown'
|
|
656
|
+
@client.anchor[:longitude_attribute].should == 'leftright'
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
it "should accept manually defined latitude and longitude attributes in the given model" do
|
|
660
|
+
ThinkingSphinx::Search.new(
|
|
661
|
+
:geo => [1.0, -1.0],
|
|
662
|
+
:classes => [Friendship]
|
|
663
|
+
).first
|
|
664
|
+
|
|
665
|
+
@client.anchor[:latitude_attribute].should == 'person_id'
|
|
666
|
+
@client.anchor[:longitude_attribute].should == 'person_id'
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
it "should accept geo array for geo-position values" do
|
|
670
|
+
ThinkingSphinx::Search.new(
|
|
671
|
+
:geo => [1.0, -1.0],
|
|
672
|
+
:classes => [Alpha]
|
|
673
|
+
).first
|
|
674
|
+
|
|
675
|
+
@client.anchor[:latitude].should == 1.0
|
|
676
|
+
@client.anchor[:longitude].should == -1.0
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
it "should accept lat and lng options for geo-position values" do
|
|
680
|
+
ThinkingSphinx::Search.new(
|
|
681
|
+
:lat => 1.0,
|
|
682
|
+
:lng => -1.0,
|
|
683
|
+
:classes => [Alpha]
|
|
684
|
+
).first
|
|
685
|
+
|
|
686
|
+
@client.anchor[:latitude].should == 1.0
|
|
687
|
+
@client.anchor[:longitude].should == -1.0
|
|
688
|
+
end
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
describe 'excerpts' do
|
|
692
|
+
before :each do
|
|
693
|
+
@search = ThinkingSphinx::Search.new
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
it "should add excerpts method if objects don't already have one" do
|
|
697
|
+
@search.first.should respond_to(:excerpts)
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
it "should return an instance of ThinkingSphinx::Excerpter" do
|
|
701
|
+
@search.first.excerpts.should be_a(ThinkingSphinx::Excerpter)
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
it "should not add excerpts method if objects already have one" do
|
|
705
|
+
@search.last.excerpts.should_not be_a(ThinkingSphinx::Excerpter)
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
it "should set up the excerpter with the instances and search" do
|
|
709
|
+
ThinkingSphinx::Excerpter.should_receive(:new).with(@search, @alpha_a)
|
|
710
|
+
ThinkingSphinx::Excerpter.should_receive(:new).with(@search, @alpha_b)
|
|
711
|
+
|
|
712
|
+
@search.first
|
|
713
|
+
end
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
describe '#current_page' do
|
|
718
|
+
it "should return 1 by default" do
|
|
719
|
+
ThinkingSphinx::Search.new.current_page.should == 1
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
it "should handle string page values" do
|
|
723
|
+
ThinkingSphinx::Search.new(:page => '2').current_page.should == 2
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
it "should handle empty string page values" do
|
|
727
|
+
ThinkingSphinx::Search.new(:page => '').current_page.should == 1
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
it "should return the requested page" do
|
|
731
|
+
ThinkingSphinx::Search.new(:page => 10).current_page.should == 10
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
describe '#per_page' do
|
|
736
|
+
it "should return 20 by default" do
|
|
737
|
+
ThinkingSphinx::Search.new.per_page.should == 20
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
it "should allow for custom values" do
|
|
741
|
+
ThinkingSphinx::Search.new(:per_page => 30).per_page.should == 30
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
it "should prioritise :limit over :per_page if given" do
|
|
745
|
+
ThinkingSphinx::Search.new(
|
|
746
|
+
:per_page => 30, :limit => 40
|
|
747
|
+
).per_page.should == 40
|
|
748
|
+
end
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
describe '#total_pages' do
|
|
752
|
+
it "should calculate the total pages depending on per_page and total_entries" do
|
|
753
|
+
ThinkingSphinx::Search.new.total_pages.should == 3
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
it "should allow for custom per_page values" do
|
|
757
|
+
ThinkingSphinx::Search.new(:per_page => 30).total_pages.should == 2
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
it "should not overstep the max_matches implied limit" do
|
|
761
|
+
@client.stub!(:query => {
|
|
762
|
+
:matches => [], :total_found => 41, :total => 40
|
|
763
|
+
})
|
|
764
|
+
|
|
765
|
+
ThinkingSphinx::Search.new.total_pages.should == 2
|
|
766
|
+
end
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
describe '#next_page' do
|
|
770
|
+
it "should return one more than the current page" do
|
|
771
|
+
ThinkingSphinx::Search.new.next_page.should == 2
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
it "should return nil if on the last page" do
|
|
775
|
+
ThinkingSphinx::Search.new(:page => 3).next_page.should be_nil
|
|
776
|
+
end
|
|
777
|
+
end
|
|
778
|
+
|
|
779
|
+
describe '#previous_page' do
|
|
780
|
+
it "should return one less than the current page" do
|
|
781
|
+
ThinkingSphinx::Search.new(:page => 2).previous_page.should == 1
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
it "should return nil if on the first page" do
|
|
785
|
+
ThinkingSphinx::Search.new.previous_page.should be_nil
|
|
786
|
+
end
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
describe '#total_entries' do
|
|
790
|
+
it "should return the total number of results, not just the amount on the page" do
|
|
791
|
+
ThinkingSphinx::Search.new.total_entries.should == 41
|
|
792
|
+
end
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
describe '#offset' do
|
|
796
|
+
it "should default to 0" do
|
|
797
|
+
ThinkingSphinx::Search.new.offset.should == 0
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
it "should increase by the per_page value for each page in" do
|
|
801
|
+
ThinkingSphinx::Search.new(:per_page => 25, :page => 2).offset.should == 25
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
describe '.each_with_groupby_and_count' do
|
|
806
|
+
before :each do
|
|
807
|
+
@alpha = Alpha.new
|
|
808
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
|
809
|
+
|
|
810
|
+
@client.stub! :query => {
|
|
811
|
+
:matches => [{
|
|
812
|
+
:attributes => {
|
|
813
|
+
'sphinx_internal_id' => @alpha.id,
|
|
814
|
+
'class_crc' => Alpha.to_crc32,
|
|
815
|
+
'@groupby' => 101,
|
|
816
|
+
'@count' => 5
|
|
817
|
+
}
|
|
818
|
+
}]
|
|
819
|
+
}
|
|
820
|
+
Alpha.stub!(:find => [@alpha])
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
it "should yield the match, group and count" do
|
|
824
|
+
search = ThinkingSphinx::Search.new
|
|
825
|
+
search.each_with_groupby_and_count do |obj, group, count|
|
|
826
|
+
obj.should == @alpha
|
|
827
|
+
group.should == 101
|
|
828
|
+
count.should == 5
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
describe '.each_with_weighting' do
|
|
834
|
+
before :each do
|
|
835
|
+
@alpha = Alpha.new
|
|
836
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
|
837
|
+
|
|
838
|
+
@client.stub! :query => {
|
|
839
|
+
:matches => [{
|
|
840
|
+
:attributes => {
|
|
841
|
+
'sphinx_internal_id' => @alpha.id,
|
|
842
|
+
'class_crc' => Alpha.to_crc32
|
|
843
|
+
}, :weight => 12
|
|
844
|
+
}]
|
|
845
|
+
}
|
|
846
|
+
Alpha.stub!(:find => [@alpha])
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
it "should yield the match and weight" do
|
|
850
|
+
search = ThinkingSphinx::Search.new
|
|
851
|
+
search.each_with_weighting do |obj, weight|
|
|
852
|
+
obj.should == @alpha
|
|
853
|
+
weight.should == 12
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
describe '.each_with_*' do
|
|
859
|
+
before :each do
|
|
860
|
+
@alpha = Alpha.new
|
|
861
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
|
862
|
+
|
|
863
|
+
@client.stub! :query => {
|
|
864
|
+
:matches => [{
|
|
865
|
+
:attributes => {
|
|
866
|
+
'sphinx_internal_id' => @alpha.id,
|
|
867
|
+
'class_crc' => Alpha.to_crc32,
|
|
868
|
+
'@geodist' => 101,
|
|
869
|
+
'@groupby' => 102,
|
|
870
|
+
'@count' => 103
|
|
871
|
+
}, :weight => 12
|
|
872
|
+
}]
|
|
873
|
+
}
|
|
874
|
+
Alpha.stub!(:find => [@alpha])
|
|
875
|
+
|
|
876
|
+
@search = ThinkingSphinx::Search.new
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
it "should yield geodist if requested" do
|
|
880
|
+
@search.each_with_geodist do |obj, distance|
|
|
881
|
+
obj.should == @alpha
|
|
882
|
+
distance.should == 101
|
|
883
|
+
end
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
it "should yield count if requested" do
|
|
887
|
+
@search.each_with_count do |obj, count|
|
|
888
|
+
obj.should == @alpha
|
|
889
|
+
count.should == 103
|
|
890
|
+
end
|
|
891
|
+
end
|
|
892
|
+
|
|
893
|
+
it "should yield groupby if requested" do
|
|
894
|
+
@search.each_with_groupby do |obj, group|
|
|
895
|
+
obj.should == @alpha
|
|
896
|
+
group.should == 102
|
|
897
|
+
end
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
it "should still use the array's each_with_index" do
|
|
901
|
+
@search.each_with_index do |obj, index|
|
|
902
|
+
obj.should == @alpha
|
|
903
|
+
index.should == 0
|
|
904
|
+
end
|
|
905
|
+
end
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
describe '#excerpt_for' do
|
|
909
|
+
before :each do
|
|
910
|
+
@client.stub!(:excerpts => ['excerpted string'])
|
|
911
|
+
@client.stub!(:query => {
|
|
912
|
+
:matches => [],
|
|
913
|
+
:words => {'one' => {}, 'two' => {}}
|
|
914
|
+
})
|
|
915
|
+
@search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
it "should return the Sphinx excerpt value" do
|
|
919
|
+
@search.excerpt_for('string').should == 'excerpted string'
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
it "should use the given model's core index" do
|
|
923
|
+
@client.should_receive(:excerpts) do |options|
|
|
924
|
+
options[:index].should == 'alpha_core'
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
@search.excerpt_for('string')
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
it "should optionally take a second argument to allow for multi-model searches" do
|
|
931
|
+
@client.should_receive(:excerpts) do |options|
|
|
932
|
+
options[:index].should == 'beta_core'
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
@search.excerpt_for('string', Beta)
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
it "should join the words together" do
|
|
939
|
+
@client.should_receive(:excerpts) do |options|
|
|
940
|
+
options[:words].should == @search.results[:words].keys.join(' ')
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
@search.excerpt_for('string', Beta)
|
|
944
|
+
end
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
describe '#search' do
|
|
948
|
+
before :each do
|
|
949
|
+
@search = ThinkingSphinx::Search.new('word',
|
|
950
|
+
:conditions => {:field => 'field'},
|
|
951
|
+
:with => {:int => 5}
|
|
952
|
+
)
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
it "should return itself" do
|
|
956
|
+
@search.search.object_id.should == @search.object_id
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
it "should merge in arguments" do
|
|
960
|
+
@client.should_receive(:query) do |query, index, comments|
|
|
961
|
+
query.should == 'word more @field field'
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
@search.search('more').first
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
it "should merge conditions" do
|
|
968
|
+
@client.should_receive(:query) do |query, index, comments|
|
|
969
|
+
query.should match(/@name plato/)
|
|
970
|
+
query.should match(/@field field/)
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
@search.search(:conditions => {:name => 'plato'}).first
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
it "should merge filters" do
|
|
977
|
+
@search.search(:with => {:float => 1.5}).first
|
|
978
|
+
|
|
979
|
+
@client.filters.detect { |filter|
|
|
980
|
+
filter.attribute == 'float'
|
|
981
|
+
}.should_not be_nil
|
|
982
|
+
@client.filters.detect { |filter|
|
|
983
|
+
filter.attribute == 'int'
|
|
984
|
+
}.should_not be_nil
|
|
985
|
+
end
|
|
986
|
+
end
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
describe ThinkingSphinx::Search, "playing nice with Search model" do
|
|
990
|
+
it "should not conflict with models called Search" do
|
|
991
|
+
lambda { Search.find(:all) }.should_not raise_error
|
|
992
|
+
end
|
|
993
|
+
end
|