3.5.2.1.18. Filter

В этом разделе:

Компонент Filter − универсальное средство фильтрации списков сущностей, извлекаемых из базы данных для отображения в табличном виде. Компонент позволяет производить быструю фильтрацию данных по произвольному набору условий, а также создавать фильтры для многократного использования.

Filter должен быть связан с контейнером CollectionContainer через data loader либо с источником данных collectionDatasource, содержащим запрос на JPQL. Принцип действия фильтра основан на модификации этого запроса в соответствии с критериями, заданными пользователем. Таким образом, фильтрация осуществляется на уровне БД при выполнении транслированного из JPQL в SQL запроса, и на Middleware и клиентский уровень загружаются только отобранные данные.

Использование фильтра

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

gui filter descr

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

Для того чтобы создать быстрый фильтр, нажмите на ссылку Add search condition (Добавить условие поиска). Отобразится экран выбора условий:

gui filter conditions

Рассмотрим возможные типы условий:

  • Properties (Атрибуты) – атрибуты данной сущности и связанных с ней сущностей. Отображаются персистентные атрибуты, явно заданные в элементе property XML-описателя фильтра, либо соответствующие правилам, указанным в элементе properties.

  • Custom conditions (Специальные условия) – условия, заданные разработчиком в элементах custom XML-дескриптора фильтра.

  • Create new (Создать новое) – позволяет создать новое произвольное условие на JPQL. Данный пункт доступен пользователю, если у него есть специфическое разрешение cuba.gui.filter.customConditions.

Выбранные условия отображаются в верхней части панель фильтра. Рядом с каждым условием находится кнопка gui_filter_remove_condition, позволяющая удалить их из набора.

Быстрый фильтр можно сохранить для повторного использования в дальнейшем. Для этого нажмите на кнопку настроек фильтра и выберите Save/Save as (Сохранить/Сохранить как). Во всплывающем окне задайте имя нового фильтра:

gui filter name

Фильтр будет сохранен в выпадающем меню кнопки Search (Поиск).

Пункт меню Reset filter (Сбросить фильтр) позволяет сбросить все текущие условия поиска.

gui filter reset

Кнопка настроек фильтра содержит выпадающий список опций для управления фильтром:

  • Save (Сохранить) – сохранить изменения в текущем фильтре.

  • Save with values (Сохранить со значениями) – сохранить изменения в текущем фильтре, использовав значения в редакторах параметров фильтра как значения по умолчанию.

  • Save as (Сохранить как) – сохранить фильтр под новым именем.

  • Edit (Редактировать) – открыть редактор фильтра (см. ниже).

  • Make default (Установить по умолчанию) – установить фильтр по умолчанию для данного экрана. Фильтр будет автоматически выводиться на панель при каждом открытии экрана.

  • Remove (Удалить) – удалить текущий фильтр.

  • Pin applied (Закрепить) – использовать результаты последнего поиска для последовательной фильтрации данных (см. Последовательное наложение фильтров).

  • Save as search folder (Сохранить как папку поиска) – создать папку поиска на основе текущего фильтра.

  • Save as application folder (Сохранить как папку приложения) – создать папку приложения на основе текущего фильтра. Эта опция доступна только пользователям со специфическим разрешением cuba.gui.appFolder.global.

Опция Edit открывает редактор фильтра, который дает возможность расширенной настройки текущего фильтра:

gui filter editor

Название фильтра указывается в поле Filter name (Имя фильтра). Это имя будет отображаться в списке доступных фильтров для текущего экрана.

Фильтр можно сделать global (то есть доступным для всех пользователей) с помощью установки флажка Available for all users (Общий) для всех пользователей и global default с помощью флажка Global default. Для этих операций пользователю требуется специфическое разрешение CUBA > Фильтр > Создание/изменение глобальных фильтров. Если фильтр помечен как global default, то он будет автоматически выбран при открытии экрана пользователями. Каждый пользователь может установить свой собственный фильтр по умолчанию с помощью флажка Default (По умолчанию). Эта настройка имеет приоритет над global default.

