Sử dụng Arel trong Rails
Bài đăng này đã không được cập nhật trong 6 năm
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:
All rights reserved