freelancing-god-thinking-sphinx 1.1.12 → 1.1.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,28 +3,25 @@ module ThinkingSphinx
3
3
  attr_accessor :arguments
4
4
 
5
5
  def initialize(arguments)
6
- @arguments = arguments.clone
7
- @attribute_values = {}
8
- @facet_names = []
6
+ @arguments = arguments.clone
7
+ @facet_names = []
9
8
  end
10
9
 
11
10
  def add_from_results(facet, results)
12
11
  name = ThinkingSphinx::Facet.name_for(facet)
13
-
14
- self[name] ||= {}
15
- @attribute_values[name] ||= {}
12
+
13
+ self[name] ||= {}
16
14
  @facet_names << name
17
-
15
+
18
16
  return if results.empty?
19
-
17
+
20
18
  facet = facet_from_object(results.first, facet) if facet.is_a?(String)
21
19
 
22
20
  results.each_with_groupby_and_count { |result, group, count|
23
21
  facet_value = facet.value(result, group)
24
22
 
25
- self[name][facet_value] ||= 0
26
- self[name][facet_value] += count
27
- @attribute_values[name][facet_value] = group
23
+ self[name][facet_value] ||= 0
24
+ self[name][facet_value] += count
28
25
  }
29
26
  end
30
27
 
@@ -34,7 +31,7 @@ module ThinkingSphinx
34
31
  options[:with] ||= {}
35
32
 
36
33
  hash.each do |key, value|
37
- attrib = ThinkingSphinx::Facet.attribute_name_for(key)
34
+ attrib = ThinkingSphinx::Facet.attribute_name_from_value(key, value)
38
35
  options[:with][attrib] = underlying_value key, value
39
36
  end
40
37
 
@@ -48,8 +45,10 @@ module ThinkingSphinx
48
45
  case value
49
46
  when Array
50
47
  value.collect { |item| underlying_value(key, item) }
48
+ when String
49
+ value.to_crc32
51
50
  else
52
- @attribute_values[key][value]
51
+ value
53
52
  end
54
53
  end
55
54
 
@@ -52,12 +52,14 @@ module ThinkingSphinx
52
52
  # :as => :posts, :prefixes => true
53
53
  # )
54
54
  #
55
- def initialize(columns, options = {})
55
+ def initialize(source, columns, options = {})
56
56
  super
57
57
 
58
58
  @sortable = options[:sortable] || false
59
59
  @infixes = options[:infixes] || false
60
60
  @prefixes = options[:prefixes] || false
61
+
62
+ source.fields << self
61
63
  end
62
64
 
63
65
  # Get the part of the SELECT clause related to this field. Don't forget
@@ -9,8 +9,7 @@ module ThinkingSphinx
9
9
  # Enjoy.
10
10
  #
11
11
  class Index
12
- attr_accessor :model, :fields, :attributes, :conditions, :groupings,
13
- :delta_object, :options
12
+ attr_accessor :model, :sources, :delta_object
14
13
 
15
14
  # Create a new index instance by passing in the model it is tied to, and
16
15
  # a block to build it with (optional but recommended). For documentation
@@ -28,153 +27,11 @@ module ThinkingSphinx
28
27
  #
29
28
  def initialize(model, &block)
30
29
  @model = model
31
- @associations = {}
32
- @fields = []
33
- @attributes = []
34
- @conditions = []
35
- @groupings = []
30
+ @sources = []
36
31
  @options = {}
37
32
  @delta_object = nil
38
33
 