В дереве содержатся условия фильтра. Условия можно добавлять с помощью кнопки Add (Добавить) менять местами при помощи кнопок gui_filter_cond_down/gui_filter_cond_up или удалять с помощью кнопки Remove (Удалить).

Группировку условий по И или ИЛИ можно добавить с помощью соответствующих кнопок. Все добавленные на верхний уровень (то есть без явной группировки) условия объединяются по И.

При выборе условия в дереве в правой части редактора открывается список его свойств.

С помощью соответствующих флажков можно сделать выбранное в таблице условие скрытым или обязательным для заполнения. Параметр скрытого условия не отображается пользователю, поэтому он должны быть введен во время редактирования фильтра.

Свойство Width позволяет задать ширину поля ввода параметра для текущего условия. По умолчанию, условия на панели фильтров отображаются в три колонки. Ширина поля равняется количеству колонок, которое оно может занять (1, 2 или 3).

Значение параметра текущего условия по умолчанию можно задать в поле Default value (Значение по умолчанию).

Специальный заголовок условия фильтрации можно задать в поле Caption (Заголовок).

Поле Operation позволяет выбрать оператор поиска. Список доступных операторов зависит от типа атрибута.

При поиске по атрибуту сущности с типом DateTime и без аннотации @IgnoreUserTimeZone по умолчанию будет учитываться часовой пояс текущего пользователя. Чтобы учитывать часовой пояс для атрибута с типом Date, необходимо установить флаг Use time zone в редакторе нового условия.

Описание компонента Filter

XML-имя компонента: filter.

Пример объявления компонента в XML-дескрипторе экрана:

<data readOnly="true">
    <collection id="carsDc" class="com.haulmont.sample.core.entity.Car" view="carBrowse">
        <loader id="carsDl" maxResults="50">
            <query>
                <![CDATA[select e from sample_Car e order by e.createTs]]>
            </query>
        </loader>
    </collection>
</data>
<layout expand="carsTable" spacing="true">
    <filter id="filter" applyTo="carsTable" dataLoader="carsDl">
        <properties include=".*"/>
    </filter>
    <table id="carsTable" width="100%" dataContainer="carsDc">
        <columns>
            <column id="vin"/>
            <column id="colour"/>
            <column id="model"/>
        </columns>
        <rowsCount/>
    </table>
</layout>

Здесь в элементе data определен data container, который выбирает экземпляры сущности Car с помощью JPQL запроса. Для компонента filter в его атрибуте loader указан фильтруемый загрузчик data loader. Данные отображаются компонентом Table, связанным с этим же контейнером.

