sql >> Base de Datos >  >> RDS >> Mysql

Construyendo consultas dinámicamente en Rails

Puede crear una consulta SQL basada en su hash. El enfoque más genérico es SQL sin procesar, que puede ser ejecutado por ActiveRecord .

Aquí hay un código de concepto que debería darle la idea correcta:

query_select = "select * from "
query_where = ""
tables = [] # for selecting from all tables
hash.each do |table, values|
  table_name = table.constantize.table_name
  tables << table_name
  values.each do |q|
    query_where += " AND " unless query_string.empty?
    query_where += "'#{ActiveRecord::Base.connection.quote(table_name)}'."
    query_where += "'#{ActiveRecord::Base.connection.quote(q[fieldName)}'"
    if q[:operator] == "starts with" # this should be done with an appropriate method
      query_where += " LIKE '#{ActiveRecord::Base.connection.quote(q[val)}%'"
    end
  end
end
query_tables = tables.join(", ")
raw_query = query_select + query_tables + " where " + query_where 
result = ActiveRecord::Base.connection.execute(raw_query)
result.to_h # not required, but raw results are probably easier to handle as a hash

Qué hace esto:

  • query_select especifica qué información desea en el resultado
  • query_where crea todas las condiciones de búsqueda y escapa de la entrada para evitar inyecciones de SQL
  • query_tables es una lista de todas las tablas que necesita buscar
  • table_name = table.constantize.table_name le dará el nombre de la tabla SQL tal como lo usa el modelo
  • raw_query es la consulta sql combinada real de las partes anteriores
  • ActiveRecord::Base.connection.execute(raw_query) ejecuta el sql en la base de datos

Asegúrese de poner cualquier entrada enviada por el usuario entre comillas y escapar correctamente para evitar inyecciones de SQL.

Para su ejemplo, la consulta creada se verá así:

select * from companies, categories where 'companies'.'name' LIKE 'a%' AND 'companies'.'hq_city' = 'karachi' AND 'categories'.'name' NOT LIKE '%ECommerce%'

Este enfoque podría necesitar lógica adicional para unir tablas que están relacionadas. En su caso, si company y category tiene una asociación, debe agregar algo como esto a query_where

"AND 'company'.'category_id' = 'categories'.'id'"

Enfoque fácil: Puede crear un Hash para todos los pares de modelos/tablas que se pueden consultar y almacenar allí la condición de combinación adecuada. Este Hash no debería ser demasiado complejo incluso para un proyecto de tamaño mediano.

Enfoque difícil: Esto se puede hacer automáticamente, si tiene has_many , has_one y belongs_to definido correctamente en sus modelos. Puede obtener las asociaciones de un modelo usando reflect_on_all_associations . Implementar un Breath-First-Search o Depth-First Search algoritmo y comience con cualquier modelo y busque asociaciones coincidentes con otros modelos desde su entrada json. Inicie nuevas ejecuciones de BFS/DFS hasta que no queden modelos no visitados de la entrada json. A partir de la información encontrada, puede derivar todas las condiciones de unión y luego agregarlas como expresiones en el where cláusula del enfoque sql sin procesar como se explicó anteriormente. Aún más complejo, pero también factible, sería leer la base de datos schema y usando un enfoque similar al definido aquí buscando foreign keys .

Uso de asociaciones: Si todos ellos están asociados con has_many / has_one , puede manejar las uniones con ActiveRecord usando las joins método con inject en el modelo "más significativo" como este:

base_model = "Company".constantize
assocations = [:categories]  # and so on
result = assocations.inject(base_model) { |model, assoc| model.joins(assoc) }.where(query_where)

Qué hace esto:

  • pasa el modelo base como entrada inicial a Enumerable.inyectar , que llamará repetidamente a input.send(:joins, :assoc) (para mi ejemplo, esto haría Company.send(:joins, :categories) que es equivalente a `Company.categories
  • en la unión combinada, ejecuta las condiciones where (construidas como se describe arriba)

Descargo de responsabilidad La sintaxis exacta que necesita puede variar según la implementación de SQL que utilice.