39
- initialize_from_builder(&block) if block_given?
40
-
41
- add_internal_attributes_and_facets
42
- end
43
-
44
- def name
45
- self.class.name(@model)
46
- end
47
-
48
- def self.name(model)
49
- model.name.underscore.tr(':/\\', '_')
50
- end
51
-
52
- def to_riddle_for_core(offset, index)
53
- link!
54
-
55
- source = Riddle::Configuration::SQLSource.new(
56
- "#{name}_core_#{index}", adapter.sphinx_identifier
57
- )
58
-
59
- set_source_database_settings source
60
- set_source_attributes source, offset
61
- set_source_sql source, offset
62
- set_source_settings source
63
-
64
- source
65
- end
66
-
67
- def to_riddle_for_delta(offset, index)
68
- link!
69
-
70
- source = Riddle::Configuration::SQLSource.new(
71
- "#{name}_delta_#{index}", adapter.sphinx_identifier
72
- )
73
- source.parent = "#{name}_core_#{index}"
74
-
75
- set_source_database_settings source
76
- set_source_attributes source, offset
77
- set_source_sql source, offset, true
78
-
79
- source
80
- end
81
-
82
- # Link all the fields and associations to their corresponding
83
- # associations and joins. This _must_ be called before interrogating
84
- # the index's fields and associations for anything that may reference
85
- # their SQL structure.
86
- #
87
- def link!
88
- base = ::ActiveRecord::Associations::ClassMethods::JoinDependency.new(
89
- @model, [], nil
90
- )
91
-
92
- @fields.each { |field|
93
- field.model ||= @model
94
- field.columns.each { |col|
95
- field.associations[col] = associations(col.__stack.clone)
96
- field.associations[col].each { |assoc| assoc.join_to(base) }
97
- }
98
- }
99
-
100
- @attributes.each { |attribute|
101
- attribute.model ||= @model
102
- attribute.columns.each { |col|
103
- attribute.associations[col] = associations(col.__stack.clone)
104
- attribute.associations[col].each { |assoc| assoc.join_to(base) }
105
- }
106
- }
107
- end
108
-
109
- # Flag to indicate whether this index has a corresponding delta index.
110
- #
111
- def delta?
112
- !@delta_object.nil?
113
- end
114
-
115
- def adapter
116
- @adapter ||= @model.sphinx_database_adapter
117
- end
118
-
119
- def prefix_fields
120
- @fields.select { |field| field.prefixes }
121
- end
122
-
123
- def infix_fields
124
- @fields.select { |field| field.infixes }
125
- end
126
-
127
- def index_options
128
- all_index_options = ThinkingSphinx::Configuration.instance.index_options.clone
129
- @options.keys.select { |key|
130
- ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s)
131
- }.each { |key| all_index_options[key.to_sym] = @options[key] }
132
- all_index_options
133
- end
134
-
135
- def quote_column(column)
136
- @model.connection.quote_column_name(column)
137
- end
138
-
139
- private
140
-
141
- def utf8?
142
- self.index_options[:charset_type] == "utf-8"
143
- end
144
-
145
- # Does all the magic with the block provided to the base #initialize.
146
- # Creates a new class subclassed from Builder, and evaluates the block
147
- # on it, then pulls all relevant settings - fields, attributes, conditions,
148
- # properties - into the new index.
149
- #
150
- # Also creates a CRC attribute for the model.
151
- #
152
- def initialize_from_builder(&block)
153
- builder = Class.new(Builder)
154
- builder.setup
155
-
156
- builder.instance_eval &block
157
-
158
- unless @model.descends_from_active_record?
159
- stored_class = @model.store_full_sti_class ? @model.name : @model.name.demodulize
160
- builder.where("#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)} = '#{stored_class}'")
161
- end
162
-
163
- set_model = Proc.new { |item| item.model = @model }
164
-
165
- @fields = builder.fields &set_model
166
- @attributes = builder.attributes.each &set_model
167
- @conditions = builder.conditions
168
- @groupings = builder.groupings
169
- @delta_object = ThinkingSphinx::Deltas.parse self, builder.properties
170
- @options = builder.properties
171
-
172
- is_faceted = Proc.new { |item| item.faceted }
173
- add_facet = Proc.new { |item| @model.sphinx_facets << item.to_facet }
174
-
175
- @model.sphinx_facets ||= []
176
- @fields.select( &is_faceted).each &add_facet
177
- @attributes.select(&is_faceted).each &add_facet
34
+ # add_internal_attributes_and_facets
178
35
 
179
36
  # We want to make sure that if the database doesn't exist, then Thinking
180
37
  # Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
@@ -188,237 +45,67 @@ module ThinkingSphinx
188
45
  end
189
46
  end
190
47
 
191
- # Returns all associations used amongst all the fields and attributes.
192
- # This includes all associations between the model and what the actual
193
- # columns are from.
194
- #
195
- def all_associations
196
- @all_associations ||= (
197
- # field associations
198
- @fields.collect { |field|
199
- field.associations.values
200
- }.flatten +
201
- # attribute associations
202
- @attributes.collect { |attrib|
203
- attrib.associations.values if attrib.include_as_association?
204
- }.compact.flatten
205
- ).uniq.collect { |assoc|
206
- # get ancestors as well as column-level associations
207
- assoc.ancestors
208
- }.flatten.uniq
48
+ def fields
49
+ @sources.collect { |source| source.fields }.flatten
209
50
  end
210
51
 
