Du kan skapa en SQL-fråga baserat på din hash. Den mest generiska metoden är rå SQL, som kan köras av ActiveRecord
.
Här är lite konceptkod som borde ge dig rätt idé:
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
Vad detta gör:
query_select
anger vilken information du vill ha i resultatetquery_where
bygger alla sökvillkor och undkommer indata för att förhindra SQL-injektionerquery_tables
är en lista över alla tabeller du behöver söka eftertable_name = table.constantize.table_name
ger dig SQL-tabellnamnet som används av modellenraw_query
är den faktiska kombinerade sql-frågan från delarna ovanActiveRecord::Base.connection.execute(raw_query)
kör sql på databasen
Se till att sätta alla indata från användare inom citattecken och undvik det korrekt för att förhindra SQL-injektioner.
För ditt exempel kommer den skapade frågan att se ut så här:
select * from companies, categories where 'companies'.'name' LIKE 'a%' AND 'companies'.'hq_city' = 'karachi' AND 'categories'.'name' NOT LIKE '%ECommerce%'
Detta tillvägagångssätt kan behöva ytterligare logik för att sammanfoga tabeller som är relaterade. I ditt fall, om company
och category
har en association måste du lägga till något sånt här till query_where
"AND 'company'.'category_id' = 'categories'.'id'"
Enkelt tillvägagångssätt: Du kan skapa en Hash för alla par av modeller/tabeller som kan frågas och lagra lämpligt sammanfogningsvillkor där. Denna Hash bör inte vara för komplex även för ett medelstort projekt.
Hård strategi: Detta kan göras automatiskt om du har has_many
, has_one
och belongs_to
korrekt definierade i dina modeller. Du kan få en modells associationer med reflect_on_all_associations . Implementera en Breath-First-Search
eller Depth-First Search
algoritm och börja med valfri modell och sök efter matchande associationer till andra modeller från din json-ingång. Starta nya BFS/DFS-körningar tills det inte finns några obesökta modeller från json-ingången kvar. Från den hittade informationen kan du härleda alla anslutningsvillkor och sedan lägga till dem som uttryck i where
klausul i raw sql-metoden som förklarats ovan. Ännu mer komplext, men också genomförbart, skulle vara att läsa databasen schema
och använda ett liknande tillvägagångssätt som definieras här genom att leta efter foreign keys
.
Använda associationer: Om alla är associerade med has_many
/ has_one
, kan du hantera kopplingarna med ActiveRecord
genom att använda joins
metod med inject
på den "mest betydande" modellen så här:
base_model = "Company".constantize
assocations = [:categories] # and so on
result = assocations.inject(base_model) { |model, assoc| model.joins(assoc) }.where(query_where)
Vad detta gör:
- den skickar basmodellen som startindata till Enumerable.inject
, som upprepade gånger anropar input.send(:joins, :assoc) (för mitt exempel skulle detta göra
Company.send(:joins, :categories)
vilket motsvarar `Company.categories - på den kombinerade kopplingen exekverar den where-villkoren (konstruerade enligt beskrivningen ovan)
Ansvarsfriskrivning Den exakta syntaxen du behöver kan variera beroende på vilken SQL-implementering du använder.