friendlyfashion-thinking-sphinx 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +244 -0
- data/LICENCE +20 -0
- data/README.textile +235 -0
- data/features/abstract_inheritance.feature +10 -0
- data/features/alternate_primary_key.feature +27 -0
- data/features/attribute_transformation.feature +22 -0
- data/features/attribute_updates.feature +77 -0
- data/features/deleting_instances.feature +67 -0
- data/features/direct_attributes.feature +11 -0
- data/features/excerpts.feature +21 -0
- data/features/extensible_delta_indexing.feature +9 -0
- data/features/facets.feature +88 -0
- data/features/facets_across_model.feature +29 -0
- data/features/field_sorting.feature +18 -0
- data/features/handling_edits.feature +94 -0
- data/features/retry_stale_indexes.feature +24 -0
- data/features/searching_across_models.feature +20 -0
- data/features/searching_by_index.feature +40 -0
- data/features/searching_by_model.feature +175 -0
- data/features/searching_with_find_arguments.feature +56 -0
- data/features/sphinx_detection.feature +25 -0
- data/features/sphinx_scopes.feature +68 -0
- data/features/step_definitions/alpha_steps.rb +16 -0
- data/features/step_definitions/beta_steps.rb +7 -0
- data/features/step_definitions/common_steps.rb +201 -0
- data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
- data/features/step_definitions/facet_steps.rb +96 -0
- data/features/step_definitions/find_arguments_steps.rb +36 -0
- data/features/step_definitions/gamma_steps.rb +15 -0
- data/features/step_definitions/scope_steps.rb +19 -0
- data/features/step_definitions/search_steps.rb +94 -0
- data/features/step_definitions/sphinx_steps.rb +35 -0
- data/features/sti_searching.feature +19 -0
- data/features/support/env.rb +27 -0
- data/features/support/lib/generic_delta_handler.rb +8 -0
- data/features/thinking_sphinx/database.example.yml +3 -0
- data/features/thinking_sphinx/db/.gitignore +1 -0
- data/features/thinking_sphinx/db/fixtures/alphas.rb +8 -0
- data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
- data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
- data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
- data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
- data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
- data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
- data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
- data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
- data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
- data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
- data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
- data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
- data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
- data/features/thinking_sphinx/db/fixtures/post_keywords.txt +1 -0
- data/features/thinking_sphinx/db/fixtures/posts.rb +10 -0
- data/features/thinking_sphinx/db/fixtures/robots.rb +8 -0
- data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
- data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
- data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
- data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
- data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
- data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
- data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
- data/features/thinking_sphinx/db/migrations/create_posts.rb +6 -0
- data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
- data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
- data/features/thinking_sphinx/models/alpha.rb +23 -0
- data/features/thinking_sphinx/models/andrew.rb +17 -0
- data/features/thinking_sphinx/models/animal.rb +5 -0
- data/features/thinking_sphinx/models/author.rb +3 -0
- data/features/thinking_sphinx/models/beta.rb +13 -0
- data/features/thinking_sphinx/models/box.rb +8 -0
- data/features/thinking_sphinx/models/cat.rb +3 -0
- data/features/thinking_sphinx/models/category.rb +4 -0
- data/features/thinking_sphinx/models/comment.rb +10 -0
- data/features/thinking_sphinx/models/developer.rb +21 -0
- data/features/thinking_sphinx/models/dog.rb +3 -0
- data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
- data/features/thinking_sphinx/models/fox.rb +5 -0
- data/features/thinking_sphinx/models/gamma.rb +5 -0
- data/features/thinking_sphinx/models/genre.rb +3 -0
- data/features/thinking_sphinx/models/medium.rb +5 -0
- data/features/thinking_sphinx/models/music.rb +10 -0
- data/features/thinking_sphinx/models/person.rb +24 -0
- data/features/thinking_sphinx/models/post.rb +22 -0
- data/features/thinking_sphinx/models/robot.rb +12 -0
- data/features/thinking_sphinx/models/tag.rb +3 -0
- data/features/thinking_sphinx/models/tagging.rb +4 -0
- data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
- data/lib/cucumber/thinking_sphinx/internal_world.rb +137 -0
- data/lib/cucumber/thinking_sphinx/sql_logger.rb +28 -0
- data/lib/thinking-sphinx.rb +1 -0
- data/lib/thinking_sphinx/action_controller.rb +31 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
- data/lib/thinking_sphinx/active_record/collection_proxy.rb +47 -0
- data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
- data/lib/thinking_sphinx/active_record/delta.rb +67 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +44 -0
- data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
- data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
- data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
- data/lib/thinking_sphinx/active_record.rb +386 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +188 -0
- data/lib/thinking_sphinx/association.rb +230 -0
- data/lib/thinking_sphinx/attribute.rb +405 -0
- data/lib/thinking_sphinx/auto_version.rb +40 -0
- data/lib/thinking_sphinx/bundled_search.rb +44 -0
- data/lib/thinking_sphinx/class_facet.rb +20 -0
- data/lib/thinking_sphinx/configuration.rb +375 -0
- data/lib/thinking_sphinx/context.rb +76 -0
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
- data/lib/thinking_sphinx/deltas.rb +28 -0
- data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
- data/lib/thinking_sphinx/excerpter.rb +23 -0
- data/lib/thinking_sphinx/facet.rb +135 -0
- data/lib/thinking_sphinx/facet_search.rb +170 -0
- data/lib/thinking_sphinx/field.rb +98 -0
- data/lib/thinking_sphinx/index/builder.rb +315 -0
- data/lib/thinking_sphinx/index/faux_column.rb +118 -0
- data/lib/thinking_sphinx/index.rb +159 -0
- data/lib/thinking_sphinx/join.rb +37 -0
- data/lib/thinking_sphinx/property.rb +187 -0
- data/lib/thinking_sphinx/railtie.rb +43 -0
- data/lib/thinking_sphinx/search.rb +1061 -0
- data/lib/thinking_sphinx/search_methods.rb +439 -0
- data/lib/thinking_sphinx/sinatra.rb +7 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
- data/lib/thinking_sphinx/source/sql.rb +174 -0
- data/lib/thinking_sphinx/source.rb +194 -0
- data/lib/thinking_sphinx/tasks.rb +142 -0
- data/lib/thinking_sphinx/test.rb +55 -0
- data/lib/thinking_sphinx/version.rb +3 -0
- data/lib/thinking_sphinx.rb +297 -0
- data/spec/fixtures/data.sql +32 -0
- data/spec/fixtures/database.yml.default +3 -0
- data/spec/fixtures/models.rb +164 -0
- data/spec/fixtures/structure.sql +146 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/sphinx_helper.rb +60 -0
- data/spec/support/rails.rb +25 -0
- data/spec/thinking_sphinx/active_record/delta_spec.rb +122 -0
- data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +173 -0
- data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
- data/spec/thinking_sphinx/active_record_spec.rb +573 -0
- data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +145 -0
- data/spec/thinking_sphinx/association_spec.rb +250 -0
- data/spec/thinking_sphinx/attribute_spec.rb +552 -0
- data/spec/thinking_sphinx/auto_version_spec.rb +103 -0
- data/spec/thinking_sphinx/configuration_spec.rb +326 -0
- data/spec/thinking_sphinx/context_spec.rb +126 -0
- data/spec/thinking_sphinx/core/array_spec.rb +9 -0
- data/spec/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
- data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
- data/spec/thinking_sphinx/facet_spec.rb +359 -0
- data/spec/thinking_sphinx/field_spec.rb +127 -0
- data/spec/thinking_sphinx/index/builder_spec.rb +532 -0
- data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
- data/spec/thinking_sphinx/index_spec.rb +189 -0
- data/spec/thinking_sphinx/search_methods_spec.rb +156 -0
- data/spec/thinking_sphinx/search_spec.rb +1455 -0
- data/spec/thinking_sphinx/source_spec.rb +267 -0
- data/spec/thinking_sphinx/test_spec.rb +20 -0
- data/spec/thinking_sphinx_spec.rb +204 -0
- metadata +524 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::Configuration do
|
|
4
|
+
describe "environment class method" do
|
|
5
|
+
before :each do
|
|
6
|
+
ThinkingSphinx::Configuration.reset_environment
|
|
7
|
+
|
|
8
|
+
ENV["RAILS_ENV"] = nil
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should use Rails.env if set" do
|
|
12
|
+
was = Rails.env
|
|
13
|
+
Rails.env = 'global_rails'
|
|
14
|
+
|
|
15
|
+
ThinkingSphinx::Configuration.environment.should == 'global_rails'
|
|
16
|
+
|
|
17
|
+
Rails.env = was
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should use the Rails environment value if set" do
|
|
21
|
+
Rails.stub!(:env => "rails_production")
|
|
22
|
+
ThinkingSphinx::Configuration.environment.should == "rails_production"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should default to development" do
|
|
26
|
+
ThinkingSphinx::Configuration.environment.should == "development"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe '#version' do
|
|
31
|
+
before :each do
|
|
32
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
33
|
+
@config.reset
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should use the given version from sphinx.yml if there is one" do
|
|
37
|
+
open("#{Rails.root}/config/sphinx.yml", "w") do |f|
|
|
38
|
+
f.write YAML.dump({'development' => {'version' => '0.9.7'}})
|
|
39
|
+
end
|
|
40
|
+
@config.reset
|
|
41
|
+
|
|
42
|
+
@config.version.should == '0.9.7'
|
|
43
|
+
|
|
44
|
+
FileUtils.rm "#{Rails.root}/config/sphinx.yml"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should detect the version from Riddle otherwise" do
|
|
48
|
+
controller = @config.controller
|
|
49
|
+
controller.stub!(:sphinx_version => '0.9.6')
|
|
50
|
+
|
|
51
|
+
Riddle::Controller.stub!(:new => controller)
|
|
52
|
+
@config.reset
|
|
53
|
+
|
|
54
|
+
@config.version.should == '0.9.6'
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe "parse_config method" do
|
|
59
|
+
before :each do
|
|
60
|
+
@settings = {
|
|
61
|
+
"development" => {
|
|
62
|
+
"config_file" => "tmp/config/development.sphinx.conf",
|
|
63
|
+
"searchd_log_file" => "searchd_log_file.log",
|
|
64
|
+
"query_log_file" => "query_log_file.log",
|
|
65
|
+
"pid_file" => "pid_file.pid",
|
|
66
|
+
"searchd_file_path" => "searchd/file/path",
|
|
67
|
+
"address" => "127.0.0.1",
|
|
68
|
+
"port" => 3333,
|
|
69
|
+
"min_prefix_len" => 2,
|
|
70
|
+
"min_infix_len" => 3,
|
|
71
|
+
"mem_limit" => "128M",
|
|
72
|
+
"max_matches" => 1001,
|
|
73
|
+
"morphology" => "stem_ru",
|
|
74
|
+
"charset_type" => "latin1",
|
|
75
|
+
"charset_table" => "table",
|
|
76
|
+
"ignore_chars" => "e",
|
|
77
|
+
"searchd_binary_name" => "sphinx-searchd",
|
|
78
|
+
"indexer_binary_name" => "sphinx-indexer",
|
|
79
|
+
"index_exact_words" => true,
|
|
80
|
+
"indexed_models" => ['Alpha', 'Beta']
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
open("#{Rails.root}/config/sphinx.yml", "w") do |f|
|
|
85
|
+
f.write YAML.dump(@settings)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should use the accessors to set the configuration values" do
|
|
90
|
+
config = ThinkingSphinx::Configuration.instance
|
|
91
|
+
config.send(:parse_config)
|
|
92
|
+
|
|
93
|
+
%w(config_file searchd_log_file query_log_file pid_file searchd_file_path
|
|
94
|
+
address port searchd_binary_name indexer_binary_name).each do |key|
|
|
95
|
+
config.send(key).should == @settings["development"][key]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
after :each do
|
|
100
|
+
FileUtils.rm "#{Rails.root}/config/sphinx.yml"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe "block configuration" do
|
|
105
|
+
it "should let the user set-up a custom app_root" do
|
|
106
|
+
ThinkingSphinx::Configuration.configure do |config|
|
|
107
|
+
config.app_root = "/here/somewhere"
|
|
108
|
+
end
|
|
109
|
+
ThinkingSphinx::Configuration.instance.app_root.should == "/here/somewhere"
|
|
110
|
+
|
|
111
|
+
ThinkingSphinx::Configuration.instance.reset
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe "initialisation" do
|
|
116
|
+
it "should have a default bin_path of nothing" do
|
|
117
|
+
ThinkingSphinx::Configuration.instance.bin_path.should == ""
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "should append a / to bin_path if one is supplied" do
|
|
121
|
+
@settings = {
|
|
122
|
+
"development" => {
|
|
123
|
+
"bin_path" => "path/to/somewhere"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
open("#{Rails.root}/config/sphinx.yml", "w") do |f|
|
|
128
|
+
f.write YAML.dump(@settings)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
ThinkingSphinx::Configuration.instance.send(:parse_config)
|
|
132
|
+
ThinkingSphinx::Configuration.instance.bin_path.should match(/\/$/)
|
|
133
|
+
|
|
134
|
+
FileUtils.rm "#{Rails.root}/config/sphinx.yml"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe "index options" do
|
|
139
|
+
before :each do
|
|
140
|
+
@settings = {
|
|
141
|
+
"development" => {"disable_range" => true}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
open("#{Rails.root}/config/sphinx.yml", "w") do |f|
|
|
145
|
+
f.write YAML.dump(@settings)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
149
|
+
@config.send(:parse_config)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "should collect disable_range" do
|
|
153
|
+
@config.index_options[:disable_range].should be_true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
after :each do
|
|
157
|
+
FileUtils.rm "#{Rails.root}/config/sphinx.yml"
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should insert set index options into the configuration file" do
|
|
162
|
+
config = ThinkingSphinx::Configuration.instance
|
|
163
|
+
|
|
164
|
+
ThinkingSphinx::Configuration::IndexOptions.each do |option|
|
|
165
|
+
config.reset
|
|
166
|
+
config.index_options[option.to_sym] = "something"
|
|
167
|
+
config.build
|
|
168
|
+
|
|
169
|
+
file = open(config.config_file) { |f| f.read }
|
|
170
|
+
file.should match(/#{option}\s+= something/)
|
|
171
|
+
|
|
172
|
+
config.index_options[option.to_sym] = nil
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should insert set source options into the configuration file" do
|
|
177
|
+
config = ThinkingSphinx::Configuration.instance
|
|
178
|
+
config.reset
|
|
179
|
+
|
|
180
|
+
config.source_options[:sql_query_pre] = ["something"]
|
|
181
|
+
ThinkingSphinx::Configuration::SourceOptions.each do |option|
|
|
182
|
+
config.source_options[option.to_sym] ||= "something"
|
|
183
|
+
config.build
|
|
184
|
+
|
|
185
|
+
file = open(config.config_file) { |f| f.read }
|
|
186
|
+
file.should match(/#{option}\s+= something/)
|
|
187
|
+
|
|
188
|
+
config.source_options.delete option.to_sym
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
config.source_options[:sql_query_pre] = []
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "should not blow away delta or utf options if sql pre is specified in config" do
|
|
195
|
+
config = ThinkingSphinx::Configuration.instance
|
|
196
|
+
config.reset
|
|
197
|
+
|
|
198
|
+
config.source_options[:sql_query_pre] = ["a pre query"]
|
|
199
|
+
config.build
|
|
200
|
+
file = open(config.config_file) { |f| f.read }
|
|
201
|
+
|
|
202
|
+
file.should match(/sql_query_pre = a pre query\n\s*sql_query_pre = UPDATE `\w+` SET `delta` = 0 WHERE `delta` = 1/im)
|
|
203
|
+
file.should match(/sql_query_pre = a pre query\n\s*sql_query_pre = \n/im)
|
|
204
|
+
|
|
205
|
+
config.source_options[:sql_query_pre] = []
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "should set any explicit prefixed or infixed fields" do
|
|
209
|
+
ThinkingSphinx::Configuration.instance.build
|
|
210
|
+
|
|
211
|
+
file = open(ThinkingSphinx::Configuration.instance.config_file) { |f|
|
|
212
|
+
f.read
|
|
213
|
+
}
|
|
214
|
+
file.should match(/prefix_fields\s+= city/)
|
|
215
|
+
file.should match(/infix_fields\s+= state/)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it "should not have prefix fields in indexes where nothing is set" do
|
|
219
|
+
ThinkingSphinx::Configuration.instance.build
|
|
220
|
+
|
|
221
|
+
file = open(ThinkingSphinx::Configuration.instance.config_file) { |f|
|
|
222
|
+
f.read
|
|
223
|
+
}
|
|
224
|
+
file.should_not match(/index alpha_core\s+\{\s+[^\}]*prefix_fields\s+=[^\}]*\}/m)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
describe '#generate' do
|
|
228
|
+
let(:config) { ThinkingSphinx::Configuration.instance }
|
|
229
|
+
|
|
230
|
+
it "should set all sphinx_internal_id attributes to bigints if one is" do
|
|
231
|
+
config.reset
|
|
232
|
+
config.generate
|
|
233
|
+
|
|
234
|
+
config.configuration.indices.each do |index|
|
|
235
|
+
next if index.is_a? Riddle::Configuration::DistributedIndex
|
|
236
|
+
|
|
237
|
+
index.sources.each do |source|
|
|
238
|
+
source.sql_attr_bigint.should include(:sphinx_internal_id)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
describe '#client' do
|
|
245
|
+
before :each do
|
|
246
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
247
|
+
@config.address = 'domain.url'
|
|
248
|
+
@config.port = 3333
|
|
249
|
+
@config.configuration.searchd.max_matches = 100
|
|
250
|
+
@config.timeout = 1
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "should return an instance of Riddle::Client" do
|
|
254
|
+
@config.client.should be_a(Riddle::Client)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it "should use the configuration address" do
|
|
258
|
+
@config.client.server.should == 'domain.url'
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "should use the configuration port" do
|
|
262
|
+
@config.client.port.should == 3333
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "should use the configuration max matches" do
|
|
266
|
+
@config.client.max_matches.should == 100
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
it "should use the configuration timeout" do
|
|
270
|
+
@config.client.timeout.should == 1
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
describe 'when shuffle is enabled' do
|
|
274
|
+
let(:client) { double('client', :max_matches= => nil, :timeout= => nil) }
|
|
275
|
+
|
|
276
|
+
before :each do
|
|
277
|
+
@config.shuffle = true
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it "should shuffle client servers" do
|
|
281
|
+
@config.address = ['1.1.1.1', '2.2.2.2']
|
|
282
|
+
@config.address.stub!(:shuffle => ['2.2.2.2', '1.1.1.1'])
|
|
283
|
+
|
|
284
|
+
Riddle::Client.should_receive(:new) do |addresses, port, key|
|
|
285
|
+
addresses.should == ['2.2.2.2', '1.1.1.1']
|
|
286
|
+
client
|
|
287
|
+
end
|
|
288
|
+
@config.client
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
describe 'when shuffle is disabled' do
|
|
293
|
+
let(:client) { double('client', :max_matches= => nil, :timeout= => nil) }
|
|
294
|
+
|
|
295
|
+
before :each do
|
|
296
|
+
@config.shuffle = false
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it "should not shuffle client servers" do
|
|
300
|
+
@config.address = ['1.1.1.1', '2.2.2.2.', '3.3.3.3', '4.4.4.4', '5.5.5.5']
|
|
301
|
+
|
|
302
|
+
@config.address.should_not_receive(:shuffle)
|
|
303
|
+
Riddle::Client.should_receive(:new) do |addresses, port, key|
|
|
304
|
+
addresses.should == ['1.1.1.1', '2.2.2.2.', '3.3.3.3', '4.4.4.4', '5.5.5.5']
|
|
305
|
+
client
|
|
306
|
+
end
|
|
307
|
+
@config.client
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
describe '#models_by_crc' do
|
|
313
|
+
before :each do
|
|
314
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it "should return a hash" do
|
|
318
|
+
@config.models_by_crc.should be_a(Hash)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it "should pair class names to their crc codes" do
|
|
322
|
+
@config.models_by_crc[Person.to_crc32].should == 'Person'
|
|
323
|
+
@config.models_by_crc[Alpha.to_crc32].should == 'Alpha'
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::Context do
|
|
4
|
+
let(:ts_context) { ThinkingSphinx::Context.new }
|
|
5
|
+
|
|
6
|
+
describe '#prepare' do
|
|
7
|
+
let(:config) { ThinkingSphinx::Configuration.instance }
|
|
8
|
+
let(:file_name) { 'a.rb' }
|
|
9
|
+
let(:model_name_lower) { 'a' }
|
|
10
|
+
let(:class_name) { 'A' }
|
|
11
|
+
|
|
12
|
+
before :each do
|
|
13
|
+
config.model_directories = ['']
|
|
14
|
+
|
|
15
|
+
file_name.stub!(:gsub).and_return(model_name_lower)
|
|
16
|
+
model_name_lower.stub!(:camelize).and_return(class_name)
|
|
17
|
+
Dir.stub(:[]).and_return([file_name])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should load the files by guessing the file name" do
|
|
21
|
+
class_name.should_receive(:constantize).and_return(true)
|
|
22
|
+
|
|
23
|
+
ts_context.prepare
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should not raise errors if the model name is nil" do
|
|
27
|
+
file_name.stub!(:gsub).and_return(nil)
|
|
28
|
+
|
|
29
|
+
lambda {
|
|
30
|
+
ts_context.prepare
|
|
31
|
+
}.should_not raise_error
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should report name errors but not raise them" do
|
|
35
|
+
class_name.stub(:constantize).and_raise(NameError)
|
|
36
|
+
STDERR.stub!(:puts => '')
|
|
37
|
+
STDERR.should_receive(:puts).with('Warning: Error loading a.rb:')
|
|
38
|
+
|
|
39
|
+
lambda {
|
|
40
|
+
ts_context.prepare
|
|
41
|
+
}.should_not raise_error
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should retry if the first load fails and contains a directory" do
|
|
45
|
+
model_name_lower.should_receive(:gsub!).twice.and_return(true, nil)
|
|
46
|
+
class_name.stub(:constantize).and_raise(LoadError)
|
|
47
|
+
|
|
48
|
+
lambda {
|
|
49
|
+
ts_context.prepare
|
|
50
|
+
}.should_not raise_error
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should catch database errors with a warning" do
|
|
54
|
+
class_name.should_receive(:constantize).and_raise(Mysql2::Error)
|
|
55
|
+
STDERR.stub!(:puts => '')
|
|
56
|
+
STDERR.should_receive(:puts).with('Warning: Error loading a.rb:')
|
|
57
|
+
|
|
58
|
+
lambda {
|
|
59
|
+
ts_context.prepare
|
|
60
|
+
}.should_not raise_error
|
|
61
|
+
end unless RUBY_PLATFORM == 'java'
|
|
62
|
+
|
|
63
|
+
it "should not load models if they're explicitly set in the configuration" do
|
|
64
|
+
config.indexed_models = ['Alpha', 'Beta']
|
|
65
|
+
ts_context.prepare
|
|
66
|
+
|
|
67
|
+
ts_context.indexed_models.should == ['Alpha', 'Beta']
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#define_indexes' do
|
|
72
|
+
it "should call define_indexes on all known indexed models" do
|
|
73
|
+
ts_context.stub!(:indexed_models => ['Alpha', 'Beta'])
|
|
74
|
+
Alpha.should_receive(:define_indexes)
|
|
75
|
+
Beta.should_receive(:define_indexes)
|
|
76
|
+
|
|
77
|
+
ts_context.define_indexes
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe '#add_indexed_model' do
|
|
82
|
+
before :each do
|
|
83
|
+
ts_context.indexed_models.clear
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should add the model to the collection" do
|
|
87
|
+
ts_context.add_indexed_model 'Alpha'
|
|
88
|
+
|
|
89
|
+
ts_context.indexed_models.should == ['Alpha']
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should not duplicate models in the collection" do
|
|
93
|
+
ts_context.add_indexed_model 'Alpha'
|
|
94
|
+
ts_context.add_indexed_model 'Alpha'
|
|
95
|
+
|
|
96
|
+
ts_context.indexed_models.should == ['Alpha']
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should keep the collection in alphabetical order" do
|
|
100
|
+
ts_context.add_indexed_model 'Beta'
|
|
101
|
+
ts_context.add_indexed_model 'Alpha'
|
|
102
|
+
|
|
103
|
+
ts_context.indexed_models.should == ['Alpha', 'Beta']
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should translate classes to their names" do
|
|
107
|
+
ts_context.add_indexed_model Alpha
|
|
108
|
+
|
|
109
|
+
ts_context.indexed_models.should == ['Alpha']
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#superclass_indexed_models' do
|
|
114
|
+
it "should return indexed model names" do
|
|
115
|
+
ts_context.stub!(:indexed_models => ['Alpha', 'Beta'])
|
|
116
|
+
|
|
117
|
+
ts_context.superclass_indexed_models.should == ['Alpha', 'Beta']
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "should not include classes which have indexed superclasses" do
|
|
121
|
+
ts_context.stub!(:indexed_models => ['Parent', 'Person'])
|
|
122
|
+
|
|
123
|
+
ts_context.superclass_indexed_models.should == ['Person']
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::Excerpter do
|
|
4
|
+
before :each do
|
|
5
|
+
@alpha = Alpha.find(:first)
|
|
6
|
+
@search = mock 'search', :excerpt_for => 'excerpted value'
|
|
7
|
+
@excerpter = ThinkingSphinx::Excerpter.new(@search, @alpha)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should not respond to id" do
|
|
11
|
+
@excerpter.should_not respond_to(:id)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#method_missing' do
|
|
15
|
+
it "should return the excerpt from Sphinx" do
|
|
16
|
+
@excerpter.name.should == 'excerpted value'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should send through the instance class to excerpt_for" do
|
|
20
|
+
@search.should_receive(:excerpt_for) do |string, model|
|
|
21
|
+
model.should == Alpha
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@excerpter.name
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should use attribute methods for excerpts calls" do
|
|
28
|
+
@search.should_receive(:excerpt_for) do |string, model|
|
|
29
|
+
string.should == 'one'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@excerpter.name
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should use instance methods for excerpts calls" do
|
|
36
|
+
@search.should_receive(:excerpt_for) do |string, model|
|
|
37
|
+
string.should == 'ONE'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@excerpter.big_name
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should still raise an exception if no column or method exists" do
|
|
44
|
+
lambda {
|
|
45
|
+
@excerpter.foo
|
|
46
|
+
}.should raise_error(NoMethodError)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::FacetSearch do
|
|
4
|
+
let(:search) { stub('search', :append_to => nil, :empty? => true) }
|
|
5
|
+
let(:config) { ThinkingSphinx::Configuration.instance }
|
|
6
|
+
let(:client) { stub('client', :run => []) }
|
|
7
|
+
|
|
8
|
+
before :each do
|
|
9
|
+
config.stub!(:client => client)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe 'populate' do
|
|
13
|
+
before :each do
|
|
14
|
+
config.configuration.searchd.max_matches = 10_000
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should request all shared facets in a multi-model request by default" do
|
|
18
|
+
ThinkingSphinx.stub!(:search => search)
|
|
19
|
+
if Riddle.loaded_version.to_i < 2
|
|
20
|
+
ThinkingSphinx::FacetSearch.new.facet_names.should == ['class_crc']
|
|
21
|
+
else
|
|
22
|
+
ThinkingSphinx::FacetSearch.new.facet_names.should == ['sphinx_internal_class']
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should request all facets in a multi-model request if specified" do
|
|
27
|
+
ThinkingSphinx.stub!(:search => search)
|
|
28
|
+
names = ThinkingSphinx::FacetSearch.new(:all_facets => true).facet_names
|
|
29
|
+
|
|
30
|
+
if Riddle.loaded_version.to_i < 2
|
|
31
|
+
names.should == ['class_crc', 'city_facet', 'state_facet', 'birthday']
|
|
32
|
+
else
|
|
33
|
+
names.should == ['sphinx_internal_class', 'city_facet', 'state_facet', 'birthday']
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should use the system-set max_matches for limit on facet calls" do
|
|
38
|
+
ThinkingSphinx.should_receive(:search) do |options|
|
|
39
|
+
options[:max_matches].should == 10_000
|
|
40
|
+
options[:limit].should == 10_000
|
|
41
|
+
search
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
ThinkingSphinx::FacetSearch.new
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should use the default max-matches if there is no explicit setting" do
|
|
48
|
+
config.configuration.searchd.max_matches = nil
|
|
49
|
+
ThinkingSphinx.should_receive(:search) do |options|
|
|
50
|
+
options[:max_matches].should == 1000
|
|
51
|
+
options[:limit].should == 1000
|
|
52
|
+
search
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
ThinkingSphinx::FacetSearch.new
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should ignore user-provided max_matches and limit on facet calls" do
|
|
59
|
+
ThinkingSphinx.should_receive(:search) do |options|
|
|
60
|
+
options[:max_matches].should == 10_000
|
|
61
|
+
options[:limit].should == 10_000
|
|
62
|
+
search
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
ThinkingSphinx::FacetSearch.new(
|
|
66
|
+
:max_matches => 500,
|
|
67
|
+
:limit => 200
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should not use an explicit :page" do
|
|
72
|
+
ThinkingSphinx.should_receive(:search) do |options|
|
|
73
|
+
options[:page].should == 1
|
|
74
|
+
search
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
ThinkingSphinx::FacetSearch.new(:page => 3)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe "conflicting facets" do
|
|
81
|
+
before :each do
|
|
82
|
+
@index = ThinkingSphinx::Index::Builder.generate(Alpha) do
|
|
83
|
+
indexes :name
|
|
84
|
+
has :value, :as => :city, :facet => true
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
after :each do
|
|
89
|
+
Alpha.sphinx_facets.delete_at(-1)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should raise an error if searching with facets of same name but different type" do
|
|
93
|
+
lambda {
|
|
94
|
+
facets = ThinkingSphinx.facets :all_facets => true
|
|
95
|
+
}.should raise_error
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe ':facets option' do
|
|
100
|
+
it "should limit facets to the requested set" do
|
|
101
|
+
ThinkingSphinx.should_receive(:search).once.and_return(search)
|
|
102
|
+
|
|
103
|
+
ThinkingSphinx::FacetSearch.new(
|
|
104
|
+
:classes => [Person], :facets => :state
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe "empty result set for attributes" do
|
|
110
|
+
before :each do
|
|
111
|
+
ThinkingSphinx.stub!(:search => search)
|
|
112
|
+
@facets = ThinkingSphinx::FacetSearch.new(
|
|
113
|
+
:classes => [Person], :facets => :state
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "should add key as attribute" do
|
|
118
|
+
@facets.should have_key(:state)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should return an empty hash for the facet results" do
|
|
122
|
+
@facets[:state].should be_empty
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe "non-empty result set" do
|
|
127
|
+
before :each do
|
|
128
|
+
@person = Person.find(:first)
|
|
129
|
+
@people = [@person]
|
|
130
|
+
search.stub!(:empty? => false)
|
|
131
|
+
search.stub!(:each_with_match).
|
|
132
|
+
and_yield(@person, {:attributes => {'@groupby' => @person.city.to_crc32, '@count' => 1}})
|
|
133
|
+
ThinkingSphinx::Search.stub!(:bundle_searches => [search])
|
|
134
|
+
|
|
135
|
+
@facets = ThinkingSphinx::FacetSearch.new(
|
|
136
|
+
:classes => [Person], :facets => :city
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "should return a hash" do
|
|
141
|
+
@facets.should be_a_kind_of(Hash)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "should add key as attribute" do
|
|
145
|
+
@facets.keys.should include(:city)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "should return a hash" do
|
|
149
|
+
@facets[:city].should == {@person.city => 1}
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe "#for" do
|
|
155
|
+
before do
|
|
156
|
+
@person = Person.find(:first)
|
|
157
|
+
@people = [@person]
|
|
158
|
+
search.stub!(:each_with_match).
|
|
159
|
+
and_yield(@person, {:attributes => {'@groupby' => @person.city.to_crc32, '@count' => 1}})
|
|
160
|
+
ThinkingSphinx::Search.stub!(:bundle_searches => [search])
|
|
161
|
+
|
|
162
|
+
@facets = ThinkingSphinx::FacetSearch.new(
|
|
163
|
+
:classes => [Person], :facets => :city
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "should return the search results for the attribute and key pair" do
|
|
168
|
+
ThinkingSphinx.should_receive(:search) do |options|
|
|
169
|
+
options[:with].should have_key('city_facet')
|
|
170
|
+
options[:with]['city_facet'].should == @person.city.to_crc32
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
@facets.for(:city => @person.city)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|