Mysql
 sql >> Base de données >  >> RDS >> Mysql

Construire dynamiquement des requêtes dans les rails

Vous pouvez créer une requête SQL basée sur votre hachage. L'approche la plus générique est le SQL brut, qui peut être exécuté par ActiveRecord .

Voici un code conceptuel qui devrait vous donner la bonne idée :

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'est-ce que cela fait :

  • query_select spécifie quelles informations vous voulez dans le résultat
  • query_where construit toutes les conditions de recherche et échappe les entrées pour empêcher les injections SQL
  • query_tables est une liste de toutes les tables que vous devez rechercher
  • table_name = table.constantize.table_name vous donnera le nom_table SQL tel qu'utilisé par le modèle
  • raw_query est la requête SQL combinée réelle des parties ci-dessus
  • ActiveRecord::Base.connection.execute(raw_query) exécute le sql sur la base de données

Assurez-vous de placer toute entrée soumise par l'utilisateur entre guillemets et de l'échapper correctement pour éviter les injections SQL.

Pour votre exemple, la requête créée ressemblera à ceci :

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

Cette approche peut nécessiter une logique supplémentaire pour joindre des tables liées. Dans votre cas, si company et category avoir une association, vous devez ajouter quelque chose comme ceci au query_where

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

Approche simple : Vous pouvez créer un hachage pour toutes les paires de modèles/tables pouvant être interrogées et y stocker la condition de jointure appropriée. Ce hachage ne devrait pas être trop complexe, même pour un projet de taille moyenne.

Approche dure : Cela peut être fait automatiquement, si vous avez has_many , has_one et belongs_to correctement définis dans vos modèles. Vous pouvez obtenir les associations d'un modèle en utilisant reflect_on_all_associations . Implémenter une Breath-First-Search ou Depth-First Search algorithm et commencez par n'importe quel modèle et recherchez les associations correspondantes avec d'autres modèles à partir de votre entrée json. Démarrez de nouvelles exécutions BFS/DFS jusqu'à ce qu'il ne reste plus de modèles non visités à partir de l'entrée json. À partir des informations trouvées, vous pouvez dériver toutes les conditions de jointure, puis les ajouter en tant qu'expressions dans le where clause de l'approche SQL brute comme expliqué ci-dessus. Encore plus complexe, mais aussi faisable serait de lire la base de données schema et en utilisant une approche similaire à celle définie ici en recherchant des foreign keys .

Utiliser des associations : Si tous sont associés à has_many / has_one , vous pouvez gérer les jointures avec ActiveRecord en utilisant les joins méthode avec inject sur le modèle "le plus significatif" comme ceci :

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

Qu'est-ce que cela fait :

  • il passe le modèle de base comme entrée de départ à Enumerable.inject , qui appellera à plusieurs reprises input.send(:joins, :assoc) (pour mon exemple, cela ferait Company.send(:joins, :categories) qui équivaut à `Company.categories
  • sur la jointure combinée, il exécute les conditions where (construites comme décrit ci-dessus)

Avis de non-responsabilité La syntaxe exacte dont vous avez besoin peut varier en fonction de l'implémentation SQL que vous utilisez.