211
- # Gets a stack of associations for a specific path.
212
- #
213
- def associations(path, parent = nil)
214
- assocs = []
215
-
216
- if parent.nil?
217
- assocs = association(path.shift)
218
- else
219
- assocs = parent.children(path.shift)
220
- end
221
-
222
- until path.empty?
223
- point = path.shift
224
- assocs = assocs.collect { |assoc|
225
- assoc.children(point)
226
- }.flatten
227
- end
228
-
229
- assocs
52
+ def attributes
53
+ @sources.collect { |source| source.attributes }.flatten
230
54
  end
231
55
 
232
- # Gets the association stack for a specific key.
233
- #
234
- def association(key)
235
- @associations[key] ||= Association.children(@model, key)
236
- end
237
-
238
- def crc_column
239
- if @model.column_names.include?(@model.inheritance_column)
240
- adapter.cast_to_unsigned(adapter.convert_nulls(
241
- adapter.crc(adapter.quote_with_table(@model.inheritance_column), true),
242
- @model.to_crc32
243
- ))
244
- else
245
- @model.to_crc32.to_s
246
- end
56
+ def name
57
+ self.class.name(@model)
247
58
  end
248
59
 
249
- def add_internal_attributes_and_facets
250
- add_internal_attribute :sphinx_internal_id, :integer, @model.primary_key.to_sym
251
- add_internal_attribute :class_crc, :integer, crc_column, true
252
- add_internal_attribute :subclass_crcs, :multi, subclasses_to_s
253
- add_internal_attribute :sphinx_deleted, :integer, "0"
254
-
255
- add_internal_facet :class_crc
60
+ def self.name(model)
61
+ model.name.underscore.tr(':/\\', '_')
256
62
  end
257
63
 
258
- def add_internal_attribute(name, type, contents, facet = false)
259
- return unless attribute_by_alias(name).nil?
260
-
261
- @attributes << Attribute.new(
262
- FauxColumn.new(contents),
263
- :type => type,
264
- :as => name,
265
- :facet => facet,
266
- :admin => true
267
- )
64
+ def prefix_fields
65
+ fields.select { |field| field.prefixes }
268
66
  end
269
67
 
270
- def add_internal_facet(name)
271
- return unless facet_by_alias(name).nil?
272
-
273
- @model.sphinx_facets << ClassFacet.new(attribute_by_alias(name))
68
+ def infix_fields
69
+ fields.select { |field| field.infixes }
274
70
  end
275
71
 
276
- def attribute_by_alias(attr_alias)
277
- @attributes.detect { |attrib| attrib.alias == attr_alias }
72
+ def local_options
73
+ @options
278
74
  end
279
75
 
280
- def facet_by_alias(name)
281
- @model.sphinx_facets.detect { |facet| facet.name == name }
76
+ def options
77
+ all_index_options = ThinkingSphinx::Configuration.instance.index_options.clone
78
+ @options.keys.select { |key|
79
+ ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s)
80
+ }.each { |key| all_index_options[key.to_sym] = @options[key] }
81
+ all_index_options
282
82
  end
283
83
 
284
- def subclasses_to_s
285
- "'" + (@model.send(:subclasses).collect { |klass|
286
- klass.to_crc32.to_s
287
- } << @model.to_crc32.to_s).join(",") + "'"
288
- end
289
-
290
- def set_source_database_settings(source)
291
- config = @model.connection.instance_variable_get(:@config)
292
-
293
- source.sql_host = config[:host] || "localhost"
294
- source.sql_user = config[:username] || config[:user] || ""
295
- source.sql_pass = (config[:password].to_s || "").gsub('#', '\#')
296
- source.sql_db = config[:database]
297
- source.sql_port = config[:port]
298
- source.sql_sock = config[:socket]
84
+ def delta?
85
+ !@delta_object.nil?
299
86
  end
300
87
 
301
- def set_source_attributes(source, offset = nil)
302
- attributes.each do |attrib|
303
- source.send(attrib.type_to_config) << attrib.config_value(offset)
304
- end
305
- end
88
+ private
306
89
 
307
- def set_source_sql(source, offset, delta = false)
308
- source.sql_query = to_sql(:offset => offset, :delta => delta).gsub(/\n/, ' ')
309
- source.sql_query_range = to_sql_query_range(:delta => delta)
310
- source.sql_query_info = to_sql_query_info(offset)
311
-
312
- source.sql_query_pre += send(!delta ? :sql_query_pre_for_core : :sql_query_pre_for_delta)
313
-
314
- if @options[:group_concat_max_len]
315
- source.sql_query_pre << "SET SESSION group_concat_max_len = #{@options[:group_concat_max_len]}"
316
- end
317
-
318
- source.sql_query_pre += [adapter.utf8_query_pre].compact if utf8?
90
+ def adapter
91
+ @adapter ||= @model.sphinx_database_adapter
319
92
  end
