Filterable List: Nghệ Thuật Đãi Cát Tìm Vàng Trong Biển Dữ Liệu Khổng Lồ
Chào anh em! Chắc hẳn anh em không lạ gì những giao diện kiểu: Bảng danh sách sản phẩm bên phải, còn bên trái là một đống các ô checkbox, thanh kéo giá tiền, ô tìm kiếm... Người dùng tick vào đâu, danh sách lập tức thay đổi mượt mà đến đó. Đó chính là Filterable List.
Nhìn từ góc độ người dùng (UX), nó là một trải nghiệm tuyệt vời. Nhưng dưới góc độ của một kỹ sư Backend và System, đây là một bài toán hóc búa về hiệu năng. Làm sao để lôi ra được 50 bản ghi từ một bảng Database chứa 5 triệu dòng (như log giao dịch hệ thống soát vé chẳng hạn) chỉ trong nháy mắt?
Hôm nay, chúng ta sẽ bóc tách hai trường phái thiết kế Filterable List và cách để không làm server "tắt thở".
1. Bản chất của Filterable List
Hiểu đơn giản, Filterable List là một danh sách dữ liệu ban đầu, được thu hẹp lại (lọc bỏ đi) dựa trên một hoặc nhiều điều kiện (criteria) mà người dùng cung cấp.
Có hai nơi chốn để bạn thực hiện hành động "lọc" này: Lọc ở Client (Frontend) hoặc Lọc ở Server (Backend). Chọn sai nơi, hệ thống của bạn sẽ trả giá đắt.
2. Trường phái 1: Client-side Filtering (Lọc trên Frontend)
Đây là trường phái "mỳ ăn liền". Máy chủ (Backend) sẽ quăng toàn bộ danh sách data (ví dụ 1000 record) xuống trình duyệt một lần duy nhất. Sau đó, mỗi khi người dùng gõ tìm kiếm, Frontend sẽ dùng JavaScript (cụ thể là hàm Array.prototype.filter()) để tự xào nấu và hiển thị.
Ưu điểm:
- Cực kỳ nhanh và mượt (vì data đã nằm sẵn trong RAM của máy người dùng).
- Backend nhàn hạ, chỉ bị gọi 1 lần.
Nhược điểm "chí mạng":
- Chỉ dùng được với tập dữ liệu nhỏ (vài nghìn dòng đổ lại). Nếu bạn ném 1 triệu dòng log giao dịch xuống, trình duyệt của khách hàng sẽ treo cứng ngay lập tức.
- Dữ liệu có thể bị cũ (stale data) vì không được cập nhật real-time từ DB.
Tips thực chiến: Đừng quên kẹp thêm kỹ thuật Debounce (chúng ta đã bàn ở bài trước) vào ô tìm kiếm nhé, dù là lọc ở Frontend thì việc render lại DOM liên tục cũng sẽ gây giật lag!
3. Trường phái 2: Server-side Filtering (Lọc dưới Backend)
Đây là sân chơi thực sự của dân thiết kế hệ thống. Frontend chỉ đóng vai trò gửi các tham số lọc (Query Parameters) lên Server, mọi thao tác tính toán, nhặt dữ liệu đều do Backend gánh vác, sau đó chỉ trả về đúng 20-50 bản ghi (phân trang).
Ví dụ URL Frontend gửi lên: GET /api/transactions?status=failed&date=2026-06-01&station_id=5
Là một Backend Developer, đây là những ranh giới phân biệt giữa code "chạy được" và code "chuẩn Senior":
Tư duy sai lầm: Kéo hết về RAM rồi mới lọc
Nhiều anh em mới dùng ORM (như Eloquent) hay mắc lỗi này:
// Kéo HẾT hàng triệu bản ghi từ DB lên RAM của server PHP
$transactions = Transaction::all();
// Sau đó mới dùng Collection filter
$failedTransactions = $transactions->filter(function ($tx) {
return $tx->status == 'failed';
});
Code này trông có vẻ ngầu, nhưng nó chính là nguyên nhân làm tràn bộ nhớ (Out of Memory) và dính lỗi N+1 Query khét lẹt.
Tư duy chuẩn mực: Ép Database làm việc nặng
Database sinh ra là để xử lý dữ liệu. Hãy đẩy điều kiện lọc vào thẳng câu lệnh SQL (WHERE). Trong Laravel, chúng ta có một method cực kỳ thanh lịch để build các câu query động dựa trên tham số người dùng gửi lên là when():
$query = Transaction::query();
// Chỉ thêm câu lệnh WHERE nếu người dùng có truyền lên tham số tương ứng
$results = $query->when(request('status'), function ($q, $status) {
return $q->where('status', $status);
})
->when(request('station_id'), function ($q, $stationId) {
return $q->where('station_id', $stationId);
})
->paginate(50);
Đoạn code trên không chỉ sạch sẽ, tránh if-else lồng nhau chằng chịt, mà còn đảm bảo chỉ lấy đúng 50 bản ghi từ Database, bảo vệ RAM máy chủ tuyệt đối.
4. Bí kíp tối ưu (Level Kiến trúc sư)
Khi hệ thống của bạn phình to ra tới hàng chục triệu bản ghi, việc dùng WHERE thông thường vẫn sẽ làm hệ thống chậm lại. Lúc này, bạn cần áp dụng các vũ khí hạng nặng:
- Đánh Index (Chỉ mục): Nếu người dùng hay lọc theo
station_idvàstatus, hãy đảm bảo bạn đã tạo Composite Index cho 2 cột này trong Database. - Tránh xa
LIKE '%keyword%': Câu lệnh này sẽ quét toàn bộ bảng (Full Table Scan) và phớt lờ mọi Index. Nếu cần tìm kiếm full-text mạnh mẽ, hãy đẩy dữ liệu sang các engine chuyên dụng như Elasticsearch chứ đừng hành hạ MySQL/PostgreSQL.
Lời kết
Filterable List không chỉ là một cái UI/UX pattern trên màn hình, nó là một chuỗi liên kết chặt chẽ từ thao tác chạm của người dùng, qua bộ đệm Debounce ở Frontend, truyền xuống API, đi qua các lớp Query Builder động, và cuối cùng chạm tới Index của Database.
All rights reserved