Элемент filter может содержать вложенные элементы. Все они описывают условия, доступные пользователю для выбора в диалоге добавления условий:

  • properties - позволяет сделать доступными сразу несколько атрибутов сущности. Данный элемент может иметь следующие атрибуты:

    • include - обязательный атрибут, содержит регулярное выражение, которому должно соответствовать имя атрибута сущности.

    • exclude - содержит регулярное выражение, при соответствии которому атрибут сущности исключается из ранее включенных с помощью include.

    • excludeProperties – содержит список атрибутов, разделённых запятыми, которые должны быть исключены из фильтрации. В отличие от exclude, этот атрибут поддерживает путь по графу сущностей для указания каждого свойства в списке. Например, customer.name.

    • excludeRecursively - указывает, должны ли атрибуты, перечисленные в excludeProperties, быть рекурсивно исключены из полного графа сущностей. Если установлено true, указанный атрибут и все одноименные атрибуты вглубь по графу сущностей не будут использоваться в фильтре.

      Пример:

      <filter id="filter"
              applyTo="ordersTable"
              dataLoader="ordersDl">
          <properties include=".*"
                      exclude="(amount)|(id)"
                      excludeProperties="version,createTs,createdBy,updateTs,updatedBy,deleteTs,deletedBy"
                      excludeRecursively="true"/>
      </filter>

      Чтобы программно исключить атрибуты из фильтра, используйте метод setPropertiesFilterPredicate() компонента Filter:

      filter.setPropertiesFilterPredicate(metaPropertyPath ->
              !metaPropertyPath.getMetaProperty().getName().equals("createTs"));

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

    • Недоступные в связи с разрешениями подсистемы безопасности.

    • Коллекции (@OneToMany, @ManyToMany).

    • Неперсистентные атрибуты.

    • Атрибуты, не имеющие локализованного названия.

    • Атрибуты, аннотированные @SystemLevel.

    • Атрибуты типа byte[].

    • Атрибут version.

  • property - явно включает атрибут сущности по имени. Данный элемент может иметь следующие атрибуты:

    • name - обязательный атрибут, содержит имя включаемого атрибута сущности. Может быть путем (через ".") по графу сущностей. Например:

      <filter id="transactionsFilter" dataLoader="transactionsDl" applyTo="table">
          <properties include=".*" exclude="(masterTransaction)|(authCode)"/>
          <property name="creditCard.maskedPan" caption="msg://EmbeddedCreditCard.maskedPan"/>
          <property name="creditCard.startDate" caption="msg://EmbeddedCreditCard.startDate"/>
      </filter>
    • caption - локализованное название атрибута сущности для отображения условия фильтра. Как правило, представляет собой строку с префиксом msg:// по правилам MessageTools.loadString().

      Если в атрибуте name указан путь (через ".") по графу сущностей, то атрибут caption является обязательным.

    • paramWhere − задает выражение на JPQL для отбора списка значений параметра условия, если параметр является связанной сущностью. Вместо алиаса сущности параметра в выражении нужно использовать метку (placeholder) {E}.

      Например, предположим, что сущность Car имеет ссылку на сущность Model. Тогда список возможных значений параметра может быть ограничен только моделями Audi:

      <filter id="carsFilter" dataLoader="carsDl">
          <property name="model" paramWhere="{E}.manufacturer = 'Audi'"/>
      </filter>

      В выражении JPQL можно использовать параметры экрана, атрибуты сессии, а также компоненты экрана, в том числе отображающие другие параметры. Правила задания параметров запроса описаны в Dependencies Between Data Components и Запросы в CollectionDatasourceImpl.

      Пример использования параметра сессии и параметра экрана:

      {E}.createdBy = :session$userLogin and {E}.name like :param$groupName

      Используя paramWhere можно вводить зависимости между параметрами. Например, предположим, что Manufacturer является отдельной сущностью. То есть Car ссылается на Model, которая в свою очередь ссылается на Manufacturer. Тогда для фильтра по Car можно создать два условия: первое для выбора Manufacturer и второе для выбора Model. Чтобы ограничить список моделей выбранным перед этим производителем, добавьте в выражение paramWhere параметр:

      {E}.manufacturer.id = :component$filter.model_manufacturer90062

      Здесь параметр ссылается на компонент, отображающий параметр Manufacturer. Имя компонента, отображающего параметр условия, можно узнать, вызвав контекстное меню на строке таблицы условий в редакторе фильтра:

      gui filter component name
    • paramView − задает представление, с которым будет загружаться список значений параметра условия, если параметр является связанной сущностью. Например, _local. Если не указано, используется _minimal.

  • custom - элемент, определяющий произвольное условие. Содержимым элемента должно быть выражение на JPQL (возможно использование JPQL Macros), которое будет добавлено в условие where запроса контейнера данных. Вместо алиаса отбираемой сущности в выражении нужно использовать метку (placeholder) {E}. Параметр условия может быть только один, и если он есть, обозначается символом ?.

    Значение условия может содержать спецсимволы, например "%" или "_" для оператора "like". Если вам нужно экранировать эти символы, добавьте в условие escape '<char>', например:

    {E}.name like ? escape '\'

    Тогда если в значении параметра условия будет передано foo\%, поиск будет интерпретировать "%" как символ в имени а не как спецсимвол.

    Пример фильтра с произвольными условиями:

    <filter id="carsFilter" dataLoader="carsDl">
        <properties include=".*"/>
        <custom name="vin" paramClass="java.lang.String" caption="msg://vin">
          {E}.vin like ?
        </custom>
        <custom name="colour" paramClass="com.company.sample.entity.Colour" caption="msg://colour"
                inExpr="true">
          ({E}.colour.id in (?))
        </custom>
        <custom name="repair" paramClass="java.lang.String" caption="msg://repair"
                join="join {E}.repairs cr">
          cr.description like ?
        </custom>
        <custom name="updateTs" caption="msg://updateTs">
          @between({E}.updateTs, now-1, now+1, day)
        </custom>
    </filter>

    Созданные custom условия отображаются в секции Специальные условия диалога добавления условий:

    gui filter custom

    Атрибуты элемента custom:

    • name − обязательный атрибут - имя условия.

    • caption − обязательный атрибут - локализованное название условия. Как правило, представляет собой строку с префиксом msg:// по правилам MessageTools.loadString().

    • paramClass − Java-класс параметра условия. Если параметр отсутствует, то данный атрибут не обязателен.

    • inExpr − должен быть установлен в true, если выражение JPQL содержит условие in (?). При этом пользователь будет иметь возможность ввести несколько значений параметра данного условия.

    • join − необязательный атрибут для задания строки, которая будет добавлена в секцию from запроса контейнера данных. Это может потребоваться для создания условия по атрибуту связанной коллекции. Значение данного атрибута должно включать в себя предложения join или left join.

      Например, предположим что сущность Car имеет атрибут repairs, который представляет собой коллекцию экземпляров связанной сущности Repair. Тогда для фильтрации Car по атрибуту description сущности Repair можно написать следующее условие:

      <filter id="carsFilter" dataLoader="carsDl">
          <custom name="repair"
                  caption="msg://repair"
                  paramClass="java.lang.String"
                  join="join {E}.repairs cr">
              cr.description like ?
          </custom>
      </filter>

      При использовании такого условия исходный запрос контейнера:

      select c from sample_Car c order by c.createTs

      будет трансформирован в следующий:

      select c from sample_Car c join c.repairs cr
      where (cr.description like ?)
      order by c.createTs

      Кроме того, есть возможность создать произвольное условие, содержащее несвязанную сущность, и использовать эту сущность далее в секции where условия. В таком случае следует использовать ", " вместо предложений join или left join в значении атрибута join.

      Ниже приведен пример произвольного условия для поиска автомобилей, которые были назначены водителям после указанной даты:

      <filter id="carsFilter"
              dataLoader="carsLoader"
              applyTo="carsTable">
          <custom name="carsFilter"
                  caption="carsFilter"
                  paramClass="java.util.Date"
                  join=", ref$DriverAllocation da">
              da.car = {E} and da.createTs >= ?
          </custom>
      </filter>
    • paramWhere − задает выражение на JPQL для отбора списка значений параметра условия, если параметр является связанной сущностью. См. описание одноименного атрибута элемента property.

    • paramView − задает представление, с которым будет загружаться список значений параметра условия, если параметр является связанной сущностью. См. описание одноименного атрибута элемента property.

