The hall of fame of frameworks that do support keyset pagination is rather short:
Ruby order_query
blaze-persistence — a rich Criteria API for JPA providers (started in July 2014)
This is where I need your help. If you are maintaining a framework that is somehow involved with pagination, I ask you, I urge you, I beg you, to build in native support for keyset pagination too. If you have any questions about the details, I’m happy to help (forum, contact form, Twitter)!
Especially the partSELECT * FROM news WHERE category_id = 1234 AND (date, id) < (prev_date, prev_id) ORDER BY date DESC, id DESC LIMIT 10;
AND (date, id) < (prev_date, prev_id)(date, id) < (prev_date, prev_id)
the same as
date < prev_date and id < prev_id
The idea is that offset is replaced with a condition that describes the last row of the previous page.
Br,TimoSQL Row Values
Besides regular scalar values, the SQL standard also defines the so-called row value constructors. They “Specify an ordered set of values to be constructed into a row or partial row” [SQL:92, §7.1: <row value constructor>]. Syntactically, row values are lists in brackets. This syntax is best known for its use in the insert statement.
Using row value constructors in the where clause is, however, less well-known but still perfectly valid. The SQL standard actually defines all comparison operators for row value constructors. The definition for the less than operations is, for example, as follows:
"Rx < Ry"is true if and only if RXi = RYi for all i < n and RXn < RYn for some n.— SQL:92, §8.2.7.2Where i and n reflect positional indexes in the lists. That means a row value RX is less than RY if any value RXn is smaller than the corresponding RYn and all preceding value pairs are equal (RXi = RYi; for i<n).
This definition makes the expression RX < RY synonymous to “RX sorts before RY” which is exactly the logic we need for the seek method.
date < prev_date and id < prev_id(date, id) < (prev_date, prev_id)
date <= prev_date and id < prev_id