Агрегации
Под агрегацией понимается создание уникального (агрегируемого) объекта, соответствующего каждому различному набору значений параметров, на котором некоторое агрегируемое свойство не NULL. Для такого объекта предполагается наличие свойств, которые отображают этот объект на каждый из параметров агрегируемого свойства, и наличие свойства, которое, наоборот, отображает параметры агрегируемого свойства на этот объект.
Для каждого различного набора значений параметров, на котором агрегируемое свойство не NULL, существует не более одного агрегируемого объекта; эта уникальность и есть ключевой инвариант, который поддерживает агрегация.
Агрегируемый объект и каждый параметр агрегируемого свойства должны принадлежать некоторому заданному классу.
Механизм агрегации работает на основе трёх событий:
- Базовое событие — событие проверки согласованности: платформа проверяет, что для каждого набора значений параметров, на котором агрегируемое свойство не
NULL, существует соответствующий агрегируемый объект, и наоборот. - Событие создания — событие разрешения: когда агрегируемое свойство становится не
NULLдля некоторого набора значений параметров и соответствующий агрегируемый объект ещё отсутствует, платформа его создаёт и заполняет свойства значениями этих параметров. - Событие удаления — событие разрешения: когда агрегируемое свойство становится
NULLдля набора значений параметров, соответствующий агрегируемый объект которого ещё существует, платформа этот объект удаляет.
В конкретной агрегации все три события могут совпадать, а события создания и удаления могут отличаться и от базового события, и друг от друга.
По сути агрегация — это синтаксический сахар. Если её развернуть, получится:
- на каждый параметр — свойство, которое помнит, какое значение было у этого параметра для каждого агрегируемого объекта;
- обратный проход — поиск агрегируемого объекта по набору значений параметров (это и есть группировка по этим свойствам);
- два обработчика событий: один создаёт новый агрегируемый объект (и записывает в него значения параметров), как только агрегируемое свойство становится не
NULLдля набора параметров, у которого ещё нет объекта; другой удаляет объект, как только у его набора параметров агрегируемое свойство снова становитсяNULL.
Вот как эта развёртка выглядит для двух параметров:
prm1 = DATA class1 (aggrClass);
prm2 = DATA class2 (aggrClass);
result = GROUP AGGR aggrClass aggrObject BY prm1(aggrObject), prm2(aggrObject);
// если aggrExpr становится не null, создаем объект класса aggrClass (эквивалентно aggrExpr => result(prm1, prm2) RESOLVE LEFT)
WHEN SET(aggrExpr) AND NOT result(prm1, prm2)
NEW aggrObject = aggrClass {
prm1(aggrObject) <- prm1;
prm2(aggrObject) <- prm2;
}
// если aggrExpr становится null, удаляем объект (эквивалентно aggrClass aggrObject IS aggrClass => result(prm1(aggrObject),prm2(aggrObject)) RESOLVE RIGHT)
WHEN aggrClass aggrObject IS aggrClass AND DROPPED(result(prm1(aggrObject),prm2(aggrObject))) DO
DELETE aggrObject;
Агрегация упаковывает всё это целиком — то же самое одной декларативной строчкой вместо всей развёртки.
Язык
Для создания агрегаций используется оператор AGGR.
Примеры
CLASS A; CLASS B; CLASS C;
f = DATA INTEGER (A, B);
c = AGGR C WHERE f(A a, B b) MATERIALIZED INDEXED;
CLASS AB;
ab = AGGR AB WHERE A a IS A AND B b IS B; // для каждой пары A B создает объект AB
CLASS Shipment 'Поставка';
date = ABSTRACT DATE (Shipment);
CLASS Invoice 'Инвойс';
createShipment 'Создавать поставку' = DATA BOOLEAN (Invoice);
date 'Дата накладной' = DATA DATE (Invoice);
CLASS ShipmentInvoice 'Поставка по инвойсу' : Shipment;
// создаем поставку по инвойсу, если для инвойса задана опция создавать поставку
shipment(Invoice invoice) = AGGR ShipmentInvoice WHERE createShipment(invoice);
date(ShipmentInvoice si) += sum(date(invoice(si)),1); // дата поставки = дата инвойса + 1