Bạn có thể tạo một truy vấn SQL dựa trên hàm băm của mình. Cách tiếp cận chung nhất là SQL thô, có thể được thực thi bởi ActiveRecord
.
Đây là một số mã khái niệm sẽ cung cấp cho bạn ý tưởng phù hợp:
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
Điều này có tác dụng gì:
-
query_select
chỉ định thông tin bạn muốn trong kết quả -
query_where
xây dựng tất cả các điều kiện tìm kiếm và thoát đầu vào để ngăn chặn việc đưa vào SQL -
query_tables
là danh sách tất cả các bảng bạn cần tìm kiếm -
table_name = table.constantize.table_name
sẽ cung cấp cho bạn tên_bảng SQL như được sử dụng bởi mô hình -
raw_query
là truy vấn sql kết hợp thực tế từ các phần trên -
ActiveRecord::Base.connection.execute(raw_query)
thực thi sql trên cơ sở dữ liệu
Đảm bảo đặt bất kỳ đầu vào nào do người dùng gửi trong dấu ngoặc kép và thoát nó đúng cách để ngăn việc đưa vào SQL.
Đối với ví dụ của bạn, truy vấn đã tạo sẽ giống như sau:
select * from companies, categories where 'companies'.'name' LIKE 'a%' AND 'companies'.'hq_city' = 'karachi' AND 'categories'.'name' NOT LIKE '%ECommerce%'
Cách tiếp cận này có thể cần logic bổ sung để kết hợp các bảng có liên quan. Trong trường hợp của bạn, nếu company
và category
có một liên kết, bạn phải thêm một cái gì đó như thế này vào query_where
"AND 'company'.'category_id' = 'categories'.'id'"
Cách tiếp cận dễ dàng: Bạn có thể tạo Hash cho tất cả các cặp mô hình / bảng có thể được truy vấn và lưu trữ điều kiện nối thích hợp ở đó. Hash này không nên quá phức tạp ngay cả đối với một dự án quy mô vừa.
Cách tiếp cận khó: Điều này có thể được thực hiện tự động, nếu bạn có has_many
, has_one
và belongs_to
được xác định đúng trong các mô hình của bạn. Bạn có thể nhận các liên kết của một mô hình bằng cách sử dụng Reflect_on_all_associations . Triển khai Breath-First-Search
hoặc Depth-First Search
thuật toán và bắt đầu với bất kỳ mô hình nào và tìm kiếm các liên kết phù hợp với các mô hình khác từ đầu vào json của bạn. Bắt đầu chạy BFS / DFS mới cho đến khi không còn mô hình nào chưa được duyệt từ đầu vào json bên trái. Từ thông tin tìm được, bạn có thể lấy tất cả các điều kiện kết hợp và sau đó thêm chúng dưới dạng biểu thức trong where
mệnh đề của cách tiếp cận sql thô như đã giải thích ở trên. Thậm chí phức tạp hơn, nhưng cũng có thể làm được là đọc schema
cơ sở dữ liệu và sử dụng cách tiếp cận tương tự như được định nghĩa ở đây bằng cách tìm kiếm foreign keys
.
Sử dụng liên kết: Nếu tất cả chúng được liên kết với has_many
/ has_one
, bạn có thể xử lý các phép nối bằng ActiveRecord
bằng cách sử dụng joins
phương pháp với inject
trên mô hình "quan trọng nhất" như thế này:
base_model = "Company".constantize
assocations = [:categories] # and so on
result = assocations.inject(base_model) { |model, assoc| model.joins(assoc) }.where(query_where)
Điều này có tác dụng gì:
- nó chuyển base_model làm đầu vào bắt đầu tới Enumerable.inject
, sẽ liên tục gọi input.send (:joins,:assoc) (ví dụ của tôi, điều này sẽ thực hiện
Company.send(:joins, :categories)
tương đương với `Company.categories - trên phép nối kết hợp, nó thực thi các điều kiện where (được xây dựng như mô tả ở trên)
Tuyên bố từ chối trách nhiệm Cú pháp chính xác bạn cần có thể thay đổi tùy theo cách triển khai SQL mà bạn sử dụng.