Работа со списками данных

Работа со списками данных #

Проблема отображения большого объёма данных #

В рамках данной работы рассмотрим типичные сценарии работы с большими наборами данных. Ключевая особенность заключается в том, что невозможно предоставлять пользователю сразу большой набор данных:

  • Большой объём данных долго обрабатывает система (как на сервере, так и на клиенте), что делает работу пользователя с системой неудобной.
  • Пользователю неудобно работать с очень большими документами.
  • Системы хотят самостоятельно обрабатывать свои данные и не делиться этими данными с другими системами.

Для быстрого предоставления данных применяется подход с частичным отображением информации, постраничный вывод. Для решения проблемы нахождения информации реализуется фильтрация элементов по различным критериям.

Сценарий постраничного вывода #

Рассмотрим базовый сценарий реализации постраничного вывода.

sequenceDiagram autonumber actor user as Пользователь participant brow as Браузер participant serv as Сервер user ->> brow : Открывает ссылку со списком brow ->> serv : Выполняет GET-запрос документа по ссылке serv ->> brow : HTML-документ с данными и навигационной панелью brow ->> user : Отображает полученный документ user ->> brow : Выбирает номер страницы на панели и нажимает на ссылку brow ->> serv : Посылает GET-запрос на документ с номером страницы serv ->> brow : HTML-документ с данными и навигационной панелью brow ->> user : Отображает документ с нужной страницей

Ключевые особенности подхода:

  • У маршрута для отображения документа появляется необязательный параметр — номер страницы.
  • По умолчанию номер страницы равен 1.
  • На HTML-странице отображается навигационная панель.
  • Внутри навигационной панели находятся подготовленные ссылки, включающие номера страниц, на которые пользователь может перейти.
  • Пользователь может скопировать ссылку и поделиться ей с другими пользователями.

Для указания параметра маршрута используется следующий синтаксис:

https://some.dev/list?page=10

С помощью ?page=10 указывается список параметров к документу. В данном случае он состоит из одного параметра — page со значением '10'.

Сценарий фильтрации #

В рамках сценария фильтрации разработчику необходимо предоставить пользователю интерактивные элементы для ввода данных, т.е. форму. Отличие данной формы от формы по добавлению новых элементов состоит в том, что запрос формы не подразумевает изменение данных на сервере. Запрос на фильтрацию данных может быть отправлен любым пользователем. Таким образом метод отправки запроса — это GET-запрос.

sequenceDiagram autonumber actor user as Пользователь participant brow as Браузер participant serv as Сервер user ->> brow : Открывает ссылку со списком brow ->> serv : Выполняет GET-запрос документа по ссылке serv ->> brow : HTML-документ с данными и формой brow ->> user : Отображает полученный документ user ->> brow : Пользователь заполняет поля формы и нажимает «Применить» brow ->> serv : Посылает GET-запрос на документ с параметрами формы serv ->> brow : HTML-документ с данными и заполненной формой brow ->> user : Отображает документ с нужной страницей

Ключевые особенности подхода:

  • Для указания параметров фильтрации используются HTML-формы, интерактивные элементы.
  • Данные с формы передаётся на сервер с помощью GET-запроса, а не POST-запроса.
  • Данные с формы не должны пропадать после передачи их пользователю.
  • Пользователь может скопировать ссылку и поделиться ей с другими пользователями, чтобы они увидели такой же результат.

Параметры с формы передаются в качестве параметров маршрута: https://some.dev/list?owner=maria&version=15

Общие комментарии к реализации отображения списка #

Оба подхода по фильтрации и по постраничному выводу используют одинаковый способ передачи данных от пользователя — параметры URI. Потенциально пользователь может их сформировать самостоятельно.

Для сохранения параметров запроса необходимо сохранять все параметры, которые были переданы при отправке на страницу. Это необходимо для сохранения параметров, которые пользователь ввёл ранее. Эти параметры необходимо использовать при формировании ссылок постраничного вывода, чтобы не потерять данные. Для получения полного URI, который был сформирован пользователем, используйте свойство uri класса Request.

Для формирования правильных ссылок внутри постраничного вывода используйте метод Uri.query для установления значения свойства page.

Получение параметров запроса #

Для формирования линз для получения запроса используйте класс Query. С помощью данных линз можно из запроса, Request, получать интересующие параметры запроса.

При формировании линзы для получения параметра страницы следует использовать значение по умолчанию равным 1. Терминатором для описания линзы должен служить defaulted.

Для доступа к параметром фильтрации необходимо использовать терминатор optional, в результате которого возвращается тип данных, который может принимать null.

Терминаторы defaulted и optional не формируют линзы, которые выбрасывают исключение в случае отсутствия или неправильного формата переданных данных.

Проблема нумерации элементов #

При использовании следующих подходов невозможно использовать метод withIndex для получения уникального номера элемента:

  • фильтрации;
  • частичной выборки;
  • сортировки.

Вместо этого необходимо:

  1. Добавить порядковый или уникальный номер внутрь элемента списка.
  2. При создании элемента необходимо использовать очередной номер из счётчика.
  3. Счётчик необходимо расположить внутри класса-списка.

Методы, возвращающие выборки данных, должны возвращать просто список из элементов.

Особенности работ с линзами #

К сожалению базовый интерфейс линз не позволяет обработать некорректные значения, а также частично заполненные данные. Для решения подобных проблем можно воспользоваться следующими методами-обёртками.

import org.http4k.lens.Lens
import org.http4k.lens.LensFailure

fun <IN : Any, OUT>lensOrNull(lens: Lens<IN, OUT?>, value: IN): OUT? =
    try {
        lens.invoke(value)
    } catch (_: LensFailure) {
        null
    }

fun <IN : Any, OUT>lensOrDefault(lens: Lens<IN, OUT?>, value: IN, default: OUT): OUT =
    try {
        lens.invoke(value) ?: default
    } catch (_: LensFailure) {
        default
    }

Вариант вызова данных функций-обёрток:

lensOrNull(fromLens, request)

Задачи #

Реализация постраничного вывода #

Реализуйте постраничный вывод информации о треугольниках, которые вводились в систему. Для облегчения реализации:

  • Инициализируйте список треугольников 10 разными треугольниками.
  • Добавьте номер треугольника в набор данных, которыми управляет класс-треугольник.
  • Ограничьте количество данных на одной странице 6 элементами.

Рекомендуется реализовать метод внутри класса-списка треугольников, возвращающий подмножество списка треугольников в зависимости от переданной страницы. Страница передаётся в качестве аргумента метода.

Проверьте краевые ситуации:

  • В списке треугольников нет нужных данных.
  • На последней страницы корректно отображается укороченный список.
  • При передаче номера страницы за пределами возможных значений приложение показывает пустой список.

Реализация поиска #

Реализуйте фильтрацию списка добавленных треугольников по следующим критериям:

  • Минимальная длина наименьшей стороны треугольника.
  • Максимальная длина наибольшей стороны треугольника.

При отсутствии значения аргумента данный фильтр применяться не должен.

При реализации модифицируйте метод получения списка треугольников так, чтобы он принимал все параметры формы в качестве аргументов.

Общее требование #

Фильтрация и постраничный вывод должны работать в вашем приложении одновременно.

© A. M. Васильев, 2022, CC BY-SA 4.0, andrey@crafted.su