Như đã giới thiệu trong bài trước đó phân trang dữ liệu là một trong những tính năng rất mạnh mẽ của EzyPlatform, và trong bài này mình sẽ trình bày thuật toán bên trong tính năng phân trang này nhé.

Thuật toán phân trang sử dụng con trỏ

Hãy nói bạn có các bản ghi được sắp xếp theo thứ tự thế này:

phan-trang-du-lieu.png

Nhiệm vụ của chúng ta là sẽ phải lấy ra các trang dữ liệu, ví dụ mỗi trang sẽ có 3 phần tử, thuật toán phân trang của EzyPlatform sẽ được cài đặt như sau.

Lấy từ trang đầu

Khi bạn lấy từ trang đầu với 3 phần từ lúc này EzyPlatform sẽ thực hiện các bước trong thuật toán như sau:

Ở lần lấy đầu tiên
  1. Client gửi lên nextPageToken = null
  2. Lấy ra 3 + 1 phần từ đầu tiên là [1, 2, 3, 4].
  3. Kiểm tra xem số phần từ lấy được có phải là 4 không, nếu đúng là 4 thì sẽ có trang kế tiếp, ngược lại thì không có trang kế tiếp.
  4. Có trang kế tiếp nên trả về hasNext = truepageToken.next=3 cho client. Lưu ý rằng pageToken.next=3 chứ không phải bằng 4, vì nếu lấy bản ghi 4 thì ở lần lấy kế tiếp sẽ không biết có trang sau hay không.
  5. Bởi vì đây là lấy trang đầu nên không có thông tin của nextPageToken được gửi từ client nên trả về cho client hasPrevious=falsepageToken.previous=null.

Kết quả trả về cho client sẽ là:

{
  "items": [1, 2, 3],
  "pageToken": {
    "next": "3",
    "previous": null
  },
  "continuation": {
    "hasNext": true,
    "hasPrevious": false
  },
  "count": 3,
  "total": 9,
  "timestamp": 1700380800000
}
Ở lần lấy thứ 2
  1. Client gửi lên nextPageToken = 3
  2. Lấy ra 3 + 1 phần từ đầu tiên với điều kiện >3 là [4, 5, 6, 7].
  3. Kiểm tra xem số phần từ lấy được có phải là 4 không, nếu đúng là 4 thì sẽ có trang kế tiếp, ngược lại thì không có trang kế tiếp.
  4. Có trang kế tiếp nên trả về hasNext = truepageToken.next=6 cho client.
  5. Vì có nhận được thông tin nextPageToken được gửi từ client nên trả về cho client hasPrevious=truepageToken.previous=4. Lưu ý phải trả về pageToken.previous=4 chứ không phải 3 vì nếu muốn lấy trang trước của 4 thì sẽ sử dụng điều kiện truy vấn < 4.

Kết quả trả về cho client sẽ là:

{
  "items": [4, 5, 6],
  "pageToken": {
    "next": "6",
    "previous": "4"
  },
  "continuation": {
    "hasNext": true,
    "hasPrevious": true
  },
  "count": 3,
  "total": 9,
  "timestamp": 1700380800000
}
Ở lần lấy thứ 3
  1. Client gửi lên nextPageToken = 6
  2. Lấy ra 3 + 1 phần từ đầu tiên với điều kiện >3 là [7, 8, 9].
  3. Vì chỉ có 3 phần tử lấy được thay vì 4 nên không có trang kế tiếp.
  4. Không có trang kế tiếp nên trả về hasNext = falsepageToken.next=null cho client.
  5. Vì có nhận được thông tin nextPageToken được gửi từ client nên trả về cho client hasPrevious=truepageToken.previous=7.

Kết quả trả về cho client sẽ là:

{
  "items": [7, 8, 9],
  "pageToken": {
    "next": null,
    "previous": "7"
  },
  "continuation": {
    "hasNext": false,
    "hasPrevious": true
  },
  "count": 3,
  "total": 9,
  "timestamp": 1700380800000
}

