Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Construindo consultas dinamicamente no Rails


Você pode criar uma consulta SQL com base em seu hash. A abordagem mais genérica é o SQL bruto, que pode ser executado pelo ActiveRecord .

Aqui está algum código de conceito que deve lhe dar a ideia certa:
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

O que isso faz:
  • query_select especifica quais informações você deseja no resultado
  • query_where cria todas as condições de pesquisa e escapa da entrada para evitar injeções de SQL
  • query_tables é uma lista de todas as tabelas que você precisa pesquisar
  • table_name = table.constantize.table_name lhe dará o nome_da_tabela SQL conforme usado pelo modelo
  • raw_query é a consulta sql combinada real das partes acima
  • ActiveRecord::Base.connection.execute(raw_query) executa o sql no banco de dados

Certifique-se de colocar qualquer entrada enviada pelo usuário entre aspas e escape-a corretamente para evitar injeções de SQL.

Para o seu exemplo, a consulta criada ficará assim:
select * from companies, categories where 'companies'.'name' LIKE 'a%' AND 'companies'.'hq_city' = 'karachi' AND 'categories'.'name' NOT LIKE '%ECommerce%'

Essa abordagem pode precisar de lógica adicional para unir tabelas relacionadas.No seu caso, se company e category tiver uma associação, você deve adicionar algo assim ao query_where
"AND 'company'.'category_id' = 'categories'.'id'"

Abordagem fácil: Você pode criar um Hash para todos os pares de modelos/tabelas que podem ser consultados e armazenar a condição de junção apropriada lá. Este Hash não deve ser muito complexo, mesmo para um projeto de médio porte.

Abordagem difícil: Isso pode ser feito automaticamente, se você tiver has_many , has_one e belongs_to definido corretamente em seus modelos. Você pode obter as associações de um modelo usando reflect_on_all_associations . Implemente uma Breath-First-Search ou Depth-First Search algoritmo e comece com qualquer modelo e procure associações correspondentes a outros modelos de sua entrada json. Inicie novas execuções de BFS/DFS até que não haja mais modelos não visitados da entrada json. A partir das informações encontradas, você pode derivar todas as condições de junção e adicioná-las como expressões no where cláusula da abordagem sql bruto, conforme explicado acima. Ainda mais complexo, mas também factível, seria ler o banco de dados schema e usando uma abordagem semelhante, conforme definido aqui, procurando por foreign keys .

Usando associações: Se todos eles estiverem associados a has_many / has_one , você pode manipular as junções com ActiveRecord usando as joins método com inject no modelo "mais 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)

O que isso faz:
  • ele passa o modelo_base como entrada inicial para Enumerable.inject , que chamará repetidamente input.send(:joins, :assoc) (no meu exemplo, isso faria Company.send(:joins, :categories) que é equivalente a `Empresa.categorias
  • na junção combinada, ele executa as condições where (construídas conforme descrito acima)

Isenção de responsabilidade A sintaxe exata que você precisa pode variar com base na implementação SQL que você usa.