module ScopedSearch::QueryBuilder::Field
This module gets included into the Field
class to add SQL generation.
Public Instance Methods
Source
# File lib/scoped_search/query_builder.rb 383 def construct_join_sql(key_relation, num) 384 join_sql = "" 385 connection = klass.connection 386 key = key_relation.to_s.singularize.to_sym 387 388 key_table = definition.reflection_by_name(klass, key).table_name 389 value_table = klass.table_name.to_s 390 391 value_table_fk_key, key_table_pk = reflection_keys(definition.reflection_by_name(klass, key)) 392 393 main_reflection = definition.reflection_by_name(definition.klass, relation) 394 if main_reflection 395 main_table = definition.klass.table_name 396 main_table_pk, value_table_fk_main = reflection_keys(definition.reflection_by_name(definition.klass, relation)) 397 398 join_sql = "\n INNER JOIN #{connection.quote_table_name(value_table)} #{value_table}_#{num} ON (#{main_table}.#{main_table_pk} = #{value_table}_#{num}.#{value_table_fk_main})" 399 value_table = " #{value_table}_#{num}" 400 end 401 join_sql += "\n INNER JOIN #{connection.quote_table_name(key_table)} #{key_table}_#{num} ON (#{key_table}_#{num}.#{key_table_pk} = #{value_table}.#{value_table_fk_key}) " 402 403 return join_sql 404 end
This method construct join statement for a key value table It assume the following table structure
+----------+ +---------+ +--------+ | main | | value | | key | | main_pk | | main_fk | | | | | | key_fk | | key_pk | +----------+ +---------+ +--------+
uniq name for the joins are needed in case that there is more than one condition on different keys in the same query.
Source
# File lib/scoped_search/query_builder.rb 415 def construct_simple_join_sql(num) 416 connection = klass.connection 417 key_value_table = klass.table_name 418 419 main_table = definition.klass.table_name 420 main_table_pk, value_table_fk_main = reflection_keys(definition.reflection_by_name(definition.klass, relation)) 421 422 join_sql = "\n INNER JOIN #{connection.quote_table_name(key_value_table)} #{key_value_table}_#{num} ON (#{connection.quote_table_name(main_table)}.#{connection.quote_column_name(main_table_pk)} = #{key_value_table}_#{num}.#{connection.quote_column_name(value_table_fk_main)})" 423 return join_sql 424 end
This method construct join statement for a key value table It assume the following table structure
+----------+ +---------+ | main | | key | | main_pk | | value | | | | main_fk | +----------+ +---------+
uniq name for the joins are needed in case that there is more than one condition on different keys in the same query.
Source
# File lib/scoped_search/query_builder.rb 434 def reflection_conditions(reflection) 435 return unless reflection 436 conditions = reflection.options[:conditions] 437 conditions ||= "#{reflection.options[:source]}_type = '#{reflection.options[:source_type]}'" if reflection.options[:source] && reflection.options[:source_type] 438 conditions ||= "#{reflection.try(:foreign_type)} = '#{reflection.klass}'" if reflection.options[:polymorphic] 439 " AND #{conditions}" if conditions 440 end
Source
# File lib/scoped_search/query_builder.rb 426 def reflection_keys(reflection) 427 pk = reflection.klass.primary_key 428 fk = reflection.options[:foreign_key] 429 # activerecord prior to 3.1 doesn't respond to foreign_key method and hold the key name in the reflection primary key 430 fk ||= reflection.respond_to?(:foreign_key) ? reflection.foreign_key : reflection.primary_key_name 431 reflection.macro == :belongs_to ? [fk, pk] : [pk, fk] 432 end
Source
# File lib/scoped_search/query_builder.rb 442 def to_ext_method_sql(key, operator, value, &block) 443 raise ScopedSearch::QueryNotSupported, "'#{definition.klass}' doesn't respond to '#{ext_method}'" unless definition.klass.respond_to?(ext_method) 444 begin 445 conditions = definition.klass.send(ext_method.to_sym, key, operator, value) 446 rescue StandardError => e 447 raise ScopedSearch::QueryNotSupported, "external method '#{ext_method}' failed with error: #{e}" 448 end 449 raise ScopedSearch::QueryNotSupported, "external method '#{ext_method}' should return hash" unless conditions.kind_of?(Hash) 450 sql = '' 451 conditions.map do |notification, content| 452 case notification 453 when :include then yield(:include, content) 454 when :joins then yield(:joins, content) 455 when :conditions then sql = content 456 when :parameter then content.map{|c| yield(:parameter, c)} 457 end 458 end 459 return sql 460 end
Source
# File lib/scoped_search/query_builder.rb 353 def to_sql(operator = nil, &block) # :yields: finder_option_type, value 354 num = rand(1000000) 355 connection = klass.connection 356 if key_relation 357 yield(:joins, construct_join_sql(key_relation, num) ) 358 yield(:keycondition, "#{key_klass.table_name}_#{num}.#{connection.quote_column_name(key_field.to_s)} = ?") 359 klass_table_name = relation ? "#{klass.table_name}_#{num}" : klass.table_name 360 return "#{connection.quote_table_name(klass_table_name)}.#{connection.quote_column_name(field.to_s)}" 361 elsif key_field 362 yield(:joins, construct_simple_join_sql(num)) 363 yield(:keycondition, "#{key_klass.table_name}_#{num}.#{connection.quote_column_name(key_field.to_s)} = ?") 364 klass_table_name = relation ? "#{klass.table_name}_#{num}" : klass.table_name 365 return "#{connection.quote_table_name(klass_table_name)}.#{connection.quote_column_name(field.to_s)}" 366 elsif relation 367 yield(:include, relation) 368 end 369 column_name = connection.quote_table_name(klass.table_name.to_s) + "." + connection.quote_column_name(field.to_s) 370 column_name = "(#{column_name} >> #{offset*word_size} & #{2**word_size - 1})" if offset 371 column_name 372 end
Return an SQL representation for this field. Also make sure that the relation which includes the search field is included in the SQL query.
This function may yield an :include that should be used in the ActiveRecord::Base#find call, to make sure that the field is available for the SQL query.