Brief: свойства
Выражения и композиция
Свойство принимает набор объектов-параметров и возвращает ровно одно значение — как чистая функция, но вычисляемая сразу на всей базе, как колонка SQL-запроса. Значение либо хранится (первичное свойство, оператор DATA), либо вычисляется выражением: арифметика, логика (AND, OR, NOT), сравнения, операции со строками, проверка и приведение класса (IS, AS). Функции строк, чисел и дат (lpad, substr, mod, currentDate) — не операторы, а свойства модулей Utils и Time.
Подстановка свойства в выражение другого свойства — композиция; оператор JOIN записывает ее явно. Список создающих свойства операторов — Операторы.
price = DATA NUMERIC[14,2] (Item);
vat = DATA NUMERIC[6,2] (Item);
priceWithVAT (Item i) = price(i) * (1 + vat(i) / 100);
Группировка (GROUP)
Оператор GROUP разбивает все наборы объектов на группы и вычисляет для каждой одну агрегирующую функцию — SUM, MAX, MIN, CONCAT, LAST, EQUAL, AGGR / NAGGR, CUSTOM (агрегат СУБД):
GROUP
type [expr1, ..., exprN]
[orderClause]
[TOP topExpr] [OFFSET offsetExpr]
[WHERE whereExpr]
[BY groupExpr1, ..., groupExprM]
Отдельной функции подсчета нет: количество считается как GROUP SUM 1. Аналогия: GROUP BY в SQL, только результат — самостоятельное свойство, а не часть запроса. Механизм — какие наборы объектов попадают в группу, какими получаются параметры создаваемого свойства и где важен порядок — описан в группировке.
sold (Sku s) = GROUP SUM quantity(OrderDetail d) BY sku(d);
Разбиение и сортировка (PARTITION)
Оператор PARTITION тоже разбивает наборы объектов на группы блоком BY, но возвращает результат не на группу, а на каждый набор объектов — по окну внутри его группы, заданному порядком ORDER. Агрегирующие функции здесь — SUM, PREV, LAST, CUSTOM: отсюда места и ранги, сквозная нумерация, накопительные итоги, значение предыдущего или последнего набора в окне. Аналогия: оконные функции SQL (OVER (PARTITION BY ... ORDER BY ...)). Что именно попадает в окно, описано в разбиении / упорядочивании.
Форма PARTITION UNGROUP решает обратную задачу — распределяет значение свойства-источника по наборам объектов группы: пропорционально заданному выражению (PROPORTION) или по порядку (LIMIT).
place (Team t) = PARTITION SUM 1 ORDER DESC points(t), t BY conference(t);
Агрегация объектов (GROUP AGGR, AGGR)
Эти операторы работают не со значениями, а с объектами.
GROUP AGGR — форма оператора GROUP, возвращающая сам объект группы. Получается отображение, обратное перечисленным в BY свойствам, — например, поиск объекта по коду; платформа добавляет ограничение, что в группе такой объект не более одного.
Оператор AGGR идет дальше: он сам создает объект, когда агрегируемое выражение становится не NULL, и удаляет, когда оно снова NULL, заполняя свойства-отображения на параметры. Механизм — агрегации.
countryName = GROUP AGGR Country c BY name(c);
shipment (Invoice i) = AGGR ShipmentInvoice WHERE createShipment(i);
Выбор и переопределение (CASE, IF, OVERRIDE)
Оператор выбора проверяет условия по порядку и возвращает результат первого выполненного; условие выполнено, если его значение не NULL.
CASE— явные парыWHEN ... THEN ...и необязательныйELSE.IF— постфиксная одиночная формаresult IF condition;IF ... THENдобавляет блокELSE.OVERRIDE— первый операнд, не равныйNULL; так же подставляется значение по умолчанию вместоNULL.EXCLUSIVE— то же плюс декларация, что неNULLне более одного операнда.MULTI— операнд выбирается по совместимости классов аргументов с его сигнатурой.
signedQuantity (Ledger l) = MULTI quantity[InLedger](l), quantity[OutLedger](l);
price (Item i) = OVERRIDE salePrice(i), basePrice(i), 0;
Рекурсия (RECURSION)
Оператор RECURSION создает свойство, вычисляемое итерациями; к нему обращаются на деревьях, графах и транзитивных замыканиях — уровень узла, все предки объекта, достижимость по цепочке ссылок. Его части — STEP, префикс $ перед параметром и опция CYCLES — и то, как считаются итерации, описаны в рекурсии.
level (Group child, Group parent) = RECURSION 1 IF child IS Group AND parent = child
STEP 1 IF parent = parent($parent);
Абстрактные свойства (ABSTRACT)
Оператор ABSTRACT объявляет свойство без реализации: базовый модуль задает класс значения и классы параметров, а другие модули добавляют реализации инструкцией +=. Из них платформа собирает оператор выбора — это расширение свойств, способ снять зависимость между модулями и получить полиморфизм свойств.
Какая реализация выбирается и что требуется от реализаций, задается опциями — MULTI, CASE, VALUE, EXCLUSIVE, OVERRIDE, FULL — и описано в расширении свойств.
name 'Наименование' = ABSTRACT ISTRING[250] (Document); // базовый модуль
name (Shipment s) += ISTRING[250]('Поставка ' + number(s)); // модуль поставок