Arel, the SQL manager for Ruby (path 1)
Bài đăng này đã không được cập nhật trong 3 năm
Trong 1 bài viết trước, mình đã trình bày qua về việc sử dụng Arel để viết câu truy vấn SQL trong rails (https://viblo.asia/p/viet-scope-bang-arel-7prv31LkMKod), ở bài viết này mình xin dịch lại bài viết cách mà Arel manager SQL như thế nào
Nguồn bài viết: https://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html
Arel là loại thư viện mà nhiều người trong chúng ta sử dụng dựa vào nó hàng ngày và thậm chí có thể không biết về nó. Vậy thư viện này có ý nghĩa như thế nào?
Đó là tất cả để xây dựng cho các truy vấn SQL. Nó không phải là loại thư viện mà bạn thường muốn sử dụng trực tiếp (mặc dù bạn có thể sử dụng ở dạng đơn giản). Arel có ý nghĩa là base mà các framework xây dựng dựa trên để tạo ra API riêng phù hợp hơn.
Một trong đó là ActiveRecord (AR), ORM mặc định trong Rails. ActiveRecord chịu trách nhiệm cung cấp kết nối tới cơ sở dữ liệu, một cách thuận tiện để xác định các mối quan hệ giữa các mô hình của bạn, cung cấp một cách truy vấn dễ dàng và tất cả những thứ chúng ta thích.
# ActiveRecord
User.first.comments.where(created_at: 2.days.ago..Time.current).limit(5)
ActiveRecord sử dụng Arel để xây dựng các truy vấn và cuối cùng gọi ra nó để có được SQL chuyển nó để connect vào cơ sở dữ liệu của bạn.
Vậy làm thế nào Arel xây dựng các truy vấn trong một cách linh hoạt như vậy? Bằng cách xây dựng AST . Arel hoạt động nội bộ trên các nút AST - bạn sửa đổi truy vấn thông qua một cuộc gọi phương thức, Arel sửa đổi hoặc tạo nút thích hợp trong cây.
Hình này có hai đặc tính quan trọng. Thứ nhất, composability. Bằng cách kết hợp được, bạn sẽ có thể xây dựng truy vấn lặp đi lặp lại, từng mảnh, và thậm chí kết hợp nhiều câu hỏi với nhau. Nhiều phần của API (và do đó API của AR) sẽ không thể hoặc ít nhất là rất khó xử lý nếu không có thuộc tính này.
# ActiveRecord
bob = User.where(email: "bob@test.com").where(active: true)
# => SELECT "users".* FROM "users" WHERE "users"."email" = 'bob@test.com' AND "users"."active" = 't'
details = User.select(:id, :email, :first_name).order(id: :desc)
# => SELECT "users"."id", "users"."email", "users"."first_name" FROM "users" ORDER BY "users"."id" DESC
bob.merge(details).first
# => SELECT "users"."id", "users"."email", "users"."first_name" FROM "users"
# WHERE "users"."email" = 'bob@test.com' AND "users"."active" = 't'
# ORDER BY "users"."id" DESC LIMIT 1
Trong khi một ví dụ contrived, nó cho thấy rằng nó sẽ rất khó khăn để làm việc với các truy vấn này mà không có một số loại đại diện trừu tượng.
Arel không quan tâm những gì sẽ xảy ra với kết quả. Nó có thể kết thúc chuyển đổi thành một truy vấn SQL hoặc vào một định dạng hoàn toàn khác. Trên thực tế, Arel có thể chuyển đổi truy vấn sang định dạng điểm của Graphviz và bạn có thể tạo ra các biểu đồ trong số đó.
Chúng ta đã chỉ thấy truy vấn của ActiveRecord , phần được xây dựng trên Arel. Hãy bắt đầu làm việc trực tiếp với Arel. Sử dụng các hướng dẫn sau. Tập lệnh sẽ tải về phiên bản chính xác của thư viện và để lại cho bạn bên trong một trường hợp PULL REPL (chạy bundle console nếu bạn đã rời khỏi REPL và muốn quay lại). Tốt nhất nên kiểm tra tất cả các tập lệnh của bên thứ ba trước khi bạn chạy chúng.
cd /tmp
mkdir arel_playground && cd arel_playground
wget https://jpospisil.com/arel_setup.sh
# or
curl -L -o arel_setup.sh https://jpospisil.com/arel_setup.sh
bash ./arel_setup.sh
Để duy trì hiện tại trong tương lai gần, text dựa trên phiên bản Arel đã được phát hành. Text cũng chứa rất nhiều liên kết đến mã nguồn Arel thực tế, bạn cần phải nhìn trong text vùng được đánh dấu để xem tất cả các tùy chọn!
Diving in with SelectManager
Hãy bắt đầu bằng cách xây dựng một truy vấn chọn sẽ cung cấp cho tất cả user. Trước tiên, chúng ta cần tạo ra một đối tượng đại diện cho bảng. Lưu ý rằng bạn có thể đặt tên bảng bất cứ điều gì bạn muốn, nó không phải tồn tại ở bất cứ đâu.
users = Arel::Table.new(:users)
Arel::Table chính nó không làm nhiều nhưng nó có rất nhiều method can thiệp sâu hơn vào hệ thống. Method chúng tôi quan tâm hiện nay là prọect method . Tên đến từ đa số quan hệ nhưng hãy yên tâm, nó chỉ là một select đơn giản.
select_manager = users.project(Arel.star)
Lưu ý sử dụng Arel.star , một method tiện lợi cho ký tự * . Những gì chúng tôi có là một ví dụ của Arel :: SelectManager , đối tượng chịu trách nhiệm kết nối của truy vấn select. Bây giờ chúng ta sẽ có thể nhận được SQL kết quả từ select_manager .
select_manager.to_sql
# => NoMethodError: undefined method `connection' for nil:NilClass
Và nó đã không làm việc. Nếu bạn nghĩ về nó, loại thất bại có ý nghĩa (mặc dù lỗi nên được xử lý nhẹ nhàng hơn) bởi vì chúng tôi không xác định bất kỳ chi tiết cơ sở dữ liệu và Arel không có cách nào để biết cơ sở dữ liệu chúng tôi muốn truy vấn tạo ra những gì. Cơ sở dữ liệu có thể khác nhau về cú pháp, khả năng và thậm chí cả trong việc sử dụng ký tự. Hãy thử một kết nối cơ sở dữ liệu ActiveRecord.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
users = Arel::Table.new(:users, ActiveRecord::Base)
select_manager = users.project(Arel.star)
select_manager.to_sql
# => SELECT * FROM "users"
Thông qua ActiveRecord::Base tới cấu trúc của Arel::Table . Chúng ta cũng có thể biến tổng thể thông qua Arel::Table.engine= . Với tất cả những gì đã có, chúng tôi cuối cùng đã có truy vấn SQL.
Điều thú vị là sự kết hợp giữa Arel và ActiveRecord. Arel là kỹ thuật độc lập từ ActiveRecord nhưng nó cần để có được các chi tiết cơ sở dữ liệu từ một nơi nào đó và hiện nay nó sử dụng ActiveRecord. Cụ thể hơn, Arel yêu cầu API của ActiveRecord. Thậm chí có một thực hiện giả mạo ActiveRecord, FakeRecord , được sử dụng để chạy các bài kiểm tra của Arel. Trong quá khứ bạn cần một máy chủ MySQL đang chạy.
Getting picky
Truy vấn cho tất cả các detail của user là tốt, nhưng chúng ta hãy cụ thể hơn. Giả sử chúng ta muốn chỉ chọn id và tên người dùng. Sự trừu tượng quan trọng Arel cung cấp để làm việc với các thuộc tính (tên cột) là Arel :: Attribute .
Arel::Attribute thể hiện một cột đơn có tên tùy ý. Cách dễ nhất để giữ một Arel::Attribute cho một bảng là sử dụng phương thức Arel :: Table # [] . Chúng ta có thể sử dụng kết quả ngay trong project method.
select_manager = users.project(users[:id], users[:name])
select_manager.to_sql
# => SELECT "users"."id", "users"."name" FROM "users
Như bạn đã nhận thấy, class được bao gồm một loạt các module có thêm rất nhiều tính năng. Các module đầu tiên, Arel :: Expressions , cho biết thêm các chức năng tổng hợp chung.
select_manager = users.project(users[:comments_count].average)
select_manager.to_sql
# => SELECT AVG("users"."comments_count") AS avg_id FROM "users"
Kết quả của các hàm tổng hợp này được giữ trong các biến có tên mã hoá ( avg_id trong trường hợp này). May mắn thay, Arel :: AliasPredication giải quyết vấn đề này.
select_manager = users.project(users[:vip].as("status"), users[:vip].count.as("count")).group("vip")
select_manager.to_sql
# => SELECT "users"."vip" AS status, COUNT("users"."vip") AS count FROM "users" GROUP BY vip
Các Arel :: Math module là khá gọn gàng. Nó thực hiện các toán tử toán phổ biến để chúng ta có thể sử dụng chúng trực tiếp trên các thuộc tính như thể chúng ta đang làm việc với các giá trị.
Hết phần 1
All rights reserved