Язык предикатов

Язык предикатов разработан для удобной работы как на стороне frontend, так и backend — для модификации, анализа и других операций.

С точки зрения абстрактного определения, предикат — это функция, которая принимает элемент некоторого множества и возвращает True или False.

В библиотеке ecos-records добавлена реализация данной концепции.

Есть два агрегатных состояния предиката - JSON или JAVA классы. Для конвертации между этими двумя состояниями есть сервис PredicateService и методы writeJson/readJson. Java представление - просто модель.

Обработка модели - отдельная задача, которая лежит на источниках данных или на преобразователях запросов (например: для запросов в alfresco предикаты трансформируются в fts-alfresco запрос в классе PredicateToFtsAlfrescoConverter).

JAVA представление

Предикат
  • Predicate - базовый маркерный интерфейс без методов

  • AttributePredicate - предикат связанный с некоторым аттрибутом

  • EmptyPredicate - предикат «Значение аттрибута пустое»

  • ValuePredicate - предикат «Значение аттрибута равно/содержит/одно из/больше/меньше/похоже/больше или равно/меньше или равно. Тип сравнения определяется полем type. Значение для сравнения в поле value

  • NotPredicate - предикат «НЕ какой_то_другой_предикат»

  • ComposedPredicate - предикат, который объединяет другие предикаты

  • AndPredicate - предикат И

  • OrPredicate - предикат ИЛИ

  • VoidPredicate - пустой предикат, который получается если t (тип) не задан. Как правило означает, что при фильтрации/поиске никаких проверок не нужно (берем все записи).

JSON представление

Пример:

{
   "t": "and",
   "val": [
       {
           "t": "or",
           "val": [
               {
                   "att": "country",
                   "t": "contains",
                   "val": "milan"
               },
               {
                   "att": "state",
                   "t": "contains",
                   "val": "milan"
               },
               {
                   "att": "name",
                   "t": "contains",
                   "val": "milan"
               }
           ]
       }
   ]
}

В поле «t» записывается тип предиката, в «att» название аттрибута (если тип предиката его требует) и в «val» значение (опять же имеет смысл не для всех предикатов).

Для конвертации java предикатов в json и обратно:

String predicateStr = Json.getMapper().toString(predicate);
Predicate predicate = Json.getMapper().read(predicateStr, Predicate.class);

Для чтения предикатов из запроса RecordsQuery:

Predicate predicate = recordsQuery.getQuery(Predicate.class);

Типы предикатов

Типы предикатов

Предикат

Описание

FTS

starts

Строка начинается с val.
Пример:
{t: «starts», att:«title», val: «startsWIth»}

title:«startsWith»

ends

Строка заканчивается с val.
Пример:
{t: «ends», att:«title», val: «endsWIth»}

title:«*endsWith»

or

Объединение массива предикатов в val по ИЛИ.
Пример:
{t: «or», val: [{t: «starts», att:«title», val: «startsWIth»}, {t: «ends», att:«title», val: «endsWIth»}]}

and

Объединение массива предикатов в val по И.
Пример:
{t: «and», val: [{t: «starts», att:«title», val: «startsWIth»}, {t: «ends», att:«title», val: «endsWIth»}]}

empty

Поле в att пустое.
Пример:
{t: «empty», att: «title»}

not

Отрицание предиката в val.
Пример:
{t: «not», val:{t: «empty», att: «title»}}
Этот предикат можно автоматически добавлять к другим добавляя префикс «not-».
Пример:
{t: «not-eq», att:«title», val: «startsWIth»}

eq

Значение поля att точно равно val.
Пример:
{t: «eq», att:«title», val: «SomeValue»}

gt

Значение поля att больше val

ge

Значение поля att больше или равно val

lt

Значение поля att меньше val

le

Значение поля att меньше или равно val

like

Значение att подходит под паттерн val.
В паттерне используется % как заменитель любого кол-ва символов (как в SELECT запросе БД)

in

Значение att подходит под паттерн val.

contains

Значение att содержит подстроку val (работает и для ассоциаций)

Например:

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

Доп. возможности предикатов

Фича

Описание

Промежутки и продолжительность

Для атрибутов типа date и datetime добавлена возможность вычислять продолжительность и промежутки
При указании промежутка первой границей он вычисляется от текущей даты-времени
При указании промежутка второй границей он вычисляется от первой границы
Поиск идёт включительно границ т.е. [DurationOrDateTime1, DurationOrDateTime2]
Добавлены две константы $NOW - текущая дата время, $TODAY текущая дата
Примеры:
Найти документы которые были созданы в течении 10 суток:
{t: "ge", att:"_created", val: "-P10D"}
Ищем документы у которых от $NOW - два года, до ($NOW - два года) - плюс год
{t: "eq", att:"_created", val: "-P2Y/P1Y"}
Найти документы которые были созданы в течении 2-х лет, до текущей даты-времени
{t: "eq", att:"_created", val: "-P2Y/$NOW"}
Найти документы которые были созданы в течении 2-х лет, до сегодняшнего дня
{t: "eq", att:"_created", val: "-P2Y/$TODAY"}
от 2020-01-01 до 2020-02-01
{t: "eq", att:"_created", val: "2020-01-01T00:00:00Z/2020-02-01T00:00:00Z"}
от 2020-01-01 до 2020-01-01 плюс 1 месяц
{t: "eq", att:"_created", val: "2020-01-01T00:00:00Z/P1M"}
от минус 10 дней до 2020-01-01
{t: "eq", att:"_created", val: "-P10D/2020-01-01T00:00:00Z"}
от сегодня
{t: "eq", att:"_created", val: "$TODAY"}

Дебаг предикатов и отладка

  1. Для дебага предикатов можно использовать следующий скрипт в консоли браузера:

await Records.queryOne({
    sourceId: 'uiserv/predicate',
    query: {
        record: 'emodel/sed-agreement@8a2964d4-e83b-4337-b033-05e7e4a232fc',
        predicate: {
          "t": "and",
          "val": [
            {
              "t": "not",
              "val": {
                "t": "eq",
                "att": "_status?str",
                "val": "Canceled"
              }
            }
          ]
        }
    }
}, 'result?bool')

Между json <-> yaml можно делать однозначную конвертацию в любом онлайн сервисе по запросу «конвертация yaml в json».

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

await Records.get(
    'emodel/sed-agreement@8a2964d4-e83b-4337-b033-05e7e4a232fc'
).load([
    '_status?str',
    "_status?id",
    "_status?disp",
    "_roles.isCurrentUserMemberOf.author?bool",
    "_roles.isCurrentUserMemberOf.admin?bool"
]);
{
  "_status?str": "Canceled",
  "_status?id": "emodel/sed-agreement@Canceled",
  "_status?disp": "Отменено",
  "_roles.isCurrentUserMemberOf.author?bool": false,
  "_roles.isCurrentUserMemberOf.admin?bool": true
}

Отображение кнопки при определённых условиях, описанных предикатом

_status - текстовое поле с локализацией, поэтому используется _status?str

t: not
val:
  t: in
  att: _status?str
  val:
    - draft
    - Canceled
    - Rejected