Sử dụng Arel trong Rails
Hầu hết các developer Rails đều quen thuộc sử dụng các truy vấn Active Record, rất ít sử dụng truy vấn với Arel. Nếu không biết đến Arel thì đây là một thiếu xót không hề nhỏ đối với các nhà phát triển. Bởi không có gì hoàn hảo cả. Active Record cũng có nhược điểm: trong mệnh đề WHERE Active Record ...
Hầu hết các developer Rails đều quen thuộc sử dụng các truy vấn Active Record, rất ít sử dụng truy vấn với Arel. Nếu không biết đến Arel thì đây là một thiếu xót không hề nhỏ đối với các nhà phát triển. Bởi không có gì hoàn hảo cả. Active Record cũng có nhược điểm: trong mệnh đề WHERE Active Record chỉ hỗ trợ truy vấn AND mà không hỗ trợ OR (cho đến tận rails 5), hay việc so sánh các chỉ cho các toán tử =, IN mà không có các toán tử >, <, >=, .... Để khắc phục nhược điểm này mình phải viết lại phần code này bằng SQL thuần. Với nhược điểm này nó đã được khắc phục bởi Arel. Bản thân Arel cũng có những điểm mạnh:
- Đơn giản hóa việc tạo ra các truy vấn SQL phúc tạp
- Tương thích với nhiều loại DB
Arel có rất nhiều powerfull và feature-rich technology. Nhưng trong bài viết tôi sẽ tập trung các tính năng mà bạn sử dụng thường xuyên nhất:
Arel Table
Để get Arel::Table instance của một model, ta có 2 option:
- Sử dụng class method của ActiveRecord:
users = User.arel_table
- Sử dụng constructor của class Arel::Table:
users = Arel::Table.new(:users)
SelectManager
users.project(users[:id]) # => SELECT users.id FROM users
Comparison operators =, !=, <, >, <=, >=, IN:
User.where(users[:age].eq(10)) # => SELECT * FROM "users" WHERE "users"."age" = 10 User.where(users[:age].not_eq(10)) # => SELECT * FROM "users" WHERE "users"."age" != 10 User.where(users[:age].lt(10)) # => SELECT * FROM "users" WHERE "users"."age" < 10 User.where(users[:age].gt(10)) # => SELECT * FROM "users" WHERE "users"."age" > 10 User.where(users[:age].lteq(10)) # => SELECT * FROM "users" WHERE "users"."age" <= 10 User.where(users[:age].gteq(10)) # => SELECT * FROM "users" WHERE "users"."age" >= 10 User.where(users[:age].in([20, 16, 17])) # => SELECT * FROM "users" WHERE "users"."age" IN (20, 16, 17)
Joins
User.joins(:photos).on(users[:id].eq(photos[:user_id])) # => SELECT * FROM users INNER JOIN photos ON users.id = photos.user_id
User.joins(:devices).where(users[:created_at].gt(1.day.ago))
Left joins
User.join(photos, Arel::Nodes::OuterJoin).on(users[:id].eq(photos[:user_id])) # => SELECT FROM users LEFT OUTER JOIN photos ON users.id = photos.user_id
Function AVG, SUM, COUNT, MIN, MAX, HAVING
photos.group(photos[:user_id]).having(photos[:id].count.gt(5)) # => SELECT FROM photos GROUP BY photos.user_id HAVING COUNT(photos.id) > 5 users.project(users[:age].sum) # => SELECT SUM(users.age) FROM users users.project(users[:age].average) # => SELECT AVG(users.age) FROM users users.project(users[:age].maximum) # => SELECT MAX(users.age) FROM users users.project(users[:age].minimum) # => SELECT MIN(users.age) FROM users users.project(users[:age].count) # => SELECT COUNT(users.age) FROM users
OR, AND queries
User.where(users[:name].eq("Bob").or(users[:surname].eq("Bob"))) # => SELECT "users".* FROM "users" WHERE ("users"."name" = 'Bob' OR "users"."surname" = 'Bob') name_clause = arel[:name].eq("Bob").and(arel[:surname].eq(nil)) surname_clause = arel[:name].eq(nil).and(arel[:surname].eq("Bob")) User.where(name_clause.or(surname_clause)) # => SELECT "users".* FROM "users" WHERE ("users"."name" = 'Bob' AND "users"."surname" IS NULL OR "users"."name" IS NULL AND "users"."surname" = 'Bob')
users = User.arel_table name_clause = arel.grouping(users[:name].eq("Bob").and(users[:surname].eq(nil))) surname_clause = users.grouping(arel[:name].eq(nil).and(users[:surname].eq("Bob"))) User.where(name_clause.or(surname_clause)) # => SELECT "users".* FROM "users" WHERE (("users"."name" = 'Bob' AND "users"."surname" IS NULL) OR ("users"."name" IS NULL AND "users"."surname" = 'Bob'))
LIKE queries
User.where(User.arel_table[:name].lower.matches("Bob".downcase)) # => SELECT "users".* FROM "users" WHERE (LOWER("users"."name") ILIKE 'bob')
Summary
Trên đây mình đã giới thiệu về arel với những câu truy vấn thường gặp. Để tìm hiều thêm về Arel bạn có thể đọc và tìm hiểu thêm tại:
- https://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html
- https://github.com/rails/arel