Атрибуты filter:

  • editable - если значение этого атрибута равно false, то кнопка Фильтр скрывается.

  • applyImmediately – указывает, когда применять фильтр. Если значение атрибута false, то фильтр будет применен только после нажатия кнопки Search. Если значение атрибута true, фильтр применяется сразу после изменения условий фильтрации. Общие случаи, когда фильтр применяется немедленно:

    • После изменения значения поля ввода параметра условия;

    • После изменения условия поиска;

    • После удаления условия из фильтра;

    • После изменения значения в поле Show rows;

    • При нажатии на кнопку OK в диалоговом окне редактора фильтра;

    • После очистки всех значений.

    В режиме немедленного исполнения вместо кнопки Search будет использоваться кнопка Refresh.

    Атрибут applyImmediately имеет приоритет над свойством приложения cuba.gui.genericFilterApplyImmediately.

  • manualApplyRequired − определяет, в какой момент будет применяться фильтр. Если значение атрибута равно false, то фильтр (пустой или по умолчанию) будет применяться сразу при открытии экрана. Это означает, что контейнер данных будет обновлен и связанные компоненты (например, Table) отобразят данные. Если значение атрибута равно true, то фильтр будет применяться только после нажатия на кнопку Search.

    Данный атрибут имеет приоритет над свойством приложения cuba.gui.genericFilterManualApplyRequired.

  • useMaxResults − ограничивает размер страницы загружаемых в контейнер данных экземпляров сущности. По умолчанию true.

    Если значение этого атрибута равно false, то фильтр не будет отображать поле Show rows. Количество записей в контейнере (и соответственно, показываемых таблицей) будет ограничено только параметром MaxFetchUI механизма статистики сущностей, по умолчанию - 10000.

    Если данный атрибут не указан, или равен true, то поле Show rows отображается, если у пользователя также есть специфическое разрешение cuba.gui.filter.maxResults. Если разрешение cuba.gui.filter.maxResults отсутствует, то фильтр будет принудительно отбирать только первые N строк без возможности пользователя отключить это или указать другое N. Число N определяется параметрами FetchUI, DefaultFetchUI, получаемыми из механизма статистики сущностей.

    На рисунке далее показан вид фильтра со значением атрибута useMaxResults="true", запретом специфического разрешения cuba.gui.filter.maxResults и параметром DefaultFetchUI=2

    gui filter useMaxRezult
  • textMaxResults - позволяет использовать текстовое поле вместо выпадающего списка в качестве поля Show rows. По умолчанию false.

  • folderActionsEnabled − при указании значения false позволяет скрыть следующие действия с фильтром: Сохранить как папку поиска, Сохранить как папку приложения. По умолчанию значение атрибута равно true, действия Сохранить как папку поиска, Сохранить как папку приложения доступны.

  • applyTo − необязательный атрибут, содержит идентификатор компонента, с которым связан фильтр. Используется в случае, когда необходимо иметь доступ к представлениям связанного компонента-таблицы. Например, сохраняя фильтр как папку поиска или как папку приложения, можно указать, какое представление будет применяться при просмотре этой папки.

    gui filter apply to
  • caption - позволяет задать заголовок панели фильтров.

  • columnsCount - задает количество колонок с условиями для конкретного фильтра. Значение по умолчанию - 3.

  • defaultMode - задает режим фильтра при открытии экрана. Возможные значения: generic и fts. При указании значения fts фильтр будет открыт в режиме полнотекстового поиска (если сущность индексируется). Значение по умолчанию - generic.

  • modeSwitchVisible - определяет видимость чек-бокса для перевода фильтра в режим полнотекстового поиска. Если полнотекстовый поиск невозможен, то чек-бокс будет невидим независимо от указанного значения. Возможные значения атрибута: true и false.