Lấy từ trang cuối

Khi bạn lấy từ trang đầu với 3 phần từ lúc này EzyPlatform sẽ thực hiện các bước trong thuật toán như sau:

Ở lần lấy đầu tiên
  1. Client gửi lên prevPageToken = nulllastPage=true.
  2. Lấy ra 3 + 1 phần từ cuối cùng là [9, 8, 7, 6].
  3. Kiểm tra xem số phần từ lấy được có phải là 4 không, nếu đúng là 4 thì sẽ có trang kế sau, ngược lại thì không có trang sau.
  4. Có trang sau nên trả về hasPrevious = truepageToken.previous=7 cho client.
  5. Bởi vì đây là lấy trang đầu nên không có thông tin của prevPageToken được gửi từ client nên trả về cho client hasNext=falsepageToken.next=null.
  6. Đảo ngược thứ tự các phần tử kết quả thành [7, 8, 9].

Kết quả trả về cho client sẽ là:

{
  "items": [7, 8, 9],
  "pageToken": {
    "next": null,
    "previous": "7"
  },
  "continuation": {
    "hasNext": false,
    "hasPrevious": true
  },
  "count": 3,
  "total": 9,
  "timestamp": 1700380800000
}
Ở lần lấy thứ 2
  1. Client gửi lên prevPageToken = 7
  2. Lấy ra 3 + 1 phần từ đầu tiên với điều kiện <7 là [6, 5, 4, 3].
  3. Số phần tử lấy ra được là 4 nên có trang sau.
  4. Có trang sau nên trả về hasPrevious = truepageToken.previous=4 cho client.
  5. Vì có nhận được thông tin prevPageToken được gửi từ client nên trả về cho client hasNext=truepageToken.next=7.
  6. Đảo ngược thứ tự các phần tử kết quả thành [4, 5, 6].

Kết quả trả về cho client sẽ là:

{
  "items": [4, 5, 6],
  "pageToken": {
    "next": "6",
    "previous": "4"
  },
  "continuation": {
    "hasNext": true,
    "hasPrevious": true
  },
  "count": 3,
  "total": 9,
  "timestamp": 1700380800000
}
Ở lần lấy thứ 3
  1. Client gửi lên prevPageToken = 4
  2. Lấy ra 3 + 1 phần từ đầu tiên với điều kiện <4 là [3, 2, 1].
  3. Vì chỉ có 3 phần tử lấy được thay vì 4 nên không có trang sau.
  4. Không có trang sau nên trả về hasPrevious = falsepageToken.previous=null cho client.
  5. Vì có nhận được thông tin prevPageToken được gửi từ client nên trả về cho client hasNext=truepageToken.next=3.
  6. Đảo ngược thứ tự các phần tử kết quả thành [1, 2, 3].

Kết quả trả về cho client sẽ là:

{
  "items": [1, 2, 3],
  "pageToken": {
    "next": "3",
    "previous": null
  },
  "continuation": {
    "hasNext": true,
    "hasPrevious": false
  },
  "count": 3,
  "total": 9,
  "timestamp": 1700380800000
}

Thuật toán phân trang sử dụng offset, limit

Thuật toán này cũng chung cơ chế page token như phân trang sử dụng con trỏ, tuy nhiên nó cũng có 2 điểm khác biệt.

  1. Khi truy vấn sẽ sử dụng offset thay vì điều kiện so sánh.
  2. Đối với tình huống người dùng lấy từ trang cuối nó sẽ cần lấy ra tổng số bản ghi trừ đi số bản ghi cần lấy totalRecords - limitđể tính ra được offset.

Tổng kết lại

Thuật toán cài đặt phân trong trong EzyPlatform cũng không quá phức tạp, điều quan trọng là một loạt các thiết kế và kỹ thuật lập trình đưa ra để sử dụng thuật toán này sao cho hiệu quả và mã nguồn được tái sử dụng nhiều nhất thay vì lặp đi lặp lại cho mỗi nghiệp vụ phân trang.