Mysql
 sql >> Database >  >> RDS >> Mysql

Creazione di query dinamicamente nei binari

Puoi creare una query SQL basata sul tuo hash. L'approccio più generico è l'SQL grezzo, che può essere eseguito da ActiveRecord .

Ecco qualche codice concettuale che dovrebbe darti l'idea giusta:

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

A cosa serve:

  • query_select specifica quali informazioni desideri nel risultato
  • query_where crea tutte le condizioni di ricerca ed esegue l'escape dell'input per prevenire iniezioni SQL
  • query_tables è un elenco di tutte le tabelle che devi cercare
  • table_name = table.constantize.table_name ti darà il nome_tabella SQL usato dal modello
  • raw_query è la query sql combinata effettiva dalle parti precedenti
  • ActiveRecord::Base.connection.execute(raw_query) esegue sql sul database

Assicurati di mettere tra virgolette l'input inviato dall'utente ed evitarlo correttamente per evitare iniezioni SQL.

Per il tuo esempio la query creata sarà simile a questa:

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

Questo approccio potrebbe richiedere una logica aggiuntiva per unire le tabelle correlate. Nel tuo caso, se company e category hai un'associazione, devi aggiungere qualcosa di simile a query_where

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

Approccio facile: È possibile creare un hash per tutte le coppie di modelli/tabelle che possono essere interrogate e memorizzare lì la condizione di join appropriata. Questo Hash non dovrebbe essere troppo complesso nemmeno per un progetto di medie dimensioni.

Approccio difficile: Questo può essere fatto automaticamente, se hai has_many , has_one e belongs_to correttamente definito nei tuoi modelli. Puoi ottenere le associazioni di un modello utilizzando reflect_on_all_associations . Implementa una Breath-First-Search o Depth-First Search algoritmo e inizia con qualsiasi modello e cerca le associazioni corrispondenti ad altri modelli dal tuo input json. Avvia nuove esecuzioni BFS/DFS finché non sono rimasti modelli non visitati dall'input json. Dalle informazioni trovate, puoi derivare tutte le condizioni di unione e quindi aggiungerle come espressioni nel where clausola dell'approccio sql grezzo come spiegato sopra. Ancora più complesso, ma anche fattibile sarebbe la lettura del database schema e utilizzando un approccio simile, come definito qui, cercando foreign keys .

Utilizzo delle associazioni: Se tutti sono associati a has_many / has_one , puoi gestire i join con ActiveRecord utilizzando i joins metodo con inject sul modello "più significativo" come questo:

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

A cosa serve:

  • passa il base_model come input iniziale a Enumerable.inject , che chiamerà ripetutamente input.send(:joins, :assoc) (per il mio esempio questo farebbe Company.send(:joins, :categories) che equivale a `Company.categories
  • sul join combinato, esegue le condizioni where (costruite come descritto sopra)

Disclaimer L'esatta sintassi di cui hai bisogno potrebbe variare in base all'implementazione SQL che utilizzi.