Методы интерфейса Filter:

  • setBorderVisible() - определяет видимость границы фильтра. Значение по умолчанию - true.

Слушатели компонента Filter:

  • ExpandedStateChangeListener - позволяет отслеживать изменения состояния компонента (свёрнутое/развёрнутое).

  • FilterEntityChangeListener - срабатывает при первом выборе фильтра и дальнейшем выборе сохранённых фильтров.

Внешний вид компонента Filter можно настроить с помощью переменных SCSS с префиксом $cuba-filter-*. Эти переменные можно изменить в визуальном редакторе после расширения темы или создания новой темы.



Права пользователей

  • Для создания/изменения/удаления глобальных (доступных всем пользователям) фильтров пользователь должен иметь разрешение cuba.gui.filter.global.

  • Для создания/изменения custom условий пользователь должен иметь разрешение cuba.gui.filter.customConditions.

  • Чтобы иметь возможность изменять максимальное количество строк на странице таблицы с помощью флажка и поля Show rows пользователь должен иметь разрешение cuba.gui.filter.maxResults. См. также атрибут фильтра useMaxResults.

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

Внешние параметры для управления фильтрами

  • Параметры вызова экрана

    При вызове экрана можно указать, какой фильтр и с какими параметрами должен быть применен сразу после открытия экрана. Для этого фильтр должен быть заранее создан, сохранен в базе данных, и соответствующая запись в таблице SEC_FILTER должна иметь заполненное поле CODE. Параметры вызова экрана задаются в конфигурационном файле web-menu.xml.

    Чтобы сохранить фильтр в базе данных, необходимо добавить скрипт вставки фильтра в скрипт 30.create-db.sql сущности. Для генерации скрипта найдите фильтр в справочнике Entity Inspector меню Administration, в контекстном меню выберите System Information, нажмите на кнопку Script for insert и скопируйте текст скрипта.

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

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

    Пример описателя пункта главного меню, устанавливающего в открываемом экране sample_Car.browse в компоненте carsFilter фильтр с кодом FilterByVIN, с подстановкой в параметр условия component$carsFilter.vin79216 значения TMA:

    <item id="sample_Car.browse">
        <param name="carsFilter" value="FilterByVIN"/>
        <param name="component$carsFilter.vin79216" value="TMA"/>
    </item>

    Следует отметить, что фильтр с установленным полем CODE обладает особыми свойствами:

    • Его не могут редактировать пользователи.

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

