thinking-sphinx 2.0.5 → 2.0.6

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.
Files changed (70) hide show
  1. data/README.textile +7 -1
  2. data/features/searching_by_model.feature +24 -30
  3. data/features/step_definitions/common_steps.rb +5 -5
  4. data/features/thinking_sphinx/db/.gitignore +1 -0
  5. data/features/thinking_sphinx/db/fixtures/post_keywords.txt +1 -0
  6. data/spec/fixtures/data.sql +32 -0
  7. data/spec/fixtures/database.yml.default +3 -0
  8. data/spec/fixtures/models.rb +161 -0
  9. data/spec/fixtures/structure.sql +146 -0
  10. data/spec/spec_helper.rb +62 -0
  11. data/spec/sphinx_helper.rb +61 -0
  12. data/spec/support/rails.rb +18 -0
  13. data/spec/thinking_sphinx/active_record/delta_spec.rb +24 -24
  14. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +27 -0
  15. data/spec/thinking_sphinx/active_record/scopes_spec.rb +25 -25
  16. data/spec/thinking_sphinx/active_record_spec.rb +108 -107
  17. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +38 -38
  18. data/spec/thinking_sphinx/association_spec.rb +69 -35
  19. data/spec/thinking_sphinx/context_spec.rb +61 -64
  20. data/spec/thinking_sphinx/search_spec.rb +7 -0
  21. data/spec/thinking_sphinx_spec.rb +47 -46
  22. metadata +49 -141
  23. data/VERSION +0 -1
  24. data/lib/cucumber/thinking_sphinx/external_world.rb +0 -12
  25. data/lib/cucumber/thinking_sphinx/internal_world.rb +0 -127
  26. data/lib/cucumber/thinking_sphinx/sql_logger.rb +0 -20
  27. data/lib/thinking-sphinx.rb +0 -1
  28. data/lib/thinking_sphinx.rb +0 -301
  29. data/lib/thinking_sphinx/action_controller.rb +0 -31
  30. data/lib/thinking_sphinx/active_record.rb +0 -384
  31. data/lib/thinking_sphinx/active_record/attribute_updates.rb +0 -52
  32. data/lib/thinking_sphinx/active_record/delta.rb +0 -65
  33. data/lib/thinking_sphinx/active_record/has_many_association.rb +0 -36
  34. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +0 -21
  35. data/lib/thinking_sphinx/active_record/log_subscriber.rb +0 -61
  36. data/lib/thinking_sphinx/active_record/scopes.rb +0 -93
  37. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +0 -87
  38. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +0 -62
  39. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +0 -157
  40. data/lib/thinking_sphinx/association.rb +0 -219
  41. data/lib/thinking_sphinx/attribute.rb +0 -396
  42. data/lib/thinking_sphinx/auto_version.rb +0 -38
  43. data/lib/thinking_sphinx/bundled_search.rb +0 -44
  44. data/lib/thinking_sphinx/class_facet.rb +0 -20
  45. data/lib/thinking_sphinx/configuration.rb +0 -339
  46. data/lib/thinking_sphinx/context.rb +0 -76
  47. data/lib/thinking_sphinx/core/string.rb +0 -15
  48. data/lib/thinking_sphinx/deltas.rb +0 -28
  49. data/lib/thinking_sphinx/deltas/default_delta.rb +0 -62
  50. data/lib/thinking_sphinx/deploy/capistrano.rb +0 -101
  51. data/lib/thinking_sphinx/excerpter.rb +0 -23
  52. data/lib/thinking_sphinx/facet.rb +0 -128
  53. data/lib/thinking_sphinx/facet_search.rb +0 -170
  54. data/lib/thinking_sphinx/field.rb +0 -98
  55. data/lib/thinking_sphinx/index.rb +0 -157
  56. data/lib/thinking_sphinx/index/builder.rb +0 -312
  57. data/lib/thinking_sphinx/index/faux_column.rb +0 -118
  58. data/lib/thinking_sphinx/join.rb +0 -37
  59. data/lib/thinking_sphinx/property.rb +0 -185
  60. data/lib/thinking_sphinx/railtie.rb +0 -46
  61. data/lib/thinking_sphinx/search.rb +0 -972
  62. data/lib/thinking_sphinx/search_methods.rb +0 -439
  63. data/lib/thinking_sphinx/sinatra.rb +0 -7
  64. data/lib/thinking_sphinx/source.rb +0 -194
  65. data/lib/thinking_sphinx/source/internal_properties.rb +0 -51
  66. data/lib/thinking_sphinx/source/sql.rb +0 -157
  67. data/lib/thinking_sphinx/tasks.rb +0 -130
  68. data/lib/thinking_sphinx/test.rb +0 -55
  69. data/tasks/distribution.rb +0 -33
  70. data/tasks/testing.rb +0 -80
