friendlyfashion-thinking-sphinx 2.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. data/HISTORY +244 -0
  2. data/LICENCE +20 -0
  3. data/README.textile +235 -0
  4. data/features/abstract_inheritance.feature +10 -0
  5. data/features/alternate_primary_key.feature +27 -0
  6. data/features/attribute_transformation.feature +22 -0
  7. data/features/attribute_updates.feature +77 -0
  8. data/features/deleting_instances.feature +67 -0
  9. data/features/direct_attributes.feature +11 -0
  10. data/features/excerpts.feature +21 -0
  11. data/features/extensible_delta_indexing.feature +9 -0
  12. data/features/facets.feature +88 -0
  13. data/features/facets_across_model.feature +29 -0
  14. data/features/field_sorting.feature +18 -0
  15. data/features/handling_edits.feature +94 -0
  16. data/features/retry_stale_indexes.feature +24 -0
  17. data/features/searching_across_models.feature +20 -0
  18. data/features/searching_by_index.feature +40 -0
  19. data/features/searching_by_model.feature +175 -0
  20. data/features/searching_with_find_arguments.feature +56 -0
  21. data/features/sphinx_detection.feature +25 -0
  22. data/features/sphinx_scopes.feature +68 -0
  23. data/features/step_definitions/alpha_steps.rb +16 -0
  24. data/features/step_definitions/beta_steps.rb +7 -0
  25. data/features/step_definitions/common_steps.rb +201 -0
  26. data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
  27. data/features/step_definitions/facet_steps.rb +96 -0
  28. data/features/step_definitions/find_arguments_steps.rb +36 -0
  29. data/features/step_definitions/gamma_steps.rb +15 -0
  30. data/features/step_definitions/scope_steps.rb +19 -0
  31. data/features/step_definitions/search_steps.rb +94 -0
  32. data/features/step_definitions/sphinx_steps.rb +35 -0
  33. data/features/sti_searching.feature +19 -0
  34. data/features/support/env.rb +27 -0
  35. data/features/support/lib/generic_delta_handler.rb +8 -0
  36. data/features/thinking_sphinx/database.example.yml +3 -0
  37. data/features/thinking_sphinx/db/.gitignore +1 -0
  38. data/features/thinking_sphinx/db/fixtures/alphas.rb +8 -0
  39. data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
  40. data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
  41. data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
  42. data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
  43. data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
  44. data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
  45. data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
  46. data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
  47. data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
  48. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  49. data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
  50. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  51. data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
  52. data/features/thinking_sphinx/db/fixtures/post_keywords.txt +1 -0
  53. data/features/thinking_sphinx/db/fixtures/posts.rb +10 -0
  54. data/features/thinking_sphinx/db/fixtures/robots.rb +8 -0
  55. data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
  56. data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
  57. data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
  58. data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
  59. data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
  60. data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
  61. data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
  62. data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
  63. data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
  64. data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
  65. data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
  66. data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
  67. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  68. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  69. data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
  70. data/features/thinking_sphinx/db/migrations/create_posts.rb +6 -0
  71. data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
  72. data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
  73. data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
  74. data/features/thinking_sphinx/models/alpha.rb +23 -0
  75. data/features/thinking_sphinx/models/andrew.rb +17 -0
  76. data/features/thinking_sphinx/models/animal.rb +5 -0
  77. data/features/thinking_sphinx/models/author.rb +3 -0
  78. data/features/thinking_sphinx/models/beta.rb +13 -0
  79. data/features/thinking_sphinx/models/box.rb +8 -0
  80. data/features/thinking_sphinx/models/cat.rb +3 -0
  81. data/features/thinking_sphinx/models/category.rb +4 -0
  82. data/features/thinking_sphinx/models/comment.rb +10 -0
  83. data/features/thinking_sphinx/models/developer.rb +21 -0
  84. data/features/thinking_sphinx/models/dog.rb +3 -0
  85. data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
  86. data/features/thinking_sphinx/models/fox.rb +5 -0
  87. data/features/thinking_sphinx/models/gamma.rb +5 -0
  88. data/features/thinking_sphinx/models/genre.rb +3 -0
  89. data/features/thinking_sphinx/models/medium.rb +5 -0
  90. data/features/thinking_sphinx/models/music.rb +10 -0
  91. data/features/thinking_sphinx/models/person.rb +24 -0
  92. data/features/thinking_sphinx/models/post.rb +22 -0
  93. data/features/thinking_sphinx/models/robot.rb +12 -0
  94. data/features/thinking_sphinx/models/tag.rb +3 -0
  95. data/features/thinking_sphinx/models/tagging.rb +4 -0
  96. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  97. data/lib/cucumber/thinking_sphinx/internal_world.rb +137 -0
  98. data/lib/cucumber/thinking_sphinx/sql_logger.rb +28 -0
  99. data/lib/thinking-sphinx.rb +1 -0
  100. data/lib/thinking_sphinx/action_controller.rb +31 -0
  101. data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
  102. data/lib/thinking_sphinx/active_record/collection_proxy.rb +47 -0
  103. data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
  104. data/lib/thinking_sphinx/active_record/delta.rb +67 -0
  105. data/lib/thinking_sphinx/active_record/has_many_association.rb +44 -0
  106. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
  107. data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
  108. data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
  109. data/lib/thinking_sphinx/active_record.rb +386 -0
  110. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
  111. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
  112. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +188 -0
  113. data/lib/thinking_sphinx/association.rb +230 -0
  114. data/lib/thinking_sphinx/attribute.rb +405 -0
  115. data/lib/thinking_sphinx/auto_version.rb +40 -0
  116. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  117. data/lib/thinking_sphinx/class_facet.rb +20 -0
  118. data/lib/thinking_sphinx/configuration.rb +375 -0
  119. data/lib/thinking_sphinx/context.rb +76 -0
  120. data/lib/thinking_sphinx/core/string.rb +15 -0
  121. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  122. data/lib/thinking_sphinx/deltas.rb +28 -0
  123. data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
  124. data/lib/thinking_sphinx/excerpter.rb +23 -0
  125. data/lib/thinking_sphinx/facet.rb +135 -0
  126. data/lib/thinking_sphinx/facet_search.rb +170 -0
  127. data/lib/thinking_sphinx/field.rb +98 -0
  128. data/lib/thinking_sphinx/index/builder.rb +315 -0
  129. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  130. data/lib/thinking_sphinx/index.rb +159 -0
  131. data/lib/thinking_sphinx/join.rb +37 -0
  132. data/lib/thinking_sphinx/property.rb +187 -0
  133. data/lib/thinking_sphinx/railtie.rb +43 -0
  134. data/lib/thinking_sphinx/search.rb +1061 -0
  135. data/lib/thinking_sphinx/search_methods.rb +439 -0
  136. data/lib/thinking_sphinx/sinatra.rb +7 -0
  137. data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
  138. data/lib/thinking_sphinx/source/sql.rb +174 -0
  139. data/lib/thinking_sphinx/source.rb +194 -0
  140. data/lib/thinking_sphinx/tasks.rb +142 -0
  141. data/lib/thinking_sphinx/test.rb +55 -0
  142. data/lib/thinking_sphinx/version.rb +3 -0
  143. data/lib/thinking_sphinx.rb +297 -0
  144. data/spec/fixtures/data.sql +32 -0
  145. data/spec/fixtures/database.yml.default +3 -0
  146. data/spec/fixtures/models.rb +164 -0
  147. data/spec/fixtures/structure.sql +146 -0
  148. data/spec/spec_helper.rb +61 -0
  149. data/spec/sphinx_helper.rb +60 -0
  150. data/spec/support/rails.rb +25 -0
  151. data/spec/thinking_sphinx/active_record/delta_spec.rb +122 -0
  152. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +173 -0
  153. data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
  154. data/spec/thinking_sphinx/active_record_spec.rb +573 -0
  155. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +145 -0
  156. data/spec/thinking_sphinx/association_spec.rb +250 -0
  157. data/spec/thinking_sphinx/attribute_spec.rb +552 -0
  158. data/spec/thinking_sphinx/auto_version_spec.rb +103 -0
  159. data/spec/thinking_sphinx/configuration_spec.rb +326 -0
  160. data/spec/thinking_sphinx/context_spec.rb +126 -0
  161. data/spec/thinking_sphinx/core/array_spec.rb +9 -0
  162. data/spec/thinking_sphinx/core/string_spec.rb +9 -0
  163. data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
  164. data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
  165. data/spec/thinking_sphinx/facet_spec.rb +359 -0
  166. data/spec/thinking_sphinx/field_spec.rb +127 -0
  167. data/spec/thinking_sphinx/index/builder_spec.rb +532 -0
  168. data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
  169. data/spec/thinking_sphinx/index_spec.rb +189 -0
  170. data/spec/thinking_sphinx/search_methods_spec.rb +156 -0
  171. data/spec/thinking_sphinx/search_spec.rb +1455 -0
  172. data/spec/thinking_sphinx/source_spec.rb +267 -0
  173. data/spec/thinking_sphinx/test_spec.rb +20 -0
  174. data/spec/thinking_sphinx_spec.rb +204 -0
  175. metadata +524 -0