Последовательное наложение фильтров

При включенном свойстве приложения cuba.allowQueryFromSelected в пользовательском интерфейсе компонента можно закреплять последний примененный фильтр и текущие результаты фильтрации. После этого можно выбрать другой фильтр или параметры и применить их на уже выбранных записях.

Данный подход позволяет решить две проблемы:

  • Декомпозировать сложные фильтры.

  • Применять фильтры на записи, отобранные с помощью папок приложения или поиска.

Чтобы применить этот механизм в пользовательском интерфейсе, выберите и примените один из фильтров. Затем нажмите на кнопку настроек фильтра и выберите Pin applied (Закрепить). Фильтр закрепится в верхней части панели фильтров. Далее можно применить к выбранным записям другой фильтр. Так последовательно можно накладывать друг на друга любое количество фильтров. Также фильтры можно удалять последовательно с помощью кнопки gui_filter_remove.

gui filter sequential

Механизм последовательного наложения фильтров основан на возможности DataManager выполнять последовательные запросы.

API для работы с параметрами фильтра

Интерфейс Filter предоставляет методы для установки и чтения значений параметра фильтра из кода контроллера экрана:

  • setParamValue(String paramName, Object value)

  • getParamValue(String paramName)

paramName - имя параметра фильтра. Имя параметра фильтра является составной частью имени компонента, отображающего значение параметра фильтра. Как получить имя компонента рассматривалось выше. Имя параметра - это часть имени компонента, находящаяся после последней точки. Например, если имя компонента component$filter.model_manufacturer90062, то имя параметра фильтра model_manufacturer90062.

Обратите внимание, что в обработчике InitEvent контроллера экрана данные методы использовать нельзя, т.к. в этот момент фильтр еще не проинициализирован. Вы можете работать с параметрами фильтра в обработчике BeforeShowEvent.

Режим полнотекстового поиска в фильтре

Если контейнер данных фильтра содержит сущности, индексируемые системой полнотекстового поиска (см. Платформа CUBA. Полнотекстовый поиск), то в фильтре становится доступным режим полнотекстового поиска. Чтобы переключиться в него, используйте флажок Full-Text Search ("Полнотекстовый поиск").

gui filter fts

В этом режиме фильтр имеет поля для ввода критериев поиска, и поиск производится по индексируемым подсистемой FTS полям сущности.

Если таблица указана в атрибуте applyTo, то при наведении указателя мыши на строку таблицы во всплывающем окне будет написано, в каких полях сущности было найдено условие поиска.

Для скрытия переключателя режима фильтра установите значение false атрибуту фильтра modeSwitchVisible.

Если необходимо, чтобы фильтр по умолчанию открывался в режиме полнотекстового поиска, установите значение fts атрибуту defaultMode.

Полнотекстовый поиск может использоваться совместно с любым количеством условий универсального фильтра:

book publication fts filter

Выбрать условие FTS condition можно в окне выбора условий фильтра.