@@ -1,31 +0,0 @@
1
- module ThinkingSphinx
2
- module ActionController
3
- extend ActiveSupport::Concern
4
-
5
- protected
6
-
7
- attr_internal :query_runtime
8
-
9
- def cleanup_view_runtime
10
- log_subscriber = ThinkingSphinx::ActiveRecord::LogSubscriber
11
- query_runtime_pre_render = log_subscriber.reset_runtime
12
- runtime = super
13
- query_runtime_post_render = log_subscriber.reset_runtime
14
- self.query_runtime = query_runtime_pre_render + query_runtime_post_render
15
- runtime - query_runtime_post_render
16
- end
17
-
18
- def append_info_to_payload(payload)
19
- super
20
- payload[:query_runtime] = query_runtime
21
- end
22
-
23
- module ClassMethods
24
- def log_process_action(payload)
25
- messages, query_runtime = super, payload[:query_runtime]
26
- messages << ("Sphinx: %.1fms" % query_runtime.to_f) if query_runtime
27
- messages
28
- end
29
- end
30
- end
31
- end
@@ -1,384 +0,0 @@
1
- require 'thinking_sphinx/active_record/attribute_updates'
2
- require 'thinking_sphinx/active_record/delta'
3
- require 'thinking_sphinx/active_record/has_many_association'
4
- require 'thinking_sphinx/active_record/log_subscriber'
5
- require 'thinking_sphinx/active_record/has_many_association_with_scopes'
6
- require 'thinking_sphinx/active_record/scopes'
7
-
8
- module ThinkingSphinx
9
- # Core additions to ActiveRecord models - define_index for creating indexes
10
- # for models. If you want to interrogate the index objects created for the
11
- # model, you can use the class-level accessor :sphinx_indexes.
12
- #
13
- module ActiveRecord
14
- def self.included(base)
15
- base.class_eval do
16
- if defined?(class_attribute)
17
- class_attribute :sphinx_indexes, :sphinx_facets
18
- else
19
- class_inheritable_array :sphinx_indexes, :sphinx_facets
20
- end
21
-
22
- extend ThinkingSphinx::ActiveRecord::ClassMethods
23
-
24
- class << self
25
- attr_accessor :sphinx_index_blocks
26
-
27
- def set_sphinx_primary_key(attribute)
28
- @sphinx_primary_key_attribute = attribute
29
- end
30
-
31
- def primary_key_for_sphinx
32
- @primary_key_for_sphinx ||= begin
33
- if custom_primary_key_for_sphinx?
34
- @sphinx_primary_key_attribute ||
35
- superclass.primary_key_for_sphinx
36
- else
37
- primary_key
38
- end
39
- end
40
- end
41
-
42
- def custom_primary_key_for_sphinx?
43
- (
44
- superclass.respond_to?(:custom_primary_key_for_sphinx?) &&
45
- superclass.custom_primary_key_for_sphinx?
46
- ) || !@sphinx_primary_key_attribute.nil?
47
- end
48
-
49
- def clear_primary_key_for_sphinx
50
- @primary_key_for_sphinx = nil
51
- end
52
-
53
- def sphinx_index_options
54
- sphinx_indexes.last.options
55
- end
56
-
57
- # Generate a unique CRC value for the model's name, to use to
58
- # determine which Sphinx documents belong to which AR records.
59
- #
60
- # Really only written for internal use - but hey, if it's useful to
61
- # you in some other way, awesome.
62
- #
63
- def to_crc32
64
- self.name.to_crc32
65
- end
66
-
67
- def to_crc32s
68
- (descendants << self).collect { |klass| klass.to_crc32 }
69
- end
70
-
71
- def sphinx_database_adapter
72
- ThinkingSphinx::AbstractAdapter.detect(self)
73
- end
74
-
75
- def sphinx_name
76
- self.name.underscore.tr(':/\\', '_')
77
- end
78
-
79
- private
80
-
81
- def defined_indexes?
82
- @defined_indexes
83
- end
84
-
85
- def defined_indexes=(value)
86
- @defined_indexes = value
87
- end
88
-
89
- def sphinx_delta?
90
- self.sphinx_indexes.any? { |index| index.delta? }
91
- end
92
- end
93
- end
94
-
95
- ::ActiveRecord::Associations::HasManyAssociation.send(
96
- :include, ThinkingSphinx::ActiveRecord::HasManyAssociation
97
- )
98
- ::ActiveRecord::Associations::HasManyThroughAssociation.send(
99
- :include, ThinkingSphinx::ActiveRecord::HasManyAssociation
100
- )
101
- end
102
-
103
- module ClassMethods
104
- # Allows creation of indexes for Sphinx. If you don't do this, there
105
- # isn't much point trying to search (or using this plugin at all,
106
- # really).
107
- #
108
- # An example or two:
109
- #
110
- # define_index
111
- # indexes :id, :as => :model_id
112
- # indexes name
113
- # end
114
- #
115
- # You can also grab fields from associations - multiple levels deep
116
- # if necessary.
117
- #
118
- # define_index do
119
- # indexes tags.name, :as => :tag
120
- # indexes articles.content
121
- # indexes orders.line_items.product.name, :as => :product
122
- # end
123
- #
124
- # And it will automatically concatenate multiple fields:
125
- #
126
- # define_index do
127
- # indexes [author.first_name, author.last_name], :as => :author
128
- # end
129
- #
130
- # The #indexes method is for fields - if you want attributes, use
131
- # #has instead. All the same rules apply - but keep in mind that
132
- # attributes are for sorting, grouping and filtering, not searching.
133
- #
134
- # define_index do
135
- # # fields ...
136
- #
137
- # has created_at, updated_at
138
- # end
139
- #
140
- # One last feature is the delta index. This requires the model to
141
- # have a boolean field named 'delta', and is enabled as follows:
142
- #
143
- # define_index do
144
- # # fields ...
145
- # # attributes ...
146
- #
147
- # set_property :delta => true
148
- # end
149
- #
150
- # Check out the more detailed documentation for each of these methods
151
- # at ThinkingSphinx::Index::Builder.
152
- #
153
- def define_index(name = nil, &block)
154
- self.sphinx_index_blocks ||= []
155
- self.sphinx_indexes ||= []
156
- self.sphinx_facets ||= []
157
-
158
- ThinkingSphinx.context.add_indexed_model self
159
-
160
- if sphinx_index_blocks.empty?
161
- before_validation :define_indexes
162
- before_destroy :define_indexes
163
- end
164
-
165
- self.sphinx_index_blocks << lambda {
166
- add_sphinx_index name, &block
167
- }
168
-
169
- include ThinkingSphinx::ActiveRecord::Scopes
170
- include ThinkingSphinx::SearchMethods
171
- end
172
-
173
- def define_indexes
174
- superclass.define_indexes unless superclass == ::ActiveRecord::Base
175
-
176
- return if sphinx_index_blocks.nil? ||
177
- defined_indexes? ||
178
- !ThinkingSphinx.define_indexes?
179
-
180
- sphinx_index_blocks.each do |block|
181
- block.call
182
- end
183
-
184
- self.defined_indexes = true
185
-
186
- # We want to make sure that if the database doesn't exist, then Thinking
187
- # Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
188
- # and db:migrate). It's a bit hacky, but I can't think of a better way.
189
- rescue StandardError => err
190
- case err.class.name
191
- when "Mysql::Error", "Java::JavaSql::SQLException", "ActiveRecord::StatementInvalid"
192
- return
193
- else
194
- raise err
195
- end
196
- end
197
-
198
- def add_sphinx_index(name, &block)
199
- index = ThinkingSphinx::Index::Builder.generate self, name, &block
200
-
201
- unless sphinx_indexes.any? { |i| i.name == index.name }
202
- add_sphinx_callbacks_and_extend(index.delta?)
203
- insert_sphinx_index index
204
- end
205
- end
206
-
207
- def insert_sphinx_index(index)
208
- self.sphinx_indexes << index
209
- end
210
-
211
- def has_sphinx_indexes?
212
- sphinx_indexes &&
213
- sphinx_index_blocks &&
214
- (sphinx_indexes.length > 0 || sphinx_index_blocks.length > 0)
215
- end
216
-
217
- def indexed_by_sphinx?
218
- sphinx_indexes && sphinx_indexes.length > 0
219
- end
220
-
221
- def delta_indexed_by_sphinx?
222
- sphinx_indexes && sphinx_indexes.any? { |index| index.delta? }
223
- end
224
-
225
- def sphinx_index_names
226
- define_indexes
227
- sphinx_indexes.collect(&:all_names).flatten
228
- end
229
-
230
- def core_index_names
231
- define_indexes
232
- sphinx_indexes.collect(&:core_name)
233
- end
234
-
235
- def delta_index_names
236
- define_indexes
237
- sphinx_indexes.select(&:delta?).collect(&:delta_name)
238
- end
239
-
240
- def to_riddle
241
- define_indexes
242
- sphinx_database_adapter.setup
243
-
244
- local_sphinx_indexes.collect { |index|
245
- index.to_riddle(sphinx_offset)
246
- }.flatten
247
- end
248
-
249
- def source_of_sphinx_index
250
- define_indexes
251
- possible_models = self.sphinx_indexes.collect { |index| index.model }
252
- return self if possible_models.include?(self)
253
-
254
- parent = self.superclass
255
- while !possible_models.include?(parent) && parent != ::ActiveRecord::Base
256
- parent = parent.superclass
257
- end
258
-
259
- return parent
260
- end
261
-
262
- def delete_in_index(index, document_id)
263
- return unless ThinkingSphinx.sphinx_running? &&
264
- search_for_id(document_id, index)
265
-
266
- ThinkingSphinx::Configuration.instance.client.update(
267
- index, ['sphinx_deleted'], {document_id => [1]}
268
- )
269
- rescue Riddle::ConnectionError, ThinkingSphinx::SphinxError
270
- # Not the end of the world if Sphinx isn't running.
271
- end
272
-
273
- def sphinx_offset
274
- ThinkingSphinx.context.superclass_indexed_models.
275
- index eldest_indexed_ancestor
276
- end
277
-
278
- # Temporarily disable delta indexing inside a block, then perform a
279
- # single rebuild of index at the end.
280
- #
281
- # Useful when performing updates to batches of models to prevent
282
- # the delta index being rebuilt after each individual update.
283
- #
284
- # In the following example, the delta index will only be rebuilt
285
- # once, not 10 times.
286
- #
287
- # SomeModel.suspended_delta do
288
- # 10.times do
289
- # SomeModel.create( ... )
290
- # end
291
- # end
292
- #
293
- def suspended_delta(reindex_after = true, &block)
294
- define_indexes
295
- original_setting = ThinkingSphinx.deltas_suspended?
296
- ThinkingSphinx.deltas_suspended = true
297
- begin
298
- yield
299
- ensure
300
- ThinkingSphinx.deltas_suspended = original_setting
301
- self.index_delta if reindex_after
302
- end
303
- end
304
-
305
- private
306
-
307
- def local_sphinx_indexes
308
- sphinx_indexes.select { |index|
309
- index.model == self
310
- }
311
- end
312
-
313
- def add_sphinx_callbacks_and_extend(delta = false)
314
- unless indexed_by_sphinx?
315
- after_destroy :toggle_deleted
316
-
317
- include ThinkingSphinx::ActiveRecord::AttributeUpdates
318
- end
319
-
320
- if delta && !delta_indexed_by_sphinx?
321
- include ThinkingSphinx::ActiveRecord::Delta
322
-
323
- before_save :toggle_delta
324
- after_commit :index_delta
325
- end
326
- end
327
-
328
- def eldest_indexed_ancestor
329
- ancestors.reverse.detect { |ancestor|
330
- ThinkingSphinx.context.indexed_models.include?(ancestor.name)
331
- }.name
332
- end
333
- end
334
-
335
- attr_accessor :excerpts
336
- attr_accessor :sphinx_attributes
337
- attr_accessor :matching_fields
338
-
339
- def in_index?(index)
340
- self.class.search_for_id self.sphinx_document_id, index
341
- rescue Riddle::ResponseError
342
- true
343
- end
344
-
345
- def toggle_deleted
346
- return unless ThinkingSphinx.updates_enabled?
347
-
348
- self.class.core_index_names.each do |index_name|
349
- self.class.delete_in_index index_name, self.sphinx_document_id
350
- end
351
- self.class.delta_index_names.each do |index_name|
352
- self.class.delete_in_index index_name, self.sphinx_document_id
353
- end if self.class.delta_indexed_by_sphinx? && toggled_delta?
354
-
355
- rescue ::ThinkingSphinx::ConnectionError
356
- # nothing
357
- end
358
-
359
- # Returns the unique integer id for the object. This method uses the
360
- # attribute hash to get around ActiveRecord always mapping the #id method
361
- # to whatever the real primary key is (which may be a unique string hash).
362
- #
363
- # @return [Integer] Unique record id for the purposes of Sphinx.
364
- #
365
- def primary_key_for_sphinx
366
- read_attribute(self.class.primary_key_for_sphinx)
367
- end
368
-
369
- def sphinx_document_id
370
- primary_key_for_sphinx * ThinkingSphinx.context.indexed_models.size +
371
- self.class.sphinx_offset
372
- end
373
-
374
- private
375
-
376
- def sphinx_index_name(suffix)
377
- "#{self.class.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_#{suffix}"
378
- end
379
-
380
- def define_indexes
381
- self.class.define_indexes
382
- end
383
- end
384
- end
@@ -1,52 +0,0 @@
1
- module ThinkingSphinx
2
- module ActiveRecord
3
- module AttributeUpdates
4
- def self.included(base)
5
- base.class_eval do
6
- after_save :update_attribute_values
7
- end
8
- end
9
-
10
- private
11
-
12
- def update_attribute_values
13
- return true unless ThinkingSphinx.updates_enabled? &&
14
- ThinkingSphinx.sphinx_running?
15
-
16
- self.class.sphinx_indexes.each do |index|
17
- attribute_pairs = attribute_values_for_index(index)
18
- attribute_names = attribute_pairs.keys
19
- attribute_values = attribute_names.collect { |key|
20
- attribute_pairs[key]
21
- }
22
-
23
- update_index index.core_name, attribute_names, attribute_values
24
- next unless index.delta?
25
- update_index index.delta_name, attribute_names, attribute_values
26
- end
27
-
28
- true
29
- end
30
-
31
- def updatable_attributes(index)
32
- index.attributes.select { |attrib| attrib.updatable? }
33
- end
34
-
35
- def attribute_values_for_index(index)
36
- updatable_attributes(index).inject({}) { |hash, attrib|
37
- hash[attrib.unique_name.to_s] = attrib.live_value self
38
- hash
39
- }
40
- end
41
-
42
- def update_index(index_name, attribute_names, attribute_values)
43
- config = ThinkingSphinx::Configuration.instance
44
- config.client.update index_name, attribute_names, {
45
- sphinx_document_id => attribute_values
46
- } if in_index?(index_name)
47
- rescue Riddle::ConnectionError, ThinkingSphinx::SphinxError
48
- # Not the end of the world if Sphinx isn't running.
49
- end
50
- end
51
- end
52
- end