#drools
#drools
Вопрос:
Я сталкиваюсь с некоторыми трудностями, пытаясь написать правило с помощью Drools (6.4.Final). Либо мое правило не совсем правильное, либо я заканчиваю тем, что пишу несколько правил для одной и той же цели, но без убежденности. Я хотел бы узнать ваше мнение, основанное на вашем опыте и / или рекомендациях, чтобы помочь мне.
У меня есть факт (тип C), который содержит список E.
Каждый E :
- имеет атрибут int, который равен году
- может иметь список R
Каждый R :
- имеет тип
- имеет определенную сумму
Я хочу проверять, для одного / каждого года, превышает ли сумма сумм R определенного типа значение. Если оно превышает, я хочу вставить факт (X) со всеми E, которые находятся позади.
Моя первая попытка была :
rule "Sum of R exceed for one year"
when
C( $listOfE : listOfE )
// Pick one E
E( $year : year ) from $listOfE
// All Es of the same year
// "collectLists" is a custom accumulate function that simply adds all the elements of a list
accumulate ( E(year == $year, $listofR : listofR ) from $listOfE;
$allR : collectLists($listofR))
// Check the sum
// "sumbd" is a custom accumulate function that simply adds BigDecimal (sum is broken for that in 6.4)
accumulate ( R( type == « X », $amount : amount ) from $allR;
$sum : sumbd($amount);
$cumulAnnee > 20000)
then
insert( new X($year, $elements));
end
Это работает, но есть проблема с элементами в X, которые могут не иметь R или иметь R, которые не соответствуют ожидаемому типу…
Моя вторая попытка — это:
Несколько, но более простых правил, объявляющих тип, чтобы помочь накапливать данные и работать с одним E.
declare YearAccumulation
year : int
allE : java.util.List
sum : BigDecimal
end
rule "Sum of R exceed for one year (1)"
when
C( $listOfE : listOfE )
$e : E( $year : year ) from $listOfE
// No accumulation for this year yet
not(YearAccumulation( year == $year ))
accumulate ( R( type == « X », $amount : amount ) from $e.listOfR;
$amounts : collectList($amount),
$sum : sumbd($amount);
$amounts.size > 0)
then
YearAccumulation ya = new YearAccumulation();
ya.setYear($year);
List allE = new ArrayList();
allE.add($element);
ya.setAllE(allE);
ya.setSum($sum);
insert(ya);
end
rule "Sum of R exceed for one year (2)"
when
C( $listOfE : listOfE )
$e : E( $year : year ) from $listOfE
accumulate ( R( type == « X », $amount : amount ) from $e.listOfR;
$amounts : collectList($amount),
$sum : sumbd($amount);
$amounts.size > 0)
// Do not process already processed E
$ya : YearAccumulation ( year == $year, $e not memberOf allE)
then
$ya.getAllE().add($e);
$ya.setSum($ya.getSum().add($sum));
update($ya);
end
rule "Sum of R exceed for one year (3)"
when
YearAccumulation ($year:year, $elements:allE, sum > 20000)
then
insert( new X($year, $elements));
end
Но я не чувствую уверенности в этом решении, и получение результата выглядит (довольно) сложным.
В конце концов, я хотел бы иметь простое правило (правила), которое будет легко понять и поддерживать.
Спасибо
Ответ №1:
Простым подходом было бы собрать все суммы за год за один проход по всем E и R и вернуть a Map<YearType,BigDecimal>
, где YearType
объединяет год и тип, и вы можете извлечь объекты ввода для проверки BigDecimal и т. Д. И т. Д.
Я не уверен, можете ли вы написать пользовательскую функцию накопления, возвращающую карту, но это можно сделать, используя «традиционный» синтаксис накопления, где у вас есть предложения init, action и result для программирования всего, что вам нужно.
Это краткая версия накопителя, который вы можете использовать:
Map() from accumulate( E( $lr: listOfR != null, $y: year )
init( Map m = new HashMap(); )
action( for( int i = 0; i < $lr.size(); i ){
//... add to m.get($y) observing R's type
} )
result( m ) )
Комментарии:
1. Спасибо. Это был бы действительно правильный путь. Но, похоже, это больше похоже на мою первую попытку.
2. Я имею в виду, что вы не проверяете тип R и теряете информацию о том, какие Es имеют учитываемые Rs.
3. Я не думаю, что есть проблема с составлением ключа карты из года и типа. Я добавлю это к своему ответу.
4. Такие алгоритмы могут быть лучше в Java, реализованные в классе C, например
Map<YearType,BigDecimal> auditOfR
, поскольку не похоже, что вычисление сумм зависит от изменчивых бизнес-правил и должно быть включено в правила. Оценка — годы и типы с чрезмерными суммами — все еще возможна в рамках правил.5. Даже если вам кажется, что вы забываете о необходимости также отслеживать все ЭС, которые в конечном итоге имеют эти Rs, имеющие правильный тип и имеющие совокупную сумму, превышающую значение (20000). Я понимаю вашу точку зрения.