module ScopedSearch::QueryBuilder::Field
This module gets included into the Field class to add SQL generation.
Public Instance Methods
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.
# File lib/scoped_search/query_builder.rb 382 def construct_join_sql(key_relation, num) 383 join_sql = "" 384 connection = klass.connection 385 key = key_relation.to_s.singularize.to_sym 386 387 key_table = definition.reflection_by_name(klass, key).table_name 388 value_table = klass.table_name.to_s 389 390 value_table_fk_key, key_table_pk = reflection_keys(definition.reflection_by_name(klass, key)) 391 392 main_reflection = definition.reflection_by_name(definition.klass, relation) 393 if main_reflection 394 main_table = definition.klass.table_name 395 main_table_pk, value_table_fk_main = reflection_keys(definition.reflection_by_name(definition.klass, relation)) 396 397 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})" 398 value_table = " #{value_table}_#{num}" 399 end 400 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}) " 401 402 return join_sql 403 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.
# File lib/scoped_search/query_builder.rb 414 def construct_simple_join_sql(num) 415 connection = klass.connection 416 key_value_table = klass.table_name 417 418 main_table = definition.klass.table_name 419 main_table_pk, value_table_fk_main = reflection_keys(definition.reflection_by_name(definition.klass, relation)) 420 421 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)})" 422 return join_sql 423 end
# File lib/scoped_search/query_builder.rb 432 def reflection_conditions(reflection) 433 return unless reflection 434 conditions = reflection.options[:conditions] 435 conditions ||= "#{reflection.options[:source]}_type = '#{reflection.options[:source_type]}'" if reflection.options[:source] && reflection.options[:source_type] 436 conditions ||= "#{reflection.try(:foreign_type)} = '#{reflection.klass}'" if reflection.options[:polymorphic] 437 " AND #{conditions}" if conditions 438 end
# File lib/scoped_search/query_builder.rb 425 def reflection_keys(reflection) 426 pk = reflection.klass.primary_key 427 fk = reflection.options[:foreign_key] 428 fk ||= reflection.foreign_key 429 reflection.macro == :belongs_to ? [fk, pk] : [pk, fk] 430 end
# File lib/scoped_search/query_builder.rb 440 def to_ext_method_sql(key, operator, value, &block) 441 raise ScopedSearch::QueryNotSupported, "'#{definition.klass}' doesn't respond to '#{ext_method}'" unless definition.klass.respond_to?(ext_method) 442 begin 443 conditions = definition.klass.send(ext_method.to_sym, key, operator, value) 444 rescue StandardError => e 445 raise ScopedSearch::QueryNotSupported, "external method '#{ext_method}' failed with error: #{e}" 446 end 447 raise ScopedSearch::QueryNotSupported, "external method '#{ext_method}' should return hash" unless conditions.kind_of?(Hash) 448 sql = '' 449 conditions.map do |notification, content| 450 case notification 451 when :include then yield(:include, content) 452 when :joins then yield(:joins, content) 453 when :conditions then sql = content 454 when :parameter then content.map{|c| yield(:parameter, c)} 455 end 456 end 457 return sql 458 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.
# File lib/scoped_search/query_builder.rb 352 def to_sql(operator = nil, &block) # :yields: finder_option_type, value 353 num = rand(1000000) 354 connection = klass.connection 355 if key_relation 356 yield(:joins, construct_join_sql(key_relation, num) ) 357 yield(:keycondition, "#{key_klass.table_name}_#{num}.#{connection.quote_column_name(key_field.to_s)} = ?") 358 klass_table_name = relation ? "#{klass.table_name}_#{num}" : klass.table_name 359 return "#{connection.quote_table_name(klass_table_name)}.#{connection.quote_column_name(field.to_s)}" 360 elsif key_field 361 yield(:joins, construct_simple_join_sql(num)) 362 yield(:keycondition, "#{key_klass.table_name}_#{num}.#{connection.quote_column_name(key_field.to_s)} = ?") 363 klass_table_name = relation ? "#{klass.table_name}_#{num}" : klass.table_name 364 return "#{connection.quote_table_name(klass_table_name)}.#{connection.quote_column_name(field.to_s)}" 365 elsif relation 366 yield(:include, relation) 367 end 368 column_name = connection.quote_table_name(klass.table_name.to_s) + "." + connection.quote_column_name(field.to_s) 369 column_name = "(#{column_name} >> #{offset*word_size} & #{2**word_size - 1})" if offset 370 column_name 371 end