0

ActiveRecord's queries tricks

Một trong những ấn tượng của mình khi làm việc với Rails là về ActiveRecord's scope. Dựa vào độ hiệu quả và dễ dàng tái sử dụng thì ActiveRecord chỉ đơn giản là tuyệt vời. Sau đây mình sẽ giới thiệu với các bạn về một vài tricks nhỏ khi làm việc với scope cùng ActiveRecord.

1. Join query with condition on the associated table

Giả sử model User có quan hệ với model Company

class User < ApplicationRecord
  belong_to :company
  ...
end
class Company < ApplicationRecord
  has_many :users
  ...
end

Và lúc này yêu cầu của khách hàng là chọn ra những users thuộc company còn hoạt động (actived: true). Việc này khá là đơn giản:

scope :actived, -> {joins(:company).where(companies: {actived: true})}

Tuy nhiên, việc này sẽ vi phạm về tính đóng gói (encapsulation) của hướng đối tượng (OOP) vì 1 phần code bên company đã leak sang bên user. Vậy để giải quyết việc này query sẽ chuyển thành như sau.

#Company model
scope :actived, -> where{actived: true} 
#User model
scope :actived, -> {joins(:company).merge(Company.actived)}

Như vậy logic và những phần liên quan (concerns) được tách ra độc lập.

2. Different nested joins

Hãy cẩn thận với việc sử dụng query joins trong Rails. Mặc định khi viết scopes đơn lẻ nếu sử dụng joins thì ActiveRecord sẽ khi lại dưới dạng INNER JOIN tuy nhiên trong trường hợp dưới đây lại không phải vậy:

Giả sử có thêm model Course như sau:

class Course < ApplicationRecord
  belong_to: company
end
class Company < ApplicationRecord
   has_many :users
   has_many :courses

Khi sử dụng câu lệnh joins

User.joins(:comapny).merge(Company.joins(:courses))
=>SELECT *
   INNER JOIN companies ON companies.user_id  = users.id
   LEFT OUTER JOIN courses ON companies.course_id = courses.id

Để khắc phục tình huống này khá đơn giản với câu query: User.joins(company: :courses)

=>SELECT *
    INNER JOIN companies ON companies.user_id  = users.id
    INNER JOIN courses ON companies.course_id = courses.id

3. Subqueries

Ví dụ bạn cần lấy list companies có chứa courses tạo trong tuần này. Đôi lúc bạn sẽ xử lý như sau:

Company.where(course_id: Course.created_in_week.pluck(:id))

Tuy nhiên, lúc này chúng ta lại phải chạy 2 câu query 1 câu để lấy subset course_ids và 1 câu where course_id từ company. Trong những trường hợp như vậy, ActiveRecord đã có xử lý cho chúng ta 👍 và bạn không cần phải pluck(:id) nữa, lúc này đây số lượng câu query sẽ chuyển từ 2 sang 1.

 Company.where(course_id: Course.created_in_week)

4. Back to basics

ActiveRecord queries có thể chuyển hóa sang dạng raw SQL với câu lệnh .tosql, và .explain để có thể lấy được thông tin về performance, chi tiết câu query, ...

5. Conclusion

Trên đây là một vài tricks khi làm việc với ActiveRecord trong Rails mà mình rút ra, chúc các bạn thành công! Happy coding! 😇

6. References


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.