thinking-sphinx 1.2.13 → 1.4.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/README.textile +37 -4
- data/VERSION +1 -0
- data/features/abstract_inheritance.feature +10 -0
- data/features/alternate_primary_key.feature +1 -1
- data/features/attribute_updates.feature +49 -5
- data/features/deleting_instances.feature +3 -0
- data/features/excerpts.feature +8 -0
- data/features/facets.feature +15 -1
- data/features/facets_across_model.feature +2 -2
- data/features/field_sorting.feature +18 -0
- data/features/handling_edits.feature +1 -1
- data/features/searching_across_models.feature +2 -2
- data/features/searching_by_index.feature +40 -0
- data/features/searching_by_model.feature +1 -8
- data/features/sphinx_scopes.feature +33 -0
- data/features/step_definitions/alpha_steps.rb +14 -1
- data/features/step_definitions/beta_steps.rb +1 -1
- data/features/step_definitions/common_steps.rb +21 -2
- data/features/step_definitions/facet_steps.rb +4 -0
- data/features/step_definitions/scope_steps.rb +8 -0
- data/features/step_definitions/search_steps.rb +5 -0
- data/features/step_definitions/sphinx_steps.rb +8 -4
- data/features/sti_searching.feature +5 -0
- data/features/support/env.rb +7 -6
- data/features/{support → thinking_sphinx}/db/fixtures/betas.rb +1 -0
- data/features/{support → thinking_sphinx}/db/fixtures/comments.rb +1 -1
- data/features/{support → thinking_sphinx}/db/fixtures/developers.rb +2 -0
- data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
- data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
- data/features/{support → thinking_sphinx}/db/fixtures/people.rb +1 -1
- data/features/{support → thinking_sphinx}/db/fixtures/tags.rb +1 -1
- data/features/{support → thinking_sphinx}/db/migrations/create_alphas.rb +1 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_developers.rb +0 -2
- 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/models/alpha.rb +23 -0
- data/features/thinking_sphinx/models/andrew.rb +17 -0
- data/features/{support → thinking_sphinx}/models/beta.rb +1 -1
- data/features/{support → thinking_sphinx}/models/developer.rb +2 -2
- data/features/{support → thinking_sphinx}/models/extensible_beta.rb +1 -1
- data/features/thinking_sphinx/models/fox.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 +8 -0
- data/features/{support → thinking_sphinx}/models/person.rb +2 -1
- data/features/{support → thinking_sphinx}/models/post.rb +2 -1
- data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
- data/lib/cucumber/thinking_sphinx/internal_world.rb +13 -11
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +17 -15
- data/lib/thinking_sphinx/active_record/delta.rb +0 -26
- data/lib/thinking_sphinx/active_record/has_many_association.rb +34 -11
- data/lib/thinking_sphinx/active_record/scopes.rb +46 -3
- data/lib/thinking_sphinx/active_record.rb +271 -193
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +45 -9
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +5 -1
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +9 -1
- data/lib/thinking_sphinx/attribute.rb +67 -23
- data/lib/thinking_sphinx/auto_version.rb +24 -0
- data/lib/thinking_sphinx/bundled_search.rb +44 -0
- data/lib/thinking_sphinx/class_facet.rb +3 -2
- data/lib/thinking_sphinx/configuration.rb +78 -64
- data/lib/thinking_sphinx/context.rb +76 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
- data/lib/thinking_sphinx/deltas.rb +0 -2
- data/lib/thinking_sphinx/deploy/capistrano.rb +1 -1
- data/lib/thinking_sphinx/excerpter.rb +1 -1
- data/lib/thinking_sphinx/facet.rb +6 -5
- data/lib/thinking_sphinx/facet_search.rb +54 -24
- data/lib/thinking_sphinx/field.rb +2 -4
- data/lib/thinking_sphinx/index/builder.rb +36 -20
- data/lib/thinking_sphinx/index/faux_column.rb +8 -0
- data/lib/thinking_sphinx/index.rb +77 -19
- data/lib/thinking_sphinx/join.rb +37 -0
- data/lib/thinking_sphinx/property.rb +9 -2
- data/lib/thinking_sphinx/rails_additions.rb +4 -4
- data/lib/thinking_sphinx/search.rb +212 -66
- data/lib/thinking_sphinx/search_methods.rb +22 -4
- data/lib/thinking_sphinx/source/internal_properties.rb +2 -2
- data/lib/thinking_sphinx/source/sql.rb +5 -3
- data/lib/thinking_sphinx/source.rb +21 -12
- data/lib/thinking_sphinx/tasks.rb +26 -58
- data/lib/thinking_sphinx/test.rb +55 -0
- data/lib/thinking_sphinx.rb +70 -38
- data/rails/init.rb +4 -2
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +6 -8
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +26 -3
- data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
- data/spec/thinking_sphinx/active_record_spec.rb +618 -0
- data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +134 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +1 -1
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +87 -46
- data/spec/thinking_sphinx/auto_version_spec.rb +47 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +73 -63
- data/spec/thinking_sphinx/context_spec.rb +127 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +1 -1
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +1 -1
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +1 -9
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +76 -82
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +5 -5
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +1 -42
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +71 -31
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +8 -2
- data/spec/thinking_sphinx/index_spec.rb +183 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +5 -5
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +5 -1
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +183 -31
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +18 -2
- data/spec/thinking_sphinx/test_spec.rb +20 -0
- data/spec/thinking_sphinx_spec.rb +204 -0
- data/tasks/distribution.rb +7 -26
- data/tasks/testing.rb +32 -20
- metadata +488 -147
- data/VERSION.yml +0 -5
- data/features/datetime_deltas.feature +0 -66
- data/features/delayed_delta_indexing.feature +0 -37
- data/features/step_definitions/datetime_delta_steps.rb +0 -15
- data/features/step_definitions/delayed_delta_indexing_steps.rb +0 -7
- data/features/support/database.yml +0 -5
- data/features/support/db/active_record.rb +0 -40
- data/features/support/db/database.yml +0 -5
- data/features/support/db/fixtures/delayed_betas.rb +0 -10
- data/features/support/db/fixtures/thetas.rb +0 -10
- data/features/support/db/migrations/create_delayed_betas.rb +0 -17
- data/features/support/db/migrations/create_thetas.rb +0 -5
- data/features/support/db/mysql.rb +0 -3
- data/features/support/db/postgresql.rb +0 -3
- data/features/support/models/alpha.rb +0 -10
- data/features/support/models/delayed_beta.rb +0 -7
- data/features/support/models/theta.rb +0 -7
- data/features/support/post_database.rb +0 -43
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +0 -50
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +0 -24
- data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +0 -27
- data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +0 -26
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +0 -30
- data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +0 -96
- data/spec/lib/thinking_sphinx/active_record_spec.rb +0 -353
- data/spec/lib/thinking_sphinx/deltas/job_spec.rb +0 -32
- data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
- data/spec/lib/thinking_sphinx_spec.rb +0 -162
- data/vendor/after_commit/LICENSE +0 -20
- data/vendor/after_commit/README +0 -16
- data/vendor/after_commit/Rakefile +0 -22
- data/vendor/after_commit/init.rb +0 -8
- data/vendor/after_commit/lib/after_commit/active_record.rb +0 -114
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +0 -103
- data/vendor/after_commit/lib/after_commit.rb +0 -45
- data/vendor/after_commit/test/after_commit_test.rb +0 -53
- data/vendor/delayed_job/lib/delayed/job.rb +0 -251
- data/vendor/delayed_job/lib/delayed/message_sending.rb +0 -7
- data/vendor/delayed_job/lib/delayed/performable_method.rb +0 -55
- data/vendor/delayed_job/lib/delayed/worker.rb +0 -54
- data/vendor/riddle/lib/riddle/client/filter.rb +0 -53
- data/vendor/riddle/lib/riddle/client/message.rb +0 -66
- data/vendor/riddle/lib/riddle/client/response.rb +0 -84
- data/vendor/riddle/lib/riddle/client.rb +0 -635
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +0 -48
- data/vendor/riddle/lib/riddle/configuration/index.rb +0 -142
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +0 -19
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +0 -17
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +0 -25
- data/vendor/riddle/lib/riddle/configuration/section.rb +0 -43
- data/vendor/riddle/lib/riddle/configuration/source.rb +0 -23
- data/vendor/riddle/lib/riddle/configuration/sql_source.rb +0 -34
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +0 -28
- data/vendor/riddle/lib/riddle/configuration.rb +0 -33
- data/vendor/riddle/lib/riddle/controller.rb +0 -53
- data/vendor/riddle/lib/riddle.rb +0 -30
- data/features/{support → thinking_sphinx}/database.example.yml +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/alphas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/authors.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/boxes.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/categories.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/cats.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/dogs.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/extensible_betas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/gammas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/posts.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/robots.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_animals.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_authors.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_authors_posts.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_betas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_boxes.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_categories.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_comments.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_extensible_betas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_gammas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_people.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_posts.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_robots.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_taggings.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_tags.rb +0 -0
- data/features/{support → thinking_sphinx}/models/animal.rb +0 -0
- data/features/{support → thinking_sphinx}/models/author.rb +0 -0
- data/features/{support → thinking_sphinx}/models/box.rb +0 -0
- data/features/{support → thinking_sphinx}/models/cat.rb +0 -0
- data/features/{support → thinking_sphinx}/models/category.rb +0 -0
- data/features/{support → thinking_sphinx}/models/comment.rb +3 -3
- /data/features/{support → thinking_sphinx}/models/dog.rb +0 -0
- /data/features/{support → thinking_sphinx}/models/gamma.rb +0 -0
- /data/features/{support → thinking_sphinx}/models/robot.rb +0 -0
- /data/features/{support → thinking_sphinx}/models/tag.rb +0 -0
- /data/features/{support → thinking_sphinx}/models/tagging.rb +0 -0
|
@@ -14,7 +14,7 @@ module ThinkingSphinx
|
|
|
14
14
|
kind_of? member? method methods nil? object_id respond_to? send should
|
|
15
15
|
type )
|
|
16
16
|
SafeMethods = %w( partition private_methods protected_methods
|
|
17
|
-
public_methods send )
|
|
17
|
+
public_methods send class )
|
|
18
18
|
|
|
19
19
|
instance_methods.select { |method|
|
|
20
20
|
method.to_s[/^__/].nil? && !CoreMethods.include?(method.to_s)
|
|
@@ -57,10 +57,38 @@ module ThinkingSphinx
|
|
|
57
57
|
ThinkingSphinx.facets *args
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
def self.bundle_searches(enum = nil)
|
|
61
|
+
bundle = ThinkingSphinx::BundledSearch.new
|
|
62
|
+
|
|
63
|
+
if enum.nil?
|
|
64
|
+
yield bundle
|
|
65
|
+
else
|
|
66
|
+
enum.each { |item| yield bundle, item }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
bundle.searches
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.matching_fields(fields, bitmask)
|
|
73
|
+
matches = []
|
|
74
|
+
bitstring = bitmask.to_s(2).rjust(32, '0').reverse
|
|
75
|
+
|
|
76
|
+
fields.each_with_index do |field, index|
|
|
77
|
+
matches << field if bitstring[index, 1] == '1'
|
|
78
|
+
end
|
|
79
|
+
matches
|
|
80
|
+
end
|
|
81
|
+
|
|
60
82
|
def initialize(*args)
|
|
83
|
+
ThinkingSphinx.context.define_indexes
|
|
84
|
+
|
|
61
85
|
@array = []
|
|
62
86
|
@options = args.extract_options!
|
|
63
87
|
@args = args
|
|
88
|
+
|
|
89
|
+
add_default_scope unless options[:ignore_default]
|
|
90
|
+
|
|
91
|
+
populate if @options[:populate]
|
|
64
92
|
end
|
|
65
93
|
|
|
66
94
|
def to_a
|
|
@@ -68,6 +96,12 @@ module ThinkingSphinx
|
|
|
68
96
|
@array
|
|
69
97
|
end
|
|
70
98
|
|
|
99
|
+
def freeze
|
|
100
|
+
populate
|
|
101
|
+
@array.freeze
|
|
102
|
+
self
|
|
103
|
+
end
|
|
104
|
+
|
|
71
105
|
# Indication of whether the request has been made to Sphinx for the search
|
|
72
106
|
# query.
|
|
73
107
|
#
|
|
@@ -90,6 +124,9 @@ module ThinkingSphinx
|
|
|
90
124
|
if is_scope?(method)
|
|
91
125
|
add_scope(method, *args, &block)
|
|
92
126
|
return self
|
|
127
|
+
elsif method == :search_count
|
|
128
|
+
merge_search one_class.search(*args), self.args, options
|
|
129
|
+
return scoped_count
|
|
93
130
|
elsif method.to_s[/^each_with_.*/].nil? && !@array.respond_to?(method)
|
|
94
131
|
super
|
|
95
132
|
elsif !SafeMethods.include?(method.to_s)
|
|
@@ -109,8 +146,8 @@ module ThinkingSphinx
|
|
|
109
146
|
# @param [Symbol] method The method name
|
|
110
147
|
# @return [Boolean] true if either Search or Array responds to the method.
|
|
111
148
|
#
|
|
112
|
-
def respond_to?(method)
|
|
113
|
-
super || @array.respond_to?(method)
|
|
149
|
+
def respond_to?(method, include_private = false)
|
|
150
|
+
super || @array.respond_to?(method, include_private)
|
|
114
151
|
end
|
|
115
152
|
|
|
116
153
|
# The current page number of the result set. Defaults to 1 if no page was
|
|
@@ -146,7 +183,9 @@ module ThinkingSphinx
|
|
|
146
183
|
# @return [Integer]
|
|
147
184
|
#
|
|
148
185
|
def per_page
|
|
149
|
-
@options[:limit]
|
|
186
|
+
@options[:limit] ||= @options[:per_page]
|
|
187
|
+
@options[:limit] ||= 20
|
|
188
|
+
@options[:limit].to_i
|
|
150
189
|
end
|
|
151
190
|
|
|
152
191
|
# The total number of pages available if the results are paginated.
|
|
@@ -155,33 +194,51 @@ module ThinkingSphinx
|
|
|
155
194
|
#
|
|
156
195
|
def total_pages
|
|
157
196
|
populate
|
|
197
|
+
return 0 if @results[:total].nil?
|
|
198
|
+
|
|
158
199
|
@total_pages ||= (@results[:total] / per_page.to_f).ceil
|
|
159
200
|
end
|
|
160
201
|
# Compatibility with older versions of will_paginate
|
|
161
202
|
alias_method :page_count, :total_pages
|
|
162
203
|
|
|
204
|
+
# Query time taken
|
|
205
|
+
#
|
|
206
|
+
# @return [Integer]
|
|
207
|
+
#
|
|
208
|
+
def query_time
|
|
209
|
+
populate
|
|
210
|
+
return 0 if @results[:time].nil?
|
|
211
|
+
|
|
212
|
+
@query_time ||= @results[:time]
|
|
213
|
+
end
|
|
214
|
+
|
|
163
215
|
# The total number of search results available.
|
|
164
216
|
#
|
|
165
217
|
# @return [Integer]
|
|
166
218
|
#
|
|
167
219
|
def total_entries
|
|
168
220
|
populate
|
|
221
|
+
return 0 if @results[:total_found].nil?
|
|
222
|
+
|
|
169
223
|
@total_entries ||= @results[:total_found]
|
|
170
224
|
end
|
|
171
225
|
|
|
172
226
|
# The current page's offset, based on the number of records per page.
|
|
227
|
+
# Or explicit :offset if given.
|
|
173
228
|
#
|
|
174
229
|
# @return [Integer]
|
|
175
230
|
#
|
|
176
231
|
def offset
|
|
177
|
-
(current_page - 1) * per_page
|
|
232
|
+
@options[:offset] || ((current_page - 1) * per_page)
|
|
178
233
|
end
|
|
179
234
|
|
|
180
235
|
def indexes
|
|
181
236
|
return options[:index] if options[:index]
|
|
182
237
|
return '*' if classes.empty?
|
|
183
238
|
|
|
184
|
-
classes.collect { |klass|
|
|
239
|
+
classes.collect { |klass|
|
|
240
|
+
klass.sphinx_index_names
|
|
241
|
+
}.flatten.uniq.join(',')
|
|
185
242
|
end
|
|
186
243
|
|
|
187
244
|
def each_with_groupby_and_count(&block)
|
|
@@ -201,6 +258,13 @@ module ThinkingSphinx
|
|
|
201
258
|
end
|
|
202
259
|
end
|
|
203
260
|
|
|
261
|
+
def each_with_match(&block)
|
|
262
|
+
populate
|
|
263
|
+
results[:matches].each_with_index do |match, index|
|
|
264
|
+
yield self[index], match
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
204
268
|
def excerpt_for(string, model = nil)
|
|
205
269
|
if model.nil? && one_class
|
|
206
270
|
model ||= one_class
|
|
@@ -208,17 +272,66 @@ module ThinkingSphinx
|
|
|
208
272
|
|
|
209
273
|
populate
|
|
210
274
|
client.excerpts(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
275
|
+
{
|
|
276
|
+
:docs => [string.to_s],
|
|
277
|
+
:words => results[:words].keys.join(' '),
|
|
278
|
+
:index => options[:index] || "#{model.source_of_sphinx_index.sphinx_name}_core"
|
|
279
|
+
}.merge(options[:excerpt_options] || {})
|
|
214
280
|
).first
|
|
215
281
|
end
|
|
216
282
|
|
|
217
283
|
def search(*args)
|
|
218
|
-
|
|
284
|
+
args << args.extract_options!.merge(:ignore_default => true)
|
|
285
|
+
merge_search ThinkingSphinx::Search.new(*args), self.args, options
|
|
219
286
|
self
|
|
220
287
|
end
|
|
221
288
|
|
|
289
|
+
def search_for_ids(*args)
|
|
290
|
+
args << args.extract_options!.merge(
|
|
291
|
+
:ignore_default => true,
|
|
292
|
+
:ids_only => true
|
|
293
|
+
)
|
|
294
|
+
merge_search ThinkingSphinx::Search.new(*args), self.args, options
|
|
295
|
+
self
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def facets(*args)
|
|
299
|
+
options = args.extract_options!
|
|
300
|
+
merge_search self, args, options
|
|
301
|
+
args << options
|
|
302
|
+
|
|
303
|
+
ThinkingSphinx::FacetSearch.new *args
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def client
|
|
307
|
+
client = options[:client] || config.client
|
|
308
|
+
|
|
309
|
+
prepare client
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def append_to(client)
|
|
313
|
+
prepare client
|
|
314
|
+
client.append_query query, indexes, comment
|
|
315
|
+
client.reset
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def populate_from_queue(results)
|
|
319
|
+
return if @populated
|
|
320
|
+
@populated = true
|
|
321
|
+
@results = results
|
|
322
|
+
|
|
323
|
+
if options[:ids_only]
|
|
324
|
+
replace @results[:matches].collect { |match|
|
|
325
|
+
match[:attributes]["sphinx_internal_id"]
|
|
326
|
+
}
|
|
327
|
+
else
|
|
328
|
+
replace instances_from_matches
|
|
329
|
+
add_excerpter
|
|
330
|
+
add_sphinx_attributes
|
|
331
|
+
add_matching_fields if client.rank_mode == :fieldmask
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
222
335
|
private
|
|
223
336
|
|
|
224
337
|
def config
|
|
@@ -231,8 +344,12 @@ module ThinkingSphinx
|
|
|
231
344
|
|
|
232
345
|
retry_on_stale_index do
|
|
233
346
|
begin
|
|
234
|
-
log "Querying
|
|
235
|
-
|
|
347
|
+
log "Querying: '#{query}'"
|
|
348
|
+
runtime = Benchmark.realtime {
|
|
349
|
+
@results = client.query query, indexes, comment
|
|
350
|
+
}
|
|
351
|
+
log "Found #{@results[:total_found]} results", :debug,
|
|
352
|
+
"Sphinx (#{sprintf("%f", runtime)}s)"
|
|
236
353
|
rescue Errno::ECONNREFUSED => err
|
|
237
354
|
raise ThinkingSphinx::ConnectionError,
|
|
238
355
|
'Connection to Sphinx Daemon (searchd) failed.'
|
|
@@ -246,52 +363,70 @@ module ThinkingSphinx
|
|
|
246
363
|
replace instances_from_matches
|
|
247
364
|
add_excerpter
|
|
248
365
|
add_sphinx_attributes
|
|
366
|
+
add_matching_fields if client.rank_mode == :fieldmask
|
|
249
367
|
end
|
|
250
368
|
end
|
|
251
369
|
end
|
|
252
370
|
|
|
253
371
|
def add_excerpter
|
|
254
372
|
each do |object|
|
|
255
|
-
next if object.
|
|
256
|
-
|
|
257
|
-
excerpter = ThinkingSphinx::Excerpter.new self, object
|
|
258
|
-
block = lambda { excerpter }
|
|
373
|
+
next if object.nil?
|
|
259
374
|
|
|
260
|
-
object.
|
|
261
|
-
define_method(:excerpts, &block)
|
|
262
|
-
end
|
|
375
|
+
object.excerpts = ThinkingSphinx::Excerpter.new self, object
|
|
263
376
|
end
|
|
264
377
|
end
|
|
265
378
|
|
|
266
379
|
def add_sphinx_attributes
|
|
267
380
|
each do |object|
|
|
268
|
-
next if object.nil?
|
|
381
|
+
next if object.nil?
|
|
269
382
|
|
|
270
|
-
match =
|
|
271
|
-
match[:attributes]['sphinx_internal_id'] == object.
|
|
272
|
-
primary_key_for_sphinx &&
|
|
273
|
-
match[:attributes]['class_crc'] == object.class.to_crc32
|
|
274
|
-
}
|
|
383
|
+
match = match_hash object
|
|
275
384
|
next if match.nil?
|
|
276
385
|
|
|
277
|
-
object.
|
|
278
|
-
define_method(:sphinx_attributes) { match[:attributes] }
|
|
279
|
-
end
|
|
386
|
+
object.sphinx_attributes = match[:attributes]
|
|
280
387
|
end
|
|
281
388
|
end
|
|
282
389
|
|
|
283
|
-
def
|
|
284
|
-
|
|
285
|
-
|
|
390
|
+
def add_matching_fields
|
|
391
|
+
each do |object|
|
|
392
|
+
next if object.nil?
|
|
393
|
+
|
|
394
|
+
match = match_hash object
|
|
395
|
+
next if match.nil?
|
|
396
|
+
object.matching_fields = ThinkingSphinx::Search.matching_fields(
|
|
397
|
+
@results[:fields], match[:weight]
|
|
398
|
+
)
|
|
399
|
+
end
|
|
286
400
|
end
|
|
287
401
|
|
|
288
|
-
def
|
|
289
|
-
|
|
402
|
+
def match_hash(object)
|
|
403
|
+
@results[:matches].detect { |match|
|
|
404
|
+
match[:attributes]['sphinx_internal_id'] == object.
|
|
405
|
+
primary_key_for_sphinx &&
|
|
406
|
+
match[:attributes]['class_crc'] == object.class.to_crc32
|
|
407
|
+
}
|
|
290
408
|
end
|
|
291
409
|
|
|
292
|
-
def
|
|
293
|
-
|
|
410
|
+
def self.log(message, method = :debug, identifier = 'Sphinx')
|
|
411
|
+
return if ::ActiveRecord::Base.logger.nil?
|
|
412
|
+
|
|
413
|
+
info = ''
|
|
414
|
+
if ::ActiveRecord::Base.colorize_logging
|
|
415
|
+
identifier_color, message_color = "4;32;1", "0" # 0;1 = Bold
|
|
416
|
+
info << " \e[#{identifier_color}m#{identifier}\e[0m "
|
|
417
|
+
info << "\e[#{message_color}m#{message}\e[0m"
|
|
418
|
+
else
|
|
419
|
+
info = "#{identifier} #{message}"
|
|
420
|
+
end
|
|
294
421
|
|
|
422
|
+
::ActiveRecord::Base.logger.send method, info
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def log(*args)
|
|
426
|
+
self.class.log(*args)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def prepare(client)
|
|
295
430
|
index_options = one_class ?
|
|
296
431
|
one_class.sphinx_indexes.first.local_options : {}
|
|
297
432
|
|
|
@@ -300,11 +435,12 @@ module ThinkingSphinx
|
|
|
300
435
|
:group_distinct, :id_range, :cut_off, :retry_count, :retry_delay,
|
|
301
436
|
:rank_mode, :max_query_time, :field_weights
|
|
302
437
|
].each do |key|
|
|
303
|
-
# puts "key: #{key}"
|
|
304
438
|
value = options[key] || index_options[key]
|
|
305
|
-
# puts "value: #{value.inspect}"
|
|
306
439
|
client.send("#{key}=", value) if value
|
|
307
440
|
end
|
|
441
|
+
|
|
442
|
+
# treated non-standard as :select is already used for AR queries
|
|
443
|
+
client.select = options[:sphinx_select] || '*'
|
|
308
444
|
|
|
309
445
|
client.limit = per_page
|
|
310
446
|
client.offset = offset
|
|
@@ -381,7 +517,8 @@ module ThinkingSphinx
|
|
|
381
517
|
query.gsub(/("#{token}(.*?#{token})?"|(?![!-])#{token})/u) do
|
|
382
518
|
pre, proper, post = $`, $&, $'
|
|
383
519
|
# E.g. "@foo", "/2", "~3", but not as part of a token
|
|
384
|
-
is_operator = pre.match(%r{(\W|^)[@~/]\Z})
|
|
520
|
+
is_operator = pre.match(%r{(\W|^)[@~/]\Z}) ||
|
|
521
|
+
pre.match(%r{(\W|^)@\([^\)]*$})
|
|
385
522
|
# E.g. "foo bar", with quotes
|
|
386
523
|
is_quote = proper.starts_with?('"') && proper.ends_with?('"')
|
|
387
524
|
has_star = pre.ends_with?("*") || post.starts_with?("*")
|
|
@@ -495,24 +632,8 @@ module ThinkingSphinx
|
|
|
495
632
|
filters
|
|
496
633
|
end
|
|
497
634
|
|
|
498
|
-
def condition_filters
|
|
499
|
-
(options[:conditions] || {}).collect { |attrib, value|
|
|
500
|
-
if attributes.include?(attrib.to_sym)
|
|
501
|
-
puts <<-MSG
|
|
502
|
-
Deprecation Warning: filters on attributes should be done using the :with
|
|
503
|
-
option, not :conditions. For example:
|
|
504
|
-
:with => {:#{attrib} => #{value.inspect}}
|
|
505
|
-
MSG
|
|
506
|
-
Riddle::Client::Filter.new attrib.to_s, filter_value(value)
|
|
507
|
-
else
|
|
508
|
-
nil
|
|
509
|
-
end
|
|
510
|
-
}.compact
|
|
511
|
-
end
|
|
512
|
-
|
|
513
635
|
def filters
|
|
514
636
|
internal_filters +
|
|
515
|
-
condition_filters +
|
|
516
637
|
(options[:with] || {}).collect { |attrib, value|
|
|
517
638
|
Riddle::Client::Filter.new attrib.to_s, filter_value(value)
|
|
518
639
|
} +
|
|
@@ -527,14 +648,6 @@ MSG
|
|
|
527
648
|
end
|
|
528
649
|
|
|
529
650
|
# When passed a Time instance, returns the integer timestamp.
|
|
530
|
-
#
|
|
531
|
-
# If using Rails 2.1+, need to handle timezones to translate them back to
|
|
532
|
-
# UTC, as that's what datetimes will be stored as by MySQL.
|
|
533
|
-
#
|
|
534
|
-
# in_time_zone is a method that was added for the timezone support in
|
|
535
|
-
# Rails 2.1, which is why it's used for testing. I'm sure there's better
|
|
536
|
-
# ways, but this does the job.
|
|
537
|
-
#
|
|
538
651
|
def filter_value(value)
|
|
539
652
|
case value
|
|
540
653
|
when Range
|
|
@@ -542,7 +655,7 @@ MSG
|
|
|
542
655
|
when Array
|
|
543
656
|
value.collect { |v| filter_value(v) }.flatten
|
|
544
657
|
when Time
|
|
545
|
-
|
|
658
|
+
[value.to_i]
|
|
546
659
|
when NilClass
|
|
547
660
|
0
|
|
548
661
|
else
|
|
@@ -608,6 +721,21 @@ MSG
|
|
|
608
721
|
end
|
|
609
722
|
end
|
|
610
723
|
|
|
724
|
+
def include_for_class(klass)
|
|
725
|
+
includes = options[:include] || klass.sphinx_index_options[:include]
|
|
726
|
+
|
|
727
|
+
case includes
|
|
728
|
+
when NilClass
|
|
729
|
+
nil
|
|
730
|
+
when Array
|
|
731
|
+
includes.select { |inc| klass.reflections[inc] }
|
|
732
|
+
when Symbol
|
|
733
|
+
klass.reflections[includes].nil? ? nil : includes
|
|
734
|
+
else
|
|
735
|
+
includes
|
|
736
|
+
end
|
|
737
|
+
end
|
|
738
|
+
|
|
611
739
|
def instances_from_class(klass, matches)
|
|
612
740
|
index_options = klass.sphinx_index_options
|
|
613
741
|
|
|
@@ -616,7 +744,7 @@ MSG
|
|
|
616
744
|
:all,
|
|
617
745
|
:joins => options[:joins],
|
|
618
746
|
:conditions => {klass.primary_key_for_sphinx.to_sym => ids},
|
|
619
|
-
:include => (
|
|
747
|
+
:include => include_for_class(klass),
|
|
620
748
|
:select => (options[:select] || index_options[:select]),
|
|
621
749
|
:order => (options[:sql_order] || index_options[:sql_order])
|
|
622
750
|
) : []
|
|
@@ -684,11 +812,18 @@ MSG
|
|
|
684
812
|
one_class && one_class.sphinx_scopes.include?(method)
|
|
685
813
|
end
|
|
686
814
|
|
|
815
|
+
# Adds the default_sphinx_scope if set.
|
|
816
|
+
def add_default_scope
|
|
817
|
+
return unless one_class && one_class.has_default_sphinx_scope?
|
|
818
|
+
add_scope(one_class.get_default_sphinx_scope.to_sym)
|
|
819
|
+
end
|
|
820
|
+
|
|
687
821
|
def add_scope(method, *args, &block)
|
|
688
|
-
|
|
822
|
+
method = "#{method}_without_default".to_sym
|
|
823
|
+
merge_search one_class.send(method, *args, &block), self.args, options
|
|
689
824
|
end
|
|
690
825
|
|
|
691
|
-
def merge_search(search)
|
|
826
|
+
def merge_search(search, args, options)
|
|
692
827
|
search.args.each { |arg| args << arg }
|
|
693
828
|
|
|
694
829
|
search.options.keys.each do |key|
|
|
@@ -704,5 +839,16 @@ MSG
|
|
|
704
839
|
end
|
|
705
840
|
end
|
|
706
841
|
end
|
|
842
|
+
|
|
843
|
+
def scoped_count
|
|
844
|
+
return self.total_entries if @options[:ids_only]
|
|
845
|
+
|
|
846
|
+
@options[:ids_only] = true
|
|
847
|
+
results_count = self.total_entries
|
|
848
|
+
@options[:ids_only] = false
|
|
849
|
+
@populated = false
|
|
850
|
+
|
|
851
|
+
results_count
|
|
852
|
+
end
|
|
707
853
|
end
|
|
708
854
|
end
|
|
@@ -155,7 +155,7 @@ module ThinkingSphinx
|
|
|
155
155
|
# asterisks. You need to make the config/sphinx.yml changes yourself.
|
|
156
156
|
#
|
|
157
157
|
# By default, the tokens are assumed to match the regular expression
|
|
158
|
-
# /\w
|
|
158
|
+
# /\w\+/u\+. If you've modified the charset_table, pass another regular
|
|
159
159
|
# expression, e.g.
|
|
160
160
|
#
|
|
161
161
|
# User.search("oo@bar.c", :star => /[\w@.]+/u)
|
|
@@ -313,13 +313,31 @@ module ThinkingSphinx
|
|
|
313
313
|
# Once you've got your results set, you can access the distances as
|
|
314
314
|
# follows:
|
|
315
315
|
#
|
|
316
|
-
#
|
|
317
|
-
#
|
|
318
|
-
#
|
|
316
|
+
# @results.each_with_geodist do |result, distance|
|
|
317
|
+
# # ...
|
|
318
|
+
# end
|
|
319
319
|
#
|
|
320
320
|
# The distance value is returned as a float, representing the distance in
|
|
321
321
|
# metres.
|
|
322
322
|
#
|
|
323
|
+
# == Filtering by custom attributes
|
|
324
|
+
#
|
|
325
|
+
# Do note that this applies only to sphinx 0.9.9
|
|
326
|
+
#
|
|
327
|
+
# Should you find yourself in desperate need of a filter that involves
|
|
328
|
+
# selecting either one of multiple conditions, one solution could be
|
|
329
|
+
# provided by the :sphinx_select option within the search.
|
|
330
|
+
# This handles which fields are selected by sphinx from its store.
|
|
331
|
+
#
|
|
332
|
+
# The default value is "*", and you can add custom fields using syntax
|
|
333
|
+
# similar to sql:
|
|
334
|
+
#
|
|
335
|
+
# Flower.search "foo",
|
|
336
|
+
# :sphinx_select => "*, petals < 1 or color = 2 as grass"
|
|
337
|
+
#
|
|
338
|
+
# This will add the 'grass' attribute, which will now be usable in your
|
|
339
|
+
# filters.
|
|
340
|
+
#
|
|
323
341
|
# == Handling a Stale Index
|
|
324
342
|
#
|
|
325
343
|
# Especially if you don't use delta indexing, you risk having records in
|
|
@@ -2,9 +2,9 @@ module ThinkingSphinx
|
|
|
2
2
|
class Source
|
|
3
3
|
module InternalProperties
|
|
4
4
|
def add_internal_attributes_and_facets
|
|
5
|
-
add_internal_attribute :sphinx_internal_id,
|
|
5
|
+
add_internal_attribute :sphinx_internal_id, nil,
|
|
6
|
+
@model.primary_key_for_sphinx.to_sym
|
|
6
7
|
add_internal_attribute :class_crc, :integer, crc_column, true
|
|
7
|
-
add_internal_attribute :subclass_crcs, :multi, subclasses_to_s
|
|
8
8
|
add_internal_attribute :sphinx_deleted, :integer, "0"
|
|
9
9
|
|
|
10
10
|
add_internal_facet :class_crc
|
|
@@ -13,8 +13,10 @@ module ThinkingSphinx
|
|
|
13
13
|
# source.to_sql(:delta => true)
|
|
14
14
|
#
|
|
15
15
|
def to_sql(options={})
|
|
16
|
-
sql =
|
|
17
|
-
|
|
16
|
+
sql = "SELECT "
|
|
17
|
+
sql += "SQL_NO_CACHE " if adapter.sphinx_identifier == "mysql"
|
|
18
|
+
sql += <<-SQL
|
|
19
|
+
#{ sql_select_clause options[:offset] }
|
|
18
20
|
FROM #{ @model.quoted_table_name }
|
|
19
21
|
#{ all_associations.collect { |assoc| assoc.to_sql }.join(' ') }
|
|
20
22
|
#{ sql_where_clause(options) }
|
|
@@ -53,7 +55,7 @@ GROUP BY #{ sql_group_clause }
|
|
|
53
55
|
#
|
|
54
56
|
def to_sql_query_info(offset)
|
|
55
57
|
"SELECT * FROM #{@model.quoted_table_name} WHERE " +
|
|
56
|
-
"#{quote_column(@model.primary_key_for_sphinx)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
|
|
58
|
+
"#{quote_column(@model.primary_key_for_sphinx)} = (($id - #{offset}) / #{ThinkingSphinx.context.indexed_models.size})"
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
def sql_select_clause(offset)
|
|
@@ -6,19 +6,22 @@ module ThinkingSphinx
|
|
|
6
6
|
include ThinkingSphinx::Source::InternalProperties
|
|
7
7
|
include ThinkingSphinx::Source::SQL
|
|
8
8
|
|
|
9
|
-
attr_accessor :model, :fields, :attributes, :conditions, :groupings,
|
|
9
|
+
attr_accessor :model, :fields, :attributes, :joins, :conditions, :groupings,
|
|
10
10
|
:options
|
|
11
|
-
attr_reader :base, :index
|
|
11
|
+
attr_reader :base, :index, :database_configuration
|
|
12
12
|
|
|
13
13
|
def initialize(index, options = {})
|
|
14
14
|
@index = index
|
|
15
15
|
@model = index.model
|
|
16
16
|
@fields = []
|
|
17
17
|
@attributes = []
|
|
18
|
+
@joins = []
|
|
18
19
|
@conditions = []
|
|
19
20
|
@groupings = []
|
|
20
21
|
@options = options
|
|
21
22
|
@associations = {}
|
|
23
|
+
@database_configuration = @model.connection.
|
|
24
|
+
instance_variable_get(:@config).clone
|
|
22
25
|
|
|
23
26
|
@base = ::ActiveRecord::Associations::ClassMethods::JoinDependency.new(
|
|
24
27
|
@model, [], nil
|
|
@@ -33,30 +36,31 @@ module ThinkingSphinx
|
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
def name
|
|
36
|
-
|
|
39
|
+
index.name
|
|
37
40
|
end
|
|
38
41
|
|
|
39
|
-
def to_riddle_for_core(offset,
|
|
42
|
+
def to_riddle_for_core(offset, position)
|
|
40
43
|
source = Riddle::Configuration::SQLSource.new(
|
|
41
|
-
"#{
|
|
44
|
+
"#{index.core_name}_#{position}", adapter.sphinx_identifier
|
|
42
45
|
)
|
|
43
46
|
|
|
44
47
|
set_source_database_settings source
|
|
45
48
|
set_source_attributes source, offset
|
|
46
|
-
set_source_sql source, offset
|
|
47
49
|
set_source_settings source
|
|
50
|
+
set_source_sql source, offset
|
|
48
51
|
|
|
49
52
|
source
|
|
50
53
|
end
|
|
51
54
|
|
|
52
|
-
def to_riddle_for_delta(offset,
|
|
55
|
+
def to_riddle_for_delta(offset, position)
|
|
53
56
|
source = Riddle::Configuration::SQLSource.new(
|
|
54
|
-
"#{
|
|
57
|
+
"#{index.delta_name}_#{position}", adapter.sphinx_identifier
|
|
55
58
|
)
|
|
56
|
-
source.parent = "#{
|
|
59
|
+
source.parent = "#{index.core_name}_#{position}"
|
|
57
60
|
|
|
58
61
|
set_source_database_settings source
|
|
59
62
|
set_source_attributes source, offset, true
|
|
63
|
+
set_source_settings source
|
|
60
64
|
set_source_sql source, offset, true
|
|
61
65
|
|
|
62
66
|
source
|
|
@@ -79,7 +83,7 @@ module ThinkingSphinx
|
|
|
79
83
|
end
|
|
80
84
|
|
|
81
85
|
def set_source_database_settings(source)
|
|
82
|
-
config = @
|
|
86
|
+
config = @database_configuration
|
|
83
87
|
|
|
84
88
|
source.sql_host = config[:host] || "localhost"
|
|
85
89
|
source.sql_user = config[:username] || config[:user] || 'root'
|
|
@@ -107,6 +111,7 @@ module ThinkingSphinx
|
|
|
107
111
|
end
|
|
108
112
|
|
|
109
113
|
source.sql_query_pre += [adapter.utf8_query_pre].compact if utf8?
|
|
114
|
+
source.sql_query_pre << adapter.utc_query_pre
|
|
110
115
|
end
|
|
111
116
|
|
|
112
117
|
def set_source_settings(source)
|
|
@@ -136,7 +141,11 @@ module ThinkingSphinx
|
|
|
136
141
|
# attribute associations
|
|
137
142
|
@attributes.collect { |attrib|
|
|
138
143
|
attrib.associations.values if attrib.include_as_association?
|
|
139
|
-
}.compact.flatten
|
|
144
|
+
}.compact.flatten +
|
|
145
|
+
# explicit joins
|
|
146
|
+
@joins.collect { |join|
|
|
147
|
+
join.associations
|
|
148
|
+
}.flatten
|
|
140
149
|
).uniq.collect { |assoc|
|
|
141
150
|
# get ancestors as well as column-level associations
|
|
142
151
|
assoc.ancestors
|
|
@@ -144,7 +153,7 @@ module ThinkingSphinx
|
|
|
144
153
|
end
|
|
145
154
|
|
|
146
155
|
def utf8?
|
|
147
|
-
@index.options[:charset_type]
|
|
156
|
+
@index.options[:charset_type] =~ /utf-8|zh_cn.utf-8/
|
|
148
157
|
end
|
|
149
158
|
end
|
|
150
159
|
end
|