Sie können eine SQL-Abfrage basierend auf Ihrem Hash erstellen. Der allgemeinste Ansatz ist rohes SQL, das von ActiveRecord ausgeführt werden kann .
Hier ist ein Konzeptcode, der Ihnen die richtige Vorstellung vermitteln sollte:
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
Was das bewirkt:
query_selectgibt an, welche Informationen Sie im Ergebnis haben möchtenquery_whereerstellt alle Suchbedingungen und maskiert die Eingabe, um SQL-Injektionen zu verhindernquery_tablesist eine Liste aller Tabellen, die Sie durchsuchen müssentable_name = table.constantize.table_namegibt Ihnen den SQL-Tabellennamen, wie er vom Modell verwendet wirdraw_queryist die eigentliche kombinierte SQL-Abfrage aus den obigen TeilenActiveRecord::Base.connection.execute(raw_query)führt das SQL auf der Datenbank aus
Stellen Sie sicher, dass alle Benutzereingaben in Anführungszeichen gesetzt und korrekt maskiert werden, um SQL-Einschleusungen zu verhindern.
Für Ihr Beispiel sieht die erstellte Abfrage folgendermaßen aus:
select * from companies, categories where 'companies'.'name' LIKE 'a%' AND 'companies'.'hq_city' = 'karachi' AND 'categories'.'name' NOT LIKE '%ECommerce%'
Dieser Ansatz erfordert möglicherweise zusätzliche Logik zum Zusammenführen verwandter Tabellen. In Ihrem Fall, wenn company und category eine Assoziation haben, müssen Sie so etwas zu query_where hinzufügen
"AND 'company'.'category_id' = 'categories'.'id'"
Einfacher Ansatz: Sie können für alle abfragbaren Paare von Modellen/Tabellen einen Hash erstellen und dort die entsprechende Join-Bedingung hinterlegen. Dieser Hash sollte selbst für ein mittelgroßes Projekt nicht zu komplex sein.
Harter Ansatz: Dies kann automatisch erfolgen, wenn Sie has_many haben , has_one und belongs_to richtig in Ihren Modellen definiert. Sie können die Zuordnungen eines Modells mit reflect_on_all_associationsBreath-First-Search oder Depth-First Search Algorithmus und beginnen Sie mit einem beliebigen Modell und suchen Sie nach passenden Assoziationen zu anderen Modellen aus Ihrer json-Eingabe. Starten Sie neue BFS/DFS-Läufe, bis keine nicht besuchten Modelle aus der json-Eingabe übrig sind. Aus den gefundenen Informationen können Sie alle Join-Bedingungen ableiten und diese dann als Ausdrücke im where einfügen -Klausel des Raw-SQL-Ansatzes, wie oben erläutert. Noch aufwendiger, aber auch machbar wäre das Auslesen des schema der Datenbank und verwenden Sie einen ähnlichen Ansatz wie hier definiert, indem Sie nach foreign keys suchen .
Assoziationen verwenden: Wenn alle mit has_many verknüpft sind / has_one , können Sie die Joins mit ActiveRecord handhaben durch Verwendung der joins Methode mit inject auf dem "bedeutendsten" Modell so:
base_model = "Company".constantize
assocations = [:categories] # and so on
result = assocations.inject(base_model) { |model, assoc| model.joins(assoc) }.where(query_where)
Was das bewirkt:
- es übergibt das base_model als Starteingabe an Enumerable.inject
, die wiederholt input.send(:joins, :assoc) aufruft (in meinem Beispiel würde dies
Company.send(:joins, :categories)tun was äquivalent zu `Company.categories
ist - beim kombinierten Join führt es die where-Bedingungen aus (wie oben beschrieben aufgebaut)
Haftungsausschluss Die genaue Syntax, die Sie benötigen, kann je nach verwendeter SQL-Implementierung variieren.