320
93
 
321
- def set_source_settings(source)
322
- ThinkingSphinx::Configuration.instance.source_options.each do |key, value|
323
- source.send("#{key}=".to_sym, value)
324
- end
325
-
326
- @options.each do |key, value|
327
- source.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::SourceOptions.include?(key.to_s) && !value.nil?
328
- end
94
+ def utf8?
95
+ options[:charset_type] == "utf-8"
329
96
  end
330
97
 
331
- def sql_query_pre_for_core
332
- if self.delta? && !@delta_object.reset_query(@model).blank?
333
- [@delta_object.reset_query(@model)]
334
- else
335
- []
336
- end
98
+ # Does all the magic with the block provided to the base #initialize.
99
+ # Creates a new class subclassed from Builder, and evaluates the block
100
+ # on it, then pulls all relevant settings - fields, attributes, conditions,
101
+ # properties - into the new index.
102
+ #
103
+ def initialize_from_builder(&block)
104
+ #
337
105
  end
338
106
 
339
107
  def sql_query_pre_for_delta
340
108
  [""]
341
109
  end
342
-
343
- # Generates the big SQL statement to get the data back for all the fields
344
- # and attributes, using all the relevant association joins. If you want
345
- # the version filtered for delta values, send through :delta => true in the
346
- # options. Won't do much though if the index isn't set up to support a
347
- # delta sibling.
348
- #
349
- # Examples:
350
- #
351
- # index.to_sql
352
- # index.to_sql(:delta => true)
353
- #
354
- def to_sql(options={})
355
- assocs = all_associations
356
-
357
- where_clause = ""
358
- if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
359
- where_clause << " AND #{@delta_object.clause(@model, options[:delta])}"
360
- end
361
- unless @conditions.empty?
362
- where_clause << " AND " << @conditions.join(" AND ")
363
- end
364
-
365
- internal_groupings = []
366
- if @model.column_names.include?(@model.inheritance_column)
367
- internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}"
368
- end
369
-
370
- unique_id_expr = ThinkingSphinx.unique_id_expression(options[:offset])
371
-
372
- sql = <<-SQL
373
- SELECT #{ (
374
- ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] +
375
- @fields.collect { |field| field.to_select_sql } +
376
- @attributes.collect { |attribute| attribute.to_select_sql }
377
- ).compact.join(", ") }
378
- FROM #{ @model.table_name }
379
- #{ assocs.collect { |assoc| assoc.to_sql }.join(' ') }
380
- WHERE #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} >= $start
381
- AND #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} <= $end
382
- #{ where_clause }
383
- GROUP BY #{ (
384
- ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] +
385
- @fields.collect { |field| field.to_group_sql }.compact +
386
- @attributes.collect { |attribute| attribute.to_group_sql }.compact +
387
- @groupings + internal_groupings
388
- ).join(", ") }
389
- SQL
390
-
391
- sql += " ORDER BY NULL" if adapter.sphinx_identifier == "mysql"
392
- sql
393
- end
394
-
395
- # Simple helper method for the query info SQL - which is a statement that
396
- # returns the single row for a corresponding id.
397
- #
398
- def to_sql_query_info(offset)
399
- "SELECT * FROM #{@model.quoted_table_name} WHERE " +
400
- " #{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
401
- end
402
-
403
- # Simple helper method for the query range SQL - which is a statement that
404
- # returns minimum and maximum id values. These can be filtered by delta -
405
- # so pass in :delta => true to get the delta version of the SQL.
406
- #
407
- def to_sql_query_range(options={})
408
- min_statement = adapter.convert_nulls(
409
- "MIN(#{quote_column(@model.primary_key)})", 1
410
- )
411
- max_statement = adapter.convert_nulls(
412
- "MAX(#{quote_column(@model.primary_key)})", 1
413
- )
414
-
415
- sql = "SELECT #{min_statement}, #{max_statement} " +
416
- "FROM #{@model.quoted_table_name} "
417
- if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
418
- sql << "WHERE #{@delta_object.clause(@model, options[:delta])}"
419
- end
420
-
421
- sql
422
- end
423
110
  end
424
111
  end