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 resultadoquery_where
cria todas as condições de pesquisa e escapa da entrada para evitar injeções de SQLquery_tables
é uma lista de todas as tabelas que você precisa pesquisartable_name = table.constantize.table_name
lhe dará o nome_da_tabela SQL conforme usado pelo modeloraw_query
é a consulta sql combinada real das partes acimaActiveRecord::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.