@@ -0,0 +1,405 @@
1
+ module ThinkingSphinx
2
+ # Attributes - eternally useful when it comes to filtering, sorting or
3
+ # grouping. This class isn't really useful to you unless you're hacking
4
+ # around with the internals of Thinking Sphinx - but hey, don't let that
5
+ # stop you.
6
+ #
7
+ # One key thing to remember - if you're using the attribute manually to
8
+ # generate SQL statements, you'll need to set the base model, and all the
9
+ # associations. Which can get messy. Use Index.link!, it really helps.
10
+ #
11
+ class Attribute < ThinkingSphinx::Property
12
+ attr_accessor :query_source
13
+
14
+ SphinxTypeMappings = {
15
+ :multi => :sql_attr_multi,
16
+ :datetime => :sql_attr_timestamp,
17
+ :string => :sql_attr_str2ordinal,
18
+ :float => :sql_attr_float,
19
+ :boolean => :sql_attr_bool,
20
+ :integer => :sql_attr_uint,
21
+ :bigint => :sql_attr_bigint,
22
+ :wordcount => :sql_attr_str2wordcount
23
+ }
24
+
25
+ # To create a new attribute, you'll need to pass in either a single Column
26
+ # or an array of them, and some (optional) options.
27
+ #
28
+ # Valid options are:
29
+ # - :as => :alias_name
30
+ # - :type => :attribute_type
31
+ # - :source => :field, :query, :ranged_query
32
+ #
33
+ # Alias is only required in three circumstances: when there's
34
+ # another attribute or field with the same name, when the column name is
35
+ # 'id', or when there's more than one column.
36
+ #
37
+ # Type is not required, unless you want to force a column to be a certain
38
+ # type (but keep in mind the value will not be CASTed in the SQL
39
+ # statements). The only time you really need to use this is when the type
40
+ # can't be figured out by the column - ie: when not actually using a
41
+ # database column as your source.
42
+ #
43
+ # Source is only used for multi-value attributes (MVA). By default this will
44
+ # use a left-join and a group_concat to obtain the values. For better performance
45
+ # during indexing it can be beneficial to let Sphinx use a separate query to retrieve
46
+ # all document,value-pairs.
47
+ # Either :query or :ranged_query will enable this feature, where :ranged_query will cause
48
+ # the query to be executed incremental.
49
+ #
50
+ # Example usage:
51
+ #
52
+ # Attribute.new(
53
+ # Column.new(:created_at)
54
+ # )
55
+ #
56
+ # Attribute.new(
57
+ # Column.new(:posts, :id),
58
+ # :as => :post_ids
59
+ # )
60
+ #
61
+ # Attribute.new(
62
+ # Column.new(:posts, :id),
63
+ # :as => :post_ids,
64
+ # :source => :ranged_query
65
+ # )
66
+ #
67
+ # Attribute.new(
68
+ # [Column.new(:pages, :id), Column.new(:articles, :id)],
69
+ # :as => :content_ids
70
+ # )
71
+ #
72
+ # Attribute.new(
73
+ # Column.new("NOW()"),
74
+ # :as => :indexed_at,
75
+ # :type => :datetime
76
+ # )
77
+ #
78
+ # If you're creating attributes for latitude and longitude, don't forget
79
+ # that Sphinx expects these values to be in radians.
80
+ #
81
+ def initialize(source, columns, options = {})
82
+ super
83
+
84
+ @type = options[:type]
85
+ @query_source = options[:source]
86
+ @crc = options[:crc]
87
+ @all_ints = options[:all_ints]
88
+
89
+ @type ||= :multi unless @query_source.nil?
90
+ if @type == :string && @crc
91
+ @type = is_many? ? :multi : :integer
92
+ end
93
+
94
+ source.attributes << self
95
+ end
96
+
97
+ # Get the part of the SELECT clause related to this attribute. Don't forget
98
+ # to set your model and associations first though.
99
+ #
100
+ # This will concatenate strings and arrays of integers, and convert
101
+ # datetimes to timestamps, as needed.
102
+ #
103
+ def to_select_sql
104
+ return nil unless include_as_association? && available?
105
+
106
+ separator = all_ints? || all_datetimes? || @crc ? ',' : ' '
107
+
108
+ clause = columns_with_prefixes.collect { |column|
109
+ case type
110
+ when :string
111
+ adapter.convert_nulls(column)
112
+ when :datetime
113
+ adapter.cast_to_datetime(column)
114
+ when :multi
115
+ column = adapter.cast_to_datetime(column) if is_many_datetimes?
116
+ column = adapter.convert_nulls(column, '0') if is_many_ints?
117
+ column
118
+ else
119
+ column
120
+ end
121
+ }.join(', ')
122
+
123
+ clause = adapter.crc(clause) if @crc
124
+ clause = adapter.concatenate(clause, separator) if concat_ws?
125
+ clause = adapter.group_concatenate(clause, separator) if is_many?
126
+ clause = adapter.downcase(clause) if insensitive?
127
+
128
+ "#{clause} AS #{quote_column(unique_name)}"
129
+ end
130
+
131
+ def type_to_config
132
+ SphinxTypeMappings[type]
133
+ end
134
+
135
+ def include_as_association?
136
+ ! (type == :multi && (query_source == :query || query_source == :ranged_query))
137
+ end
138
+
139
+ # Returns the configuration value that should be used for
140
+ # the attribute.
141
+ # Special case is the multi-valued attribute that needs some
142
+ # extra configuration.
143
+ #
144
+ def config_value(offset = nil, delta = false)
145
+ if type == :multi
146
+ multi_config = include_as_association? ? "field" :
147
+ source_value(offset, delta).gsub(/\s+/m, " ").strip
148
+ "uint #{unique_name} from #{multi_config}"
149
+ else
150
+ unique_name
151
+ end
152
+ end
153
+
154
+ # Returns the type of the column. If that's not already set, it returns
155
+ # :multi if there's the possibility of more than one value, :string if
156
+ # there's more than one association, otherwise it figures out what the
157
+ # actual column's datatype is and returns that.
158
+ #
159
+ def type
160
+ @type ||= begin
161
+ base_type = case
162
+ when is_many?, is_many_ints?
163
+ :multi
164
+ when @associations.values.flatten.length > 1
165
+ :string
166
+ else
167
+ translated_type_from_database
168
+ end
169
+
170
+ if base_type == :string && @crc
171
+ base_type = :integer
172
+ else
173
+ @crc = false unless base_type == :multi && is_many_strings? && @crc
174
+ end
175
+
176
+ base_type
177
+ end
178
+ end
179
+
180
+ def updatable?
181
+ [:integer, :datetime, :boolean].include?(type) &&
182
+ unique_name != :sphinx_internal_id &&
183
+ !is_string?
184
+ end
185
+
186
+ def live_value(instance)
187
+ object = instance
188
+ column = @columns.first
189
+ column.__stack.each { |method|
190
+ object = object.send(method)
191
+ return sphinx_value(nil) if object.nil?
192
+ }
193
+
194
+ sphinx_value object.send(column.__name)
195
+ end
196
+
197
+ def all_ints?
198
+ @all_ints || all_of_type?(:integer)
199
+ end
200
+
201
+ def all_datetimes?
202
+ all_of_type?(:datetime, :date, :timestamp)
203
+ end
204
+
205
+ def all_strings?
206
+ all_of_type?(:string, :text)
207
+ end
208
+
209
+ private
210
+
211
+ def source_value(offset, delta)
212
+ if is_string?
213
+ return "#{query_source.to_s.dasherize}; #{columns.first.__name}"
214
+ end
215
+
216
+ query = query(offset)
217
+
218
+ if query_source == :ranged_query
219
+ query += query_clause
220
+ query += " AND #{query_delta.strip}" if delta
221
+ "ranged-query; #{query}; #{range_query}"
222
+ else
223
+ query += " WHERE #{query_delta.strip}" if delta
224
+ "query; #{query}"
225
+ end
226
+ end
227
+
228
+ def query(offset)
229
+ base_assoc = base_association_for_mva
230
+ end_assoc = end_association_for_mva
231
+ raise "Could not determine SQL for MVA" if base_assoc.nil?
232
+
233
+ relation = ::ActiveRecord::Relation.new(
234
+ base_assoc.reflection.klass, Arel::Table.new(base_assoc.table)
235
+ )
236
+
237
+ association_joins.each do |join|
238
+ join.join_type = Arel::OuterJoin
239
+ relation = relation.joins(join)
240
+ end
241
+
242
+ relation = relation.select "#{foreign_key_for_mva base_assoc} #{ThinkingSphinx.unique_id_expression(adapter, offset)} AS #{quote_column('id')}, #{primary_key_for_mva(end_assoc)} AS #{quote_column(unique_name)}"
243
+
244
+ relation.to_sql
245
+ end
246
+
247
+ def query_clause
248
+ foreign_key = foreign_key_for_mva base_association_for_mva
249
+ " WHERE #{foreign_key} >= $start AND #{foreign_key} <= $end"
250
+ end
251
+
252
+ def query_delta
253
+ foreign_key = foreign_key_for_mva base_association_for_mva
254
+ <<-SQL
255
+ #{foreign_key} IN (SELECT #{quote_column model.primary_key}
256
+ FROM #{model.quoted_table_name}
257
+ WHERE #{@source.index.delta_object.clause(model, true)})
258
+ SQL
259
+ end
260
+
261
+ def range_query
262
+ assoc = base_association_for_mva
263
+ foreign_key = foreign_key_for_mva assoc
264
+ "SELECT MIN(#{foreign_key}), MAX(#{foreign_key}) FROM #{quote_table_name assoc.table}"
265
+ end
266
+
267
+ def primary_key_for_mva(assoc)
268
+ quote_with_table(
269
+ assoc.table, assoc.primary_key_from_reflection || columns.first.__name
270
+ )
271
+ end
272
+
273
+ def foreign_key_for_mva(assoc)
274
+ if ThinkingSphinx.rails_3_1?
275
+ if assoc.reflection.through_reflection
276
+ quote_with_table assoc.table, assoc.reflection.through_reflection.foreign_key
277
+ else
278
+ quote_with_table assoc.table, assoc.reflection.foreign_key
279
+ end
280
+ else
281
+ quote_with_table assoc.table, assoc.reflection.primary_key_name
282
+ end
283
+ end
284
+
285
+ def end_association_for_mva
286
+ @association_for_mva ||= associations[columns.first.__stack].detect { |assoc|
287
+ assoc.has_column?(columns.first.__name)
288
+ }
289
+ end
290
+
291
+ def base_association_for_mva
292
+ @first_association_for_mva ||= begin
293
+ assoc = end_association_for_mva
294
+ while !assoc.parent.nil?
295
+ assoc = assoc.parent
296
+ end
297
+
298
+ assoc
299
+ end
300
+ end
301
+
302
+ def association_joins
303
+ joins = []
304
+ assoc = end_association_for_mva
305
+ while assoc != base_association_for_mva
306
+ joins << assoc.join
307
+ assoc = assoc.parent
308
+ end
309
+
310
+ joins
311
+ end
312
+
313
+ def is_many_ints?
314
+ concat_ws? && all_ints?
315
+ end
316
+
317
+ def is_many_datetimes?
318
+ is_many? && all_datetimes?
319
+ end
320
+
321
+ def is_many_strings?
322
+ is_many? && all_strings?
323
+ end
324
+
325
+ def translated_type_from_database
326
+ case type_from_db = type_from_database
327
+ when :integer
328
+ integer_type_from_db
329
+ when :datetime, :string, :float, :boolean
330
+ type_from_db
331
+ when :decimal
332
+ :float
333
+ when :timestamp, :date
334
+ :datetime
335
+ else
336
+ raise <<-MESSAGE
337
+
338
+ Cannot automatically map attribute #{unique_name} in #{@model.name} to an
339
+ equivalent Sphinx type (integer, float, boolean, datetime, string as ordinal).
340
+ You could try to explicitly convert the column's value in your define_index
341
+ block:
342
+ has "CAST(column AS INT)", :type => :integer, :as => :column
343
+ MESSAGE
344
+ end
345
+ end
346
+
347
+ def type_from_database
348
+ column = column_from_db
349
+ column.nil? ? nil : column.type
350
+ end
351
+
352
+ def integer_type_from_db
353
+ column = column_from_db
354
+ return nil if column.nil?
355
+
356
+ case column.sql_type
357
+ when adapter.bigint_pattern
358
+ :bigint
359
+ else
360
+ :integer
361
+ end
362
+ end
363
+
364
+ def column_from_db
365
+ klass = @associations.values.flatten.first ?
366
+ @associations.values.flatten.first.reflection.klass : @model
367
+
368
+ klass.columns.detect { |col|
369
+ @columns.collect { |c| c.__name.to_s }.include? col.name
370
+ }
371
+ end
372
+
373
+ def all_of_type?(*column_types)
374
+ @columns.all? { |col|
375
+ klasses = @associations[col.__stack].empty? ? [@model] :
376
+ @associations[col.__stack].collect { |assoc| assoc.reflection.klass }
377
+ klasses.all? { |klass|
378
+ column = klass.columns.detect { |column| column.name == col.__name.to_s }
379
+ !column.nil? && column_types.include?(column.type)
380
+ }
381
+ }
382
+ end
383
+
384
+ def sphinx_value(value)
385
+ case value
386
+ when TrueClass
387
+ 1
388
+ when FalseClass, NilClass
389
+ 0
390
+ when Time
391
+ value.to_i
392
+ when Date
393
+ value.to_time.to_i
394
+ when String
395
+ value.to_crc32
396
+ else
397
+ value
398
+ end
399
+ end
400
+
401
+ def insensitive?
402
+ @sortable == :insensitive
403
+ end
404
+ end
405
+ end
@@ -0,0 +1,40 @@
1
+ module ThinkingSphinx
2
+ class AutoVersion
3
+ def self.detect
4
+ version = ThinkingSphinx::Configuration.instance.version
5
+ case version
6
+ when '0.9.8', '0.9.9'
7
+ require "riddle/#{version}"
8
+ when /1.10/
9
+ require 'riddle/1.10'
10
+ when /2.0.[12]/
11
+ require 'riddle/2.0.1'
12
+ when /2.0.[^12]/, /2.1.\d/
13
+ require 'riddle/2.1.0'
14
+ else
15
+ documentation_link = %Q{
16
+ For more information, read the documentation:
17
+ http://pat.github.com/ts/en/advanced_config.html
18
+ }
19
+
20
+ if version.nil? || version.empty?
21
+ STDERR.puts %Q{
22
+ Sphinx cannot be found on your system. You may need to configure the following
23
+ settings in your config/sphinx.yml file:
24
+ * bin_path
25
+ * searchd_binary_name
26
+ * indexer_binary_name
27
+
28
+ #{documentation_link}
29
+ }
30
+ else
31
+ STDERR.puts %Q{
32
+ Unsupported version: #{version}
33
+
34
+ #{documentation_link}
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ module ThinkingSphinx
2
+ class BundledSearch
3
+ attr_reader :client
4
+
5
+ def initialize
6
+ @searches = []
7
+ end
8
+
9
+ def search(*args)
10
+ @searches << ThinkingSphinx.search(*args)
11
+ @searches.last.append_to client
12
+ end
13
+
14
+ def search_for_ids(*args)
15
+ @searches << ThinkingSphinx.search_for_ids(*args)
16
+ @searches.last.append_to client
17
+ end
18
+
19
+ def searches
20
+ populate
21
+ @searches
22
+ end
23
+
24
+ private
25
+
26
+ def client
27
+ @client ||= ThinkingSphinx::Configuration.instance.client
28
+ end
29
+
30
+ def populated?
31
+ @populated
32
+ end
33
+
34
+ def populate
35
+ return if populated?
36
+
37
+ @populated = true
38
+
39
+ client.run.each_with_index do |results, index|
40
+ searches[index].populate_from_queue results
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ module ThinkingSphinx
2
+ class ClassFacet < ThinkingSphinx::Facet
3
+ def name
4
+ :class
5
+ end
6
+
7
+ def attribute_name
8
+ Riddle.loaded_version.to_i < 2 ? 'class_crc' : 'sphinx_internal_class'
9
+ end
10
+
11
+ def value(object, attribute_hash)
12
+ if Riddle.loaded_version.to_i < 2
13
+ crc = attribute_hash['class_crc']
14
+ ThinkingSphinx::Configuration.instance.models_by_crc[crc]
15
+ else
16
+ attribute_hash['sphinx_internal_class']
17
+ end
18
+ end
19
+ end
20
+ end