Możesz utworzyć zapytanie SQL na podstawie swojego skrótu. Najbardziej ogólnym podejściem jest surowy SQL, który może być wykonany przez ActiveRecord
.
Oto kod koncepcyjny, który powinien dać ci właściwy pomysł:
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
Co to robi:
query_select
określa, jakie informacje chcesz uzyskać w wynikuquery_where
buduje wszystkie warunki wyszukiwania i unika wprowadzania danych, aby zapobiec wstrzykiwaniu SQLquery_tables
to lista wszystkich tabel, które musisz przeszukaćtable_name = table.constantize.table_name
da ci nazwę tabeli SQL używaną przez modelraw_query
jest rzeczywistym połączonym zapytaniem sql z powyższych częściActiveRecord::Base.connection.execute(raw_query)
wykonuje sql w bazie danych
Upewnij się, że wszystkie dane wprowadzone przez użytkownika zostały umieszczone w cudzysłowie i odpowiednio je zmieniłeś, aby zapobiec wstrzyknięciom SQL.
W twoim przykładzie utworzone zapytanie będzie wyglądać tak:
select * from companies, categories where 'companies'.'name' LIKE 'a%' AND 'companies'.'hq_city' = 'karachi' AND 'categories'.'name' NOT LIKE '%ECommerce%'
Takie podejście może wymagać dodatkowej logiki łączenia powiązanych ze sobą tabel.W twoim przypadku, jeśli company
i category
masz skojarzenie, musisz dodać coś takiego do query_where
"AND 'company'.'category_id' = 'categories'.'id'"
Łatwe podejście: Możesz utworzyć skrót dla wszystkich par modeli/tabel, które mogą być odpytywane i przechowywać tam odpowiedni warunek złączenia. Ten skrót nie powinien być zbyt skomplikowany, nawet dla projektu średniej wielkości.
Trudne podejście: Można to zrobić automatycznie, jeśli masz has_many
, has_one
i belongs_to
prawidłowo zdefiniowane w Twoich modelach. Powiązania modelu można uzyskać za pomocą reflect_on_all_associations . Zaimplementuj Breath-First-Search
lub Depth-First Search
algorytm i zacznij od dowolnego modelu i wyszukaj pasujące asocjacje do innych modeli z danych wejściowych json. Rozpocznij nowe uruchamianie BFS/DFS, dopóki nie będzie żadnych nieodwiedzonych modeli z danych wejściowych json. Ze znalezionych informacji możesz wyprowadzić wszystkie warunki złączenia, a następnie dodać je jako wyrażenia w where
klauzula surowego podejścia sql, jak wyjaśniono powyżej. Jeszcze bardziej skomplikowane, ale również wykonalne byłoby odczytanie bazy danych schema
i używając podobnego podejścia, jak tutaj zdefiniowano, szukając foreign keys
.
Korzystanie z skojarzeń: Jeśli wszystkie z nich są powiązane z has_many
/ has_one
, możesz obsłużyć złączenia za pomocą ActiveRecord
za pomocą joins
metoda z inject
na "najważniejszym" modelu takim jak ten:
base_model = "Company".constantize
assocations = [:categories] # and so on
result = assocations.inject(base_model) { |model, assoc| model.joins(assoc) }.where(query_where)
Co to robi:
- przekazuje base_model jako dane wejściowe do Enumerable.inject
, który będzie wielokrotnie wywoływał input.send(:joins, :assoc) (w moim przykładzie wystarczyłoby to
Company.send(:joins, :categories)
co jest odpowiednikiem `Company.categories - w połączeniu połączonym wykonuje warunki WHERE (zbudowane jak opisano powyżej)
Zastrzeżenie Dokładna składnia, której potrzebujesz, może się różnić w zależności od używanej implementacji SQL.