thinking-sphinx 3.0.6 → 3.1.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.
- checksums.yaml +4 -4
- data/.travis.yml +11 -6
- data/Appraisals +5 -5
- data/Gemfile +3 -3
- data/HISTORY +32 -0
- data/README.textile +16 -5
- data/gemfiles/rails_3_2.gemfile +2 -1
- data/gemfiles/rails_4_0.gemfile +3 -2
- data/gemfiles/{rails_3_1.gemfile → rails_4_1.gemfile} +3 -2
- data/lib/thinking_sphinx.rb +5 -0
- data/lib/thinking_sphinx/active_record.rb +2 -1
- data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +1 -1
- data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +6 -7
- data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +2 -2
- data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +1 -1
- data/lib/thinking_sphinx/active_record/column_sql_presenter.rb +34 -0
- data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +4 -0
- data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +4 -0
- data/lib/thinking_sphinx/active_record/index.rb +2 -1
- data/lib/thinking_sphinx/active_record/interpreter.rb +7 -0
- data/lib/thinking_sphinx/active_record/property.rb +1 -1
- data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +10 -16
- data/lib/thinking_sphinx/active_record/sql_builder.rb +7 -1
- data/lib/thinking_sphinx/active_record/sql_builder/query.rb +0 -7
- data/lib/thinking_sphinx/active_record/sql_source.rb +20 -20
- data/lib/thinking_sphinx/active_record/sql_source/template.rb +1 -1
- data/lib/thinking_sphinx/capistrano.rb +6 -65
- data/lib/thinking_sphinx/capistrano/v2.rb +58 -0
- data/lib/thinking_sphinx/capistrano/v3.rb +101 -0
- data/lib/thinking_sphinx/configuration.rb +8 -3
- data/lib/thinking_sphinx/configuration/distributed_indices.rb +29 -0
- data/lib/thinking_sphinx/connection.rb +90 -34
- data/lib/thinking_sphinx/controller.rb +20 -0
- data/lib/thinking_sphinx/core/index.rb +4 -0
- data/lib/thinking_sphinx/deletion.rb +15 -11
- data/lib/thinking_sphinx/deltas.rb +9 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +2 -0
- data/lib/thinking_sphinx/distributed.rb +5 -0
- data/lib/thinking_sphinx/distributed/index.rb +24 -0
- data/lib/thinking_sphinx/excerpter.rb +7 -3
- data/lib/thinking_sphinx/facet_search.rb +1 -1
- data/lib/thinking_sphinx/index.rb +2 -6
- data/lib/thinking_sphinx/index_set.rb +10 -8
- data/lib/thinking_sphinx/middlewares.rb +0 -2
- data/lib/thinking_sphinx/middlewares/active_record_translator.rb +1 -0
- data/lib/thinking_sphinx/middlewares/geographer.rb +1 -1
- data/lib/thinking_sphinx/middlewares/sphinxql.rb +8 -6
- data/lib/thinking_sphinx/middlewares/utf8.rb +6 -1
- data/lib/thinking_sphinx/query.rb +9 -0
- data/lib/thinking_sphinx/railtie.rb +0 -13
- data/lib/thinking_sphinx/search/query.rb +3 -21
- data/lib/thinking_sphinx/sphinxql.rb +1 -1
- data/lib/thinking_sphinx/wildcard.rb +34 -0
- data/spec/acceptance/geosearching_spec.rb +13 -0
- data/spec/acceptance/indexing_spec.rb +27 -0
- data/spec/acceptance/remove_deleted_records_spec.rb +8 -0
- data/spec/acceptance/searching_with_sti_spec.rb +7 -0
- data/spec/acceptance/searching_within_a_model_spec.rb +8 -0
- data/spec/acceptance/sorting_search_results_spec.rb +1 -1
- data/spec/acceptance/spec_helper.rb +13 -0
- data/spec/acceptance/specifying_sql_spec.rb +2 -2
- data/spec/acceptance/support/sphinx_controller.rb +5 -5
- data/spec/acceptance/support/sphinx_helpers.rb +3 -0
- data/spec/acceptance/suspended_deltas_spec.rb +34 -0
- data/spec/internal/config/database.yml +1 -0
- data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +13 -6
- data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +2 -2
- data/spec/thinking_sphinx/active_record/database_adapters/mysql_adapter_spec.rb +7 -0
- data/spec/thinking_sphinx/active_record/database_adapters/postgresql_adapter_spec.rb +6 -0
- data/spec/thinking_sphinx/active_record/interpreter_spec.rb +27 -0
- data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +18 -7
- data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +17 -7
- data/spec/thinking_sphinx/active_record/sql_source_spec.rb +84 -82
- data/spec/thinking_sphinx/configuration_spec.rb +5 -4
- data/spec/thinking_sphinx/connection_spec.rb +1 -1
- data/spec/thinking_sphinx/deletion_spec.rb +10 -28
- data/spec/thinking_sphinx/excerpter_spec.rb +3 -3
- data/spec/thinking_sphinx/facet_search_spec.rb +13 -4
- data/spec/thinking_sphinx/index_set_spec.rb +9 -4
- data/spec/thinking_sphinx/middlewares/active_record_translator_spec.rb +8 -0
- data/spec/thinking_sphinx/middlewares/geographer_spec.rb +7 -7
- data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +17 -1
- data/spec/thinking_sphinx/search/query_spec.rb +10 -53
- data/spec/thinking_sphinx/wildcard_spec.rb +41 -0
- data/thinking-sphinx.gemspec +6 -5
- metadata +38 -14
- data/lib/thinking_sphinx/active_record/associations.rb +0 -98
- data/spec/thinking_sphinx/active_record/associations_spec.rb +0 -230
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'acceptance/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Indexing', :live => true do
|
|
4
|
+
it "does not index files where the temp file exists" do
|
|
5
|
+
path = Rails.root.join('db/sphinx/test/ts-article_core.tmp')
|
|
6
|
+
FileUtils.mkdir_p Rails.root.join('db/sphinx/test')
|
|
7
|
+
FileUtils.touch path
|
|
8
|
+
|
|
9
|
+
article = Article.create! :title => 'Pancakes'
|
|
10
|
+
index 'article_core'
|
|
11
|
+
Article.search.should be_empty
|
|
12
|
+
|
|
13
|
+
FileUtils.rm path
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "indexes files when other indices are already being processed" do
|
|
17
|
+
path = Rails.root.join('db/sphinx/test/ts-book_core.tmp')
|
|
18
|
+
FileUtils.mkdir_p Rails.root.join('db/sphinx/test')
|
|
19
|
+
FileUtils.touch path
|
|
20
|
+
|
|
21
|
+
article = Article.create! :title => 'Pancakes'
|
|
22
|
+
index 'article_core'
|
|
23
|
+
Article.search.should_not be_empty
|
|
24
|
+
|
|
25
|
+
FileUtils.rm path
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -30,4 +30,12 @@ describe 'Hiding deleted records from search results', :live => true do
|
|
|
30
30
|
|
|
31
31
|
Product.search_for_ids('Shiny').should be_empty
|
|
32
32
|
end
|
|
33
|
+
|
|
34
|
+
it "deletes STI child classes from parent indices" do
|
|
35
|
+
duck = Bird.create :name => 'Duck'
|
|
36
|
+
index
|
|
37
|
+
duck.destroy
|
|
38
|
+
|
|
39
|
+
expect(Bird.search_for_ids('duck')).to be_empty
|
|
40
|
+
end
|
|
33
41
|
end
|
|
@@ -52,4 +52,11 @@ describe 'Searching across STI models', :live => true do
|
|
|
52
52
|
|
|
53
53
|
Bird.search(nil, :skip_sti => true, :classes=>[Bird, FlightlessBird]).to_a.should == [duck, emu]
|
|
54
54
|
end
|
|
55
|
+
|
|
56
|
+
it 'finds root objects when type is blank' do
|
|
57
|
+
animal = Animal.create :name => 'Animal', type: ''
|
|
58
|
+
index
|
|
59
|
+
|
|
60
|
+
Animal.search.to_a.should == [animal]
|
|
61
|
+
end
|
|
55
62
|
end
|
|
@@ -41,6 +41,14 @@ describe 'Searching within a model', :live => true do
|
|
|
41
41
|
articles.to_a.should == [article]
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
it "allows for searching on distributed indices" do
|
|
45
|
+
article = Article.create :title => 'Pancakes'
|
|
46
|
+
index
|
|
47
|
+
|
|
48
|
+
articles = Article.search('pancake', :indices => ['article'])
|
|
49
|
+
articles.to_a.should == [article]
|
|
50
|
+
end
|
|
51
|
+
|
|
44
52
|
it "can search on namespaced models" do
|
|
45
53
|
person = Admin::Person.create :name => 'James Bond'
|
|
46
54
|
index
|
|
@@ -42,7 +42,7 @@ describe 'Sorting search results', :live => true do
|
|
|
42
42
|
index
|
|
43
43
|
|
|
44
44
|
Book.search(
|
|
45
|
-
:select => 'year MOD 2004 as mod_year', :order => 'mod_year ASC'
|
|
45
|
+
:select => '*, year MOD 2004 as mod_year', :order => 'mod_year ASC'
|
|
46
46
|
).to_a.should == [boys, grave, gods]
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -2,3 +2,16 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
root = File.expand_path File.dirname(__FILE__)
|
|
4
4
|
Dir["#{root}/support/**/*.rb"].each { |file| require file }
|
|
5
|
+
|
|
6
|
+
if ENV['SPHINX_VERSION'].try :[], /2.0.\d/
|
|
7
|
+
ThinkingSphinx::SphinxQL.variables!
|
|
8
|
+
|
|
9
|
+
ThinkingSphinx::Middlewares::DEFAULT.insert_after(
|
|
10
|
+
ThinkingSphinx::Middlewares::Inquirer,
|
|
11
|
+
ThinkingSphinx::Middlewares::UTF8
|
|
12
|
+
)
|
|
13
|
+
ThinkingSphinx::Middlewares::RAW_ONLY.insert_after(
|
|
14
|
+
ThinkingSphinx::Middlewares::Inquirer,
|
|
15
|
+
ThinkingSphinx::Middlewares::UTF8
|
|
16
|
+
)
|
|
17
|
+
end
|
|
@@ -128,7 +128,7 @@ describe 'specifying SQL for index definitions' do
|
|
|
128
128
|
query.should match(/articles\..title., books\..title./)
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
it "concatenates references
|
|
131
|
+
it "concatenates references that have column" do
|
|
132
132
|
index = ThinkingSphinx::ActiveRecord::Index.new(:event)
|
|
133
133
|
index.definition_block = Proc.new {
|
|
134
134
|
indexes eventable.title, :as => :title
|
|
@@ -138,8 +138,8 @@ describe 'specifying SQL for index definitions' do
|
|
|
138
138
|
|
|
139
139
|
query = index.sources.first.sql_query
|
|
140
140
|
query.should match(/LEFT OUTER JOIN .articles. ON .articles.\..id. = .events.\..eventable_id. AND .events.\..eventable_type. = 'Article'/)
|
|
141
|
-
query.should match(/LEFT OUTER JOIN .users. ON .users.\..id. = .events.\..eventable_id. AND .events.\..eventable_type. = 'User'/)
|
|
142
141
|
query.should_not match(/articles\..title., users\..title./)
|
|
142
|
+
query.should match(/articles\..title./)
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
it "respects deeper associations through polymorphic joins" do
|
|
@@ -5,21 +5,21 @@ class SphinxController
|
|
|
5
5
|
|
|
6
6
|
def setup
|
|
7
7
|
FileUtils.mkdir_p config.indices_location
|
|
8
|
+
config.controller.bin_path = ENV['SPHINX_BIN'] || ''
|
|
8
9
|
config.render_to_file && index
|
|
9
10
|
|
|
10
11
|
ThinkingSphinx::Configuration.reset
|
|
11
12
|
|
|
12
|
-
if ENV['SPHINX_VERSION'].try :[], /2.1.\d/
|
|
13
|
-
ThinkingSphinx::SphinxQL.functions!
|
|
14
|
-
ThinkingSphinx::Configuration.instance.settings['utf8'] = true
|
|
15
|
-
end
|
|
16
|
-
|
|
17
13
|
ActiveSupport::Dependencies.loaded.each do |path|
|
|
18
14
|
$LOADED_FEATURES.delete "#{path}.rb"
|
|
19
15
|
end
|
|
20
16
|
|
|
21
17
|
ActiveSupport::Dependencies.clear
|
|
22
18
|
|
|
19
|
+
if ENV['SPHINX_VERSION'].try :[], /2.0.\d/
|
|
20
|
+
ThinkingSphinx::Configuration.instance.settings['utf8'] = false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
23
|
config.searchd.mysql41 = 9307
|
|
24
24
|
config.settings['quiet_deltas'] = true
|
|
25
25
|
config.settings['attribute_updates'] = true
|
|
@@ -17,4 +17,38 @@ describe 'Suspend deltas for a given action', :live => true do
|
|
|
17
17
|
sleep 0.25
|
|
18
18
|
Book.search('Terry').to_a.should == [book]
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
it "returns core records even though they are no longer valid" do
|
|
22
|
+
book = Book.create :title => 'Night Watch', :author => 'Harry Pritchett'
|
|
23
|
+
index
|
|
24
|
+
|
|
25
|
+
Book.search('Harry').to_a.should == [book]
|
|
26
|
+
|
|
27
|
+
ThinkingSphinx::Deltas.suspend :book do
|
|
28
|
+
book.reload.update_attributes(:author => 'Terry Pratchett')
|
|
29
|
+
sleep 0.25
|
|
30
|
+
|
|
31
|
+
Book.search('Terry').to_a.should == []
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sleep 0.25
|
|
35
|
+
Book.search('Harry').to_a.should == [book]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "marks core records as deleted" do
|
|
39
|
+
book = Book.create :title => 'Night Watch', :author => 'Harry Pritchett'
|
|
40
|
+
index
|
|
41
|
+
|
|
42
|
+
Book.search('Harry').to_a.should == [book]
|
|
43
|
+
|
|
44
|
+
ThinkingSphinx::Deltas.suspend_and_update :book do
|
|
45
|
+
book.reload.update_attributes(:author => 'Terry Pratchett')
|
|
46
|
+
sleep 0.25
|
|
47
|
+
|
|
48
|
+
Book.search('Terry').to_a.should == []
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
sleep 0.25
|
|
52
|
+
Book.search('Harry').to_a.should be_empty
|
|
53
|
+
end
|
|
20
54
|
end
|
|
@@ -31,18 +31,25 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks do
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
describe '#after_destroy' do
|
|
34
|
-
let(:
|
|
35
|
-
:preload_indices => true) }
|
|
34
|
+
let(:index_set) { double 'index set', :to_a => [index] }
|
|
36
35
|
let(:index) { double('index', :name => 'foo_core',
|
|
37
|
-
:document_id_for_key => 14, :type => 'plain') }
|
|
38
|
-
let(:instance) { double('instance', :id => 7) }
|
|
36
|
+
:document_id_for_key => 14, :type => 'plain', :distributed? => false) }
|
|
37
|
+
let(:instance) { double('instance', :id => 7, :new_record? => false) }
|
|
39
38
|
|
|
40
39
|
before :each do
|
|
41
|
-
ThinkingSphinx::
|
|
40
|
+
ThinkingSphinx::IndexSet.stub :new => index_set
|
|
42
41
|
end
|
|
43
42
|
|
|
44
43
|
it "performs the deletion for the index and instance" do
|
|
45
|
-
ThinkingSphinx::Deletion.should_receive(:perform).with(index,
|
|
44
|
+
ThinkingSphinx::Deletion.should_receive(:perform).with(index, 7)
|
|
45
|
+
|
|
46
|
+
callbacks.after_destroy
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "doesn't do anything if the instance is a new record" do
|
|
50
|
+
instance.stub :new_record? => true
|
|
51
|
+
|
|
52
|
+
ThinkingSphinx::Deletion.should_not_receive(:perform)
|
|
46
53
|
|
|
47
54
|
callbacks.after_destroy
|
|
48
55
|
end
|
|
@@ -19,8 +19,8 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks do
|
|
|
19
19
|
:settings => {'attribute_updates' => true},
|
|
20
20
|
:indices_for_references => [index]) }
|
|
21
21
|
let(:connection) { double('connection', :execute => '') }
|
|
22
|
-
let(:index) { double
|
|
23
|
-
:sources => [source], :document_id_for_key => 3
|
|
22
|
+
let(:index) { double 'index', :name => 'article_core',
|
|
23
|
+
:sources => [source], :document_id_for_key => 3, :distributed? => false }
|
|
24
24
|
let(:source) { double('source', :attributes => []) }
|
|
25
25
|
|
|
26
26
|
before :each do
|
|
@@ -40,6 +40,13 @@ describe ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter do
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
describe '#convert_blank' do
|
|
44
|
+
it "translates arguments to a COALESCE NULLIF SQL call" do
|
|
45
|
+
adapter.convert_blank('id', 5).should == "COALESCE(NULLIF(id, ''), 5)"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
43
50
|
describe '#group_concatenate' do
|
|
44
51
|
it "group concatenates the clause with the given separator" do
|
|
45
52
|
adapter.group_concatenate('foo', ',').
|
|
@@ -49,6 +49,12 @@ describe ThinkingSphinx::ActiveRecord::DatabaseAdapters::PostgreSQLAdapter do
|
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
describe '#convert_blank' do
|
|
53
|
+
it "translates arguments to a COALESCE NULLIF SQL call" do
|
|
54
|
+
adapter.convert_blank('id', 5).should == "COALESCE(NULLIF(id, ''), 5)"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
52
58
|
describe '#group_concatenate' do
|
|
53
59
|
it "group concatenates the clause with the given separator" do
|
|
54
60
|
adapter.group_concatenate('foo', ',').
|
|
@@ -218,6 +218,33 @@ describe ThinkingSphinx::ActiveRecord::Interpreter do
|
|
|
218
218
|
end
|
|
219
219
|
end
|
|
220
220
|
|
|
221
|
+
describe '#set_database' do
|
|
222
|
+
before :each do
|
|
223
|
+
source.stub :set_database_settings => true
|
|
224
|
+
|
|
225
|
+
stub_const 'ActiveRecord::Base',
|
|
226
|
+
double(:configurations => {'other' => {:baz => :qux}})
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it "sends through a hash if provided" do
|
|
230
|
+
source.should_receive(:set_database_settings).with(:foo => :bar)
|
|
231
|
+
|
|
232
|
+
instance.set_database :foo => :bar
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "finds the environment settings if given a string key" do
|
|
236
|
+
source.should_receive(:set_database_settings).with(:baz => :qux)
|
|
237
|
+
|
|
238
|
+
instance.set_database 'other'
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it "finds the environment settings if given a symbol key" do
|
|
242
|
+
source.should_receive(:set_database_settings).with(:baz => :qux)
|
|
243
|
+
|
|
244
|
+
instance.set_database :other
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
221
248
|
describe '#set_property' do
|
|
222
249
|
before :each do
|
|
223
250
|
index.class.stub :settings => [:morphology]
|
|
@@ -2,12 +2,14 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
|
|
4
4
|
let(:adapter) { double 'adapter' }
|
|
5
|
-
let(:associations) { double 'associations', :alias_for => 'articles'
|
|
6
|
-
:aggregate_for? => false, :model_for => model }
|
|
5
|
+
let(:associations) { double 'associations', :alias_for => 'articles' }
|
|
7
6
|
let(:model) { double :column_names => ['title', 'created_at'] }
|
|
7
|
+
let(:path) { double :aggregate? => false, :model => model }
|
|
8
8
|
|
|
9
9
|
before :each do
|
|
10
10
|
adapter.stub(:quote) { |column| column }
|
|
11
|
+
|
|
12
|
+
stub_const 'Joiner::Path', double(:new => path)
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
context 'with a field' do
|
|
@@ -17,7 +19,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
|
|
|
17
19
|
)
|
|
18
20
|
}
|
|
19
21
|
let(:field) { double('field', :name => 'title', :columns => [column],
|
|
20
|
-
:type => nil, :multi? => false, :source_type => nil) }
|
|
22
|
+
:type => nil, :multi? => false, :source_type => nil, :model => double) }
|
|
21
23
|
let(:column) { double('column', :string? => false, :__stack => [],
|
|
22
24
|
:__name => 'title') }
|
|
23
25
|
|
|
@@ -36,7 +38,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
|
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
it "returns nil if the property is an aggregate" do
|
|
39
|
-
|
|
41
|
+
path.stub! :aggregate? => true
|
|
40
42
|
|
|
41
43
|
presenter.to_group.should be_nil
|
|
42
44
|
end
|
|
@@ -73,7 +75,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
|
|
|
73
75
|
"GROUP_CONCAT(#{clause} SEPARATOR '#{separator}')"
|
|
74
76
|
end
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
path.stub! :aggregate? => true
|
|
77
79
|
|
|
78
80
|
presenter.to_select.
|
|
79
81
|
should == "GROUP_CONCAT(articles.title SEPARATOR ' ') AS title"
|
|
@@ -124,7 +126,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
|
|
|
124
126
|
}
|
|
125
127
|
let(:attribute) { double('attribute', :name => 'created_at',
|
|
126
128
|
:columns => [column], :type => :integer, :multi? => false,
|
|
127
|
-
:source_type => nil) }
|
|
129
|
+
:source_type => nil, :model => double) }
|
|
128
130
|
let(:column) { double('column', :string? => false, :__stack => [],
|
|
129
131
|
:__name => 'created_at') }
|
|
130
132
|
|
|
@@ -155,7 +157,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
|
|
|
155
157
|
end
|
|
156
158
|
|
|
157
159
|
it "returns nil if the property is an aggregate" do
|
|
158
|
-
|
|
160
|
+
path.stub! :aggregate? => true
|
|
159
161
|
|
|
160
162
|
presenter.to_group.should be_nil
|
|
161
163
|
end
|
|
@@ -235,6 +237,15 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
|
|
|
235
237
|
presenter.to_select.should == "CONCAT_WS(',', CAST(UNIX_TIMESTAMP(articles.created_at) AS varchar), CAST(UNIX_TIMESTAMP(articles.created_at) AS varchar)) AS created_at"
|
|
236
238
|
end
|
|
237
239
|
|
|
240
|
+
it "does not split attribute clause for timestamp casting if it looks like a function call" do
|
|
241
|
+
column.stub :__name => "COALESCE(articles.updated_at, articles.created_at)", :string? => true
|
|
242
|
+
|
|
243
|
+
attribute.stub :name => 'mod_date', :columns => [column],
|
|
244
|
+
:type => :timestamp
|
|
245
|
+
|
|
246
|
+
presenter.to_select.should == "UNIX_TIMESTAMP(COALESCE(articles.updated_at, articles.created_at)) AS mod_date"
|
|
247
|
+
end
|
|
248
|
+
|
|
238
249
|
it "returns nil for query sourced attributes" do
|
|
239
250
|
attribute.stub :source_type => :query
|
|
240
251
|
|
|
@@ -24,7 +24,7 @@ describe ThinkingSphinx::ActiveRecord::SQLBuilder do
|
|
|
24
24
|
before :each do
|
|
25
25
|
ThinkingSphinx::Configuration.stub! :instance => config
|
|
26
26
|
ThinkingSphinx::ActiveRecord::PropertySQLPresenter.stub! :new => presenter
|
|
27
|
-
|
|
27
|
+
Joiner::Joins.stub! :new => associations
|
|
28
28
|
relation.stub! :select => relation, :where => relation, :group => relation,
|
|
29
29
|
:order => relation, :joins => relation, :to_sql => ''
|
|
30
30
|
connection.stub!(:quote_column_name) { |column| "`#{column}`"}
|
|
@@ -545,6 +545,22 @@ describe ThinkingSphinx::ActiveRecord::SQLBuilder do
|
|
|
545
545
|
end
|
|
546
546
|
end
|
|
547
547
|
|
|
548
|
+
describe 'sql_query_post_index' do
|
|
549
|
+
let(:processor) { double('processor', :reset_query => 'RESET DELTAS') }
|
|
550
|
+
|
|
551
|
+
it "adds a reset delta query if there is a delta processor and this is the core source" do
|
|
552
|
+
source.stub :delta_processor => processor, :delta? => false
|
|
553
|
+
|
|
554
|
+
builder.sql_query_post_index.should include('RESET DELTAS')
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
it "adds no reset delta query if there is a delta processor and this is the delta source" do
|
|
558
|
+
source.stub :delta_processor => processor, :delta? => true
|
|
559
|
+
|
|
560
|
+
builder.sql_query_post_index.should_not include('RESET DELTAS')
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
|
|
548
564
|
describe 'sql_query_pre' do
|
|
549
565
|
let(:processor) { double('processor', :reset_query => 'RESET DELTAS') }
|
|
550
566
|
|
|
@@ -553,12 +569,6 @@ describe ThinkingSphinx::ActiveRecord::SQLBuilder do
|
|
|
553
569
|
adapter.stub :utf8_query_pre => ['SET UTF8']
|
|
554
570
|
end
|
|
555
571
|
|
|
556
|
-
it "adds a reset delta query if there is a delta processor and this is the core source" do
|
|
557
|
-
source.stub :delta_processor => processor
|
|
558
|
-
|
|
559
|
-
builder.sql_query_pre.should include('RESET DELTAS')
|
|
560
|
-
end
|
|
561
|
-
|
|
562
572
|
it "does not add a reset query if there is no delta processor" do
|
|
563
573
|
builder.sql_query_pre.should_not include('RESET DELTAS')
|
|
564
574
|
end
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
4
|
-
let(:model) { double('model', :
|
|
5
|
-
:
|
|
4
|
+
let(:model) { double('model', :connection => connection,
|
|
5
|
+
:name => 'User', :column_names => [], :inheritance_column => 'type',
|
|
6
|
+
:primary_key => :id) }
|
|
6
7
|
let(:connection) {
|
|
7
8
|
double('connection', :instance_variable_get => db_config) }
|
|
8
9
|
let(:db_config) { {:host => 'localhost', :user => 'root',
|
|
@@ -12,8 +13,6 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
|
12
13
|
let(:adapter) { double('adapter') }
|
|
13
14
|
|
|
14
15
|
before :each do
|
|
15
|
-
stub_const 'ActiveRecord::Base', double(:connection => connection)
|
|
16
|
-
|
|
17
16
|
ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter.
|
|
18
17
|
stub!(:=== => true)
|
|
19
18
|
ThinkingSphinx::ActiveRecord::DatabaseAdapters.
|
|
@@ -56,9 +55,14 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
|
56
55
|
ThinkingSphinx::ActiveRecord::SQLSource.new model,
|
|
57
56
|
:delta_processor => processor_class
|
|
58
57
|
}
|
|
58
|
+
let(:source_with_options) {
|
|
59
|
+
ThinkingSphinx::ActiveRecord::SQLSource.new model,
|
|
60
|
+
:delta_processor => processor_class,
|
|
61
|
+
:delta_options => { :opt_key => :opt_value }
|
|
62
|
+
}
|
|
59
63
|
|
|
60
64
|
it "loads the processor with the adapter" do
|
|
61
|
-
processor_class.should_receive(:try).with(:new, adapter).
|
|
65
|
+
processor_class.should_receive(:try).with(:new, adapter, {}).
|
|
62
66
|
and_return processor
|
|
63
67
|
|
|
64
68
|
source.delta_processor
|
|
@@ -67,6 +71,11 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
|
67
71
|
it "returns the given processor" do
|
|
68
72
|
source.delta_processor.should == processor
|
|
69
73
|
end
|
|
74
|
+
|
|
75
|
+
it "passes given options to the processor" do
|
|
76
|
+
processor_class.should_receive(:try).with(:new, adapter, {:opt_key => :opt_value})
|
|
77
|
+
source_with_options.delta_processor
|
|
78
|
+
end
|
|
70
79
|
end
|
|
71
80
|
|
|
72
81
|
describe '#delta?' do
|
|
@@ -101,15 +110,15 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
|
101
110
|
|
|
102
111
|
it "uses the inheritance column if it exists for the sphinx class field" do
|
|
103
112
|
adapter.stub :quoted_table_name => '"users"', :quote => '"type"'
|
|
104
|
-
adapter.stub(:
|
|
105
|
-
"
|
|
113
|
+
adapter.stub(:convert_blank) { |clause, default|
|
|
114
|
+
"coalesce(nullif(#{clause}, ''), #{default})"
|
|
106
115
|
}
|
|
107
116
|
model.stub :column_names => ['type'], :sti_name => 'User'
|
|
108
117
|
|
|
109
118
|
source.fields.detect { |field|
|
|
110
119
|
field.name == 'sphinx_internal_class_name'
|
|
111
120
|
}.columns.first.__name.
|
|
112
|
-
should == "
|
|
121
|
+
should == "coalesce(nullif(\"users\".\"type\", ''), 'User')"
|
|
113
122
|
end
|
|
114
123
|
end
|
|
115
124
|
|
|
@@ -147,7 +156,9 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
|
147
156
|
end
|
|
148
157
|
|
|
149
158
|
describe '#render' do
|
|
150
|
-
let(:builder) { double('builder', :sql_query_pre => []
|
|
159
|
+
let(:builder) { double('builder', :sql_query_pre => [],
|
|
160
|
+
:sql_query_post_index => [], :sql_query => 'query',
|
|
161
|
+
:sql_query_range => 'range', :sql_query_info => 'info') }
|
|
151
162
|
let(:config) { double('config', :settings => {}) }
|
|
152
163
|
let(:presenter) { double('presenter', :collection_type => :uint) }
|
|
153
164
|
let(:template) { double('template', :apply => true) }
|
|
@@ -159,79 +170,6 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
|
159
170
|
ThinkingSphinx::Configuration.stub :instance => config
|
|
160
171
|
end
|
|
161
172
|
|
|
162
|
-
it "sets the sql_host setting from the model's database settings" do
|
|
163
|
-
db_config[:host] = '12.34.56.78'
|
|
164
|
-
|
|
165
|
-
source.render
|
|
166
|
-
|
|
167
|
-
source.sql_host.should == '12.34.56.78'
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
it "defaults sql_host to localhost if the model has no host" do
|
|
171
|
-
db_config[:host] = nil
|
|
172
|
-
|
|
173
|
-
source.render
|
|
174
|
-
|
|
175
|
-
source.sql_host.should == 'localhost'
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
it "sets the sql_user setting from the model's database settings" do
|
|
179
|
-
db_config[:username] = 'pat'
|
|
180
|
-
|
|
181
|
-
source.render
|
|
182
|
-
|
|
183
|
-
source.sql_user.should == 'pat'
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
it "uses the user setting if username is not set in the model" do
|
|
187
|
-
db_config[:username] = nil
|
|
188
|
-
db_config[:user] = 'pat'
|
|
189
|
-
|
|
190
|
-
source.render
|
|
191
|
-
|
|
192
|
-
source.sql_user.should == 'pat'
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
it "sets the sql_pass setting from the model's database settings" do
|
|
196
|
-
db_config[:password] = 'swordfish'
|
|
197
|
-
|
|
198
|
-
source.render
|
|
199
|
-
|
|
200
|
-
source.sql_pass.should == 'swordfish'
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
it "escapes hashes in the password for sql_pass" do
|
|
204
|
-
db_config[:password] = 'sword#fish'
|
|
205
|
-
|
|
206
|
-
source.render
|
|
207
|
-
|
|
208
|
-
source.sql_pass.should == 'sword\#fish'
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
it "sets the sql_db setting from the model's database settings" do
|
|
212
|
-
db_config[:database] = 'rails_app'
|
|
213
|
-
|
|
214
|
-
source.render
|
|
215
|
-
|
|
216
|
-
source.sql_db.should == 'rails_app'
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
it "sets the sql_port setting from the model's database settings" do
|
|
220
|
-
db_config[:port] = 5432
|
|
221
|
-
|
|
222
|
-
source.render
|
|
223
|
-
|
|
224
|
-
source.sql_port.should == 5432
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
it "sets the sql_sock setting from the model's database settings" do
|
|
228
|
-
db_config[:socket] = '/unix/socket'
|
|
229
|
-
|
|
230
|
-
source.render
|
|
231
|
-
|
|
232
|
-
source.sql_sock.should == '/unix/socket'
|
|
233
|
-
end
|
|
234
|
-
|
|
235
173
|
it "uses the builder's sql_query value" do
|
|
236
174
|
builder.stub! :sql_query => 'select * from table'
|
|
237
175
|
|
|
@@ -264,6 +202,14 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
|
264
202
|
source.sql_query_pre.should == ['Change Setting']
|
|
265
203
|
end
|
|
266
204
|
|
|
205
|
+
it "appends the builder's sql_query_post_index value" do
|
|
206
|
+
builder.stub! :sql_query_post_index => ['RESET DELTAS']
|
|
207
|
+
|
|
208
|
+
source.render
|
|
209
|
+
|
|
210
|
+
source.sql_query_post_index.should include('RESET DELTAS')
|
|
211
|
+
end
|
|
212
|
+
|
|
267
213
|
it "adds fields with attributes to sql_field_string" do
|
|
268
214
|
source.fields << double('field', :name => 'title', :source_type => nil,
|
|
269
215
|
:with_attribute? => true, :file? => false, :wordcount? => false)
|
|
@@ -397,6 +343,62 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
|
|
|
397
343
|
end
|
|
398
344
|
end
|
|
399
345
|
|
|
346
|
+
describe '#set_database_settings' do
|
|
347
|
+
it "sets the sql_host setting from the model's database settings" do
|
|
348
|
+
source.set_database_settings :host => '12.34.56.78'
|
|
349
|
+
|
|
350
|
+
source.sql_host.should == '12.34.56.78'
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
it "defaults sql_host to localhost if the model has no host" do
|
|
354
|
+
source.set_database_settings :host => nil
|
|
355
|
+
|
|
356
|
+
source.sql_host.should == 'localhost'
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
it "sets the sql_user setting from the model's database settings" do
|
|
360
|
+
source.set_database_settings :username => 'pat'
|
|
361
|
+
|
|
362
|
+
source.sql_user.should == 'pat'
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
it "uses the user setting if username is not set in the model" do
|
|
366
|
+
source.set_database_settings :username => nil, :user => 'pat'
|
|
367
|
+
|
|
368
|
+
source.sql_user.should == 'pat'
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
it "sets the sql_pass setting from the model's database settings" do
|
|
372
|
+
source.set_database_settings :password => 'swordfish'
|
|
373
|
+
|
|
374
|
+
source.sql_pass.should == 'swordfish'
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
it "escapes hashes in the password for sql_pass" do
|
|
378
|
+
source.set_database_settings :password => 'sword#fish'
|
|
379
|
+
|
|
380
|
+
source.sql_pass.should == 'sword\#fish'
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
it "sets the sql_db setting from the model's database settings" do
|
|
384
|
+
source.set_database_settings :database => 'rails_app'
|
|
385
|
+
|
|
386
|
+
source.sql_db.should == 'rails_app'
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
it "sets the sql_port setting from the model's database settings" do
|
|
390
|
+
source.set_database_settings :port => 5432
|
|
391
|
+
|
|
392
|
+
source.sql_port.should == 5432
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
it "sets the sql_sock setting from the model's database settings" do
|
|
396
|
+
source.set_database_settings :socket => '/unix/socket'
|
|
397
|
+
|
|
398
|
+
source.sql_sock.should == '/unix/socket'
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
400
402
|
describe '#type' do
|
|
401
403
|
it "is mysql when using the MySQL Adapter" do
|
|
402
404
|
ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter.
|