length, count và size trong Ruby/Rails: giống mà không giống

Ba method cùng trả về “số lượng”, nhưng với ActiveRecord::Relation, chúng có thể tạo ra ba hành vi khác nhau. Đây là kiểu API nhìn hiền lành cho đến khi production có vài triệu record.

Với Array

1
2
3
4
5
values = [1, 2, 3]

values.length # => 3
values.size # => 3
values.count # => 3

lengthsize tương đương. count còn hỗ trợ đếm theo giá trị hoặc block:

1
2
values.count(2)          # => 1
values.count(&:even?) # => 1

Với ActiveRecord::Relation

1
users = User.where(active: true)

count

Thông thường gọi SQL COUNT(*) và không load toàn bộ record:

1
2
users.count
# SELECT COUNT(*) FROM users WHERE active = TRUE

Phù hợp khi chỉ cần số lượng từ database.

length

Load relation rồi đếm object trong bộ nhớ:

1
2
users.length
# SELECT users.* FROM users WHERE active = TRUE

Nếu relation chưa load, đây có thể là một cái giá khá đắt chỉ để biết một con số.

size

size chọn theo trạng thái:

  • relation đã load → dùng số phần tử trong bộ nhớ;
  • relation chưa load → thường gọi COUNT(*);
  • association có counter cache → có thể dùng giá trị cache.
1
2
users.load
users.size # không query thêm

Chọn method nào?

  • Cần đúng số lượng trong database, không cần record → count.
  • Đã có array/relation loaded → length hoặc size.
  • Muốn Rails chọn giữa bộ nhớ và database → size.
  • Cần kiểm tra có record hay không → ưu tiên exists?, không phải count > 0.
1
User.where(active: true).exists?

Không có method “luôn nhanh nhất”. Câu hỏi đúng là dữ liệu đang ở database hay đã nằm trong memory. Performance thường chết vì bỏ qua câu hỏi nhỏ đó, không phải vì thiếu thêm một gem monitoring.

Reference