Какие данные нужно отдавать клиенту для поддержки навигации по страницам
Что проверяют
Умеете ли вы спроектировать контракт пагинации, который работает на больших объёмах, а не только на тестовой базе в сто строк. Следом обычно спрашивают, чем offset-пагинация хуже курсорной и что будет, если между запросами страниц добавили новые записи.
Как отвечать
Состав ответа зависит от способа пагинации.
- offset (номер страницы): сами элементы, номер страницы, размер,
totalElementsиtotalPages. В Spring Data этоPage<T>: он выполняет дополнительныйcount-запрос. Если общее число не нужно, беритеSlice<T>: он запрашивает на одну запись больше и отдаёт только признакhasNext; - keyset (курсор): элементы,
nextCursorиhasNext. Курсор кодирует значения сортировки последней записи и её id, для клиента он непрозрачная строка. В Spring Data для этого естьWindow<T>иScrollPosition.keyset(); - ссылки:
first,prev,next,lastв теле (Spring HATEOAS,PagedModel) или в заголовкеLink, тогда клиент не собирает URL сам; - параметры запроса: применённая сортировка и фильтры, чтобы клиент видел, что именно он листает.
Отдельно скажите про Page в ответе контроллера: сериализовать PageImpl как есть Spring Data не рекомендует, формат JSON не стабилен. Лучше свой DTO или @EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO). Номера страниц в Spring по умолчанию начинаются с нуля, размер по умолчанию 20, а верхний предел задают через spring.data.web.pageable.max-page-size.
Почему курсор: OFFSET 100000 заставляет базу прочитать и выбросить сто тысяч строк, а count(*) на большой таблице стоит дорого. Курсор по индексу работает одинаково быстро на любой глубине, и записи не дублируются при вставках между запросами.
Где спотыкаются
Сортируют по неуникальному полю, например по дате. Записи с одинаковым значением перескакивают между страницами, в сортировку нужно добавлять id.
Не ограничивают size, и клиент одним запросом просит миллион строк.
Отдают totalPages для бесконечной ленты, где его никто не показывает, и платят за count на каждом запросе.