В одной из прошлых статей мы кратко рассказали, как грид контролы работают с удаленным сервером, используя OData протокол. Большинство современных грид контролов позволяют удобно группировать данные по нескольким колонками. Рассмотрим подробнее, как формулируются запросы к REST серверу, в случае, когда грид производит автоматическую группировку данных.
Удобство (зачем это нужно)
Грид контролы, позволяющие группировать записи по нескольким колонкам, существенно упрощают жизнь как программисту, так и конечному пользователю. Очень удобно иметь возможность сгруппировать, например, список задач, выделить задачи, требующие помощи, разделить их по приоритетам, и т.д.
Потом можно сгруппировать их по сотрудникам и просмотреть все задачи для выбранного сотрудника, также сгруппированные по статусу и приоритету.
Особенно приятно для разработчиков то, что для создания такого комфортного интерфейса достаточно лишь нескольких строк кода.
Как это сделать
Мы подготовили пример списка задач
nitrosdata.com/samples_devexpress_grouping.html
Для этого примера мы использовали dxDataGrid компании Devexpress (этот грид входит в набор компонентов DevExtreme Web)
Подробнее можно посмотреть в документации dxDataGrid:
js.devexpress.com/Documentation/ApiReference/UI_Widgets/dxDataGrid/
посмотреть примеры:
js.devexpress.com/Demos/WidgetsGallery/#demo/datagridgridpagingandscrollingpager/generic/light/default
простой пример работы грида без группировки с сервером NitrosBase.js:
nitrosdata.com/samples_devexpress_grid.html
Для того чтобы грид работал с NitrosBase.js достаточно указать URL со списком данных для показа в гриде, например, список людей:
nitrosdata.com/service/testdb/person
Для того чтобы включить в гриде группировку, достаточно указать свойство
groupPanel: {visible: true}
После этого грид позволяет группировать данные перетаскивая заголовки колонок на панель группировки.
Чтобы при первом показе грид сгруппировал данные по каким-либо колонкам, необходимо в описании колонок указать:
groupIndex: 0 (для первой колонки, по содержимому которой группируются данные)
groupIndex: 1 (для второй колонки, и т.д.)
Всего пара настроек, и пользователи будут Вам благодарны :)
Как это работает
Мы указали гриду URL по которому он может получить список задач
nitrosdata.com/service/testdb/task
Далее грид автоматически формирует запрос к серверу добавляя к URL различные условия запроса.
Первый запрос, который грид шлет на сервер, для показа списка задач, сгруппированных по статусу и приоритету:
http://nitrosdata.com/service/testdb/task?$orderby=Task_Status,Task_Priority,id&$top=21&$inlinecount=allpages
- $orderby=Task_Status,Task_Priority – отсортировать записи по статусу и приоритету
- $top=31 – грид просит ограничить результат и выслать 31 запись
- $inlinecount=allpages – грид просит вернуть общее количество записей в коллекции
Поскольку данные отсортированы, то в качестве группы он берет значения полей «Task_Status» и «Task_Priority» для первой записи. Далее идут несколько записей с повторяющимися значениями этих полей. Далее идет несколько записей у которых сменился приоритет. То есть в данном случае достаточно простого запроса для получения отсортированных данных.
Если мы будем скролировать грид вниз, чтобы посмотреть остальные записи, грид попросит у сервера следующие записи, например:
В данном случае добавился еще один параметр запроса:
- $skip=29 –пропустить указанное количество записей
Перечисленные выше запросы ничем не отличаются от простых запросов, которые грид слал бы серверу для показа списка задач, отсортированных по статусу и приоритету.
Теперь попробуем схлопнуть группу записей со статусом «Completed». Грид шлёт на сервер 2 запроса:
- http://nitrosdata.com/service/testdb/task?$top=1&$filter=(Task_Status eq 'Completed')&$inlinecount=allpages
- http://nitrosdata.com/service/testdb/task?$orderby=Task_Status,Task_Priority,id&$top=31&$filter=((Task_Status ne 'Completed'))&$inlinecount=allpages
Первый запрос
- $filter=(Task_Status eq 'Completed') – отобрать все записи со статусом «Completed» (те что мы схлопываем)
- $top=1 – грид просит только одну запись
- $inlinecount=allpages – грид просит вернуть общее количество записей с данным статусом
Этот запрос нужен для того, чтобы узнать количество записей со статусом «Completed» (те что мы схлопываем)
Второй запрос
- $orderby=Task_Status,Task_Priority – отсортировать данные по указанным полям
- $filter=(Task_Status ne 'Completed') – отобрать все записи со статусом отличным от «Completed» (те записи, что идут после схлопнутых со статусом «Completed»)
- $top=31 – грид просит 31 запись
- $inlinecount=allpages – грид просит вернуть общее количество записей с данным статусом
Если мы схлопываем следующую группу, то первый из этих двух запросов также просит количество записей для схлопываемой группы, а вот следующий запрос теперь уже включает исключения из записей в двух схлопнутых группах:
- $filter=(Task_Status ne 'Completed') and (Task_Status ne 'Deferred')
Таким образом, грид вполне успешно обходится простыми запросами, с ипользованием сортировки, простой фильтрации и условий постраничного вывода.
Замечания (как могло бы работать)
В случае большого количества групп запросы с фильтрацией всех схлопнутых групп, с большим количеством условий на неравенство ($filter=… and … and … and … and … ) становятся слишком большими и недостаточно эффективными.
Можно было бы просто получить список групп, а далее просить записи по равенству полей (все записи, принадлежащие определенной группе), с запросом на сортировку по этим полям. Такой запрос при наличии необходимых индексов быстро получает небольшое количество записей внутри одной группы и потом сортирует это небольшое количество.
Но для работы таким способом необходимо было бы иметь возможность стандартизованного выполнения запросов на агрегирование данных.
Возможность выполнения запросов на агрегирование данных появилась в стандарте OData версии 4.0, и производители грид контролов собираются реализовать поддержку этих возможностей в новых версиях продуктов.
Автор: NitrosData