How to make the rule is executed as expected flow in drools - drools

The the following logic:
if (order.getPrice()<200 ) {
order.setPrice(order.getPrice()-10);
} else {
order.setPrice(order.getPrice()-20);
if (order.getPrice()<200 ) {
//do nothing
}else {
order.setFreeShip("true");
}
}
for above logic, if I want to implement in drools rule.
rule "rule 1"
when
$o:Order ( amount<200);
then
$o.setPrice($o.getPrice()-10);
end
rule "rule 2"
when
$o:Order (amount>200);
then
$o.setPrice($o.getPrice()-20);
end
If the fact's price is 210, the rule2 is activated then the rule1 will be fired. That is not expected. I don't want to retract(). So is there any better solution for this issue?
And can I specify the sequential rule once a rule is executed like a token mechanism.
Thanks.

No, you can't specify sequential for just one rule or group of rules. You'll need to create a flag and use it as a guard, for example:
rule "rule 1"
when
$o:Order ( amount<200);
not: Flag(object=$o)
then
$o.setPrice($o.getPrice()-10);
insert( new Flag($o) );
end
rule "rule 2"
when
$o:Order (amount>200);
not: Flag(object=$o)
then
$o.setPrice($o.getPrice()-20);
insert( new Flag($o) );
end
and
public class Flag {
private final Object object;
public Flag(Object o) {
this.object = o;
}
//add getter for object
//delegate equals and hashcode to object
}

rule "NYuser_Rule"
no-loop true
ruleflow-group "EvalLoopcondition"
when
m:HelloProcessModel(userlocation in ("NewYorkUser"), count < 4)
then
m.setLoopcondition(6);update(m);
end
rule "ChileUser_Rule"
no-loop true
ruleflow-group "EvalLoopcondition"
when
m:HelloProcessModel(userlocation in ("ChileUser"), count < 3)
then
m.setLoopcondition(5);update(m);
end
Something like this can help you out.

Related

how to make a list after comparing two Lists

I'm trying to collect some objects in drools using this code:
rule "Evalua Anexo 10" salience 300 when
jul: ArchJuliano()
reg551: Registro551( fraccAne10 : getFraccion() ) from jul.getListReg551()
exists ( Anexo10( getFraccion() == fraccAne10 ) from jul.getListFraccAne10() )
then
// get pojo reg551
end
The above rule will fire once for each Registro551 when fraccAne10 exists in jul.getListFraccAne10. However, I want to get a list of Registro551 instead only the object.
rule "Evalua Anexo 10" salience 300 when
jul: ArchJuliano()
listOfReg551: List() from collect (
Registro551( fraccAne10 : getFraccion() ) from jul.getListReg551()
exists ( Anexo10( getFraccion() == fraccAne10 ) from jul.getListFraccAne10() )
)
then
// trying to get List<Registro551>
// fires error: mismatched input 'exists' in rule name-of-rule
end
facts:
public class Anexo10 {
private String fraccion;
// getters and setters
public class Registro551 {
private String fraccion;
// getters and setters
public class ArchJuliano {
private List<Anexo10> listFraccAne10;
private List<Registro551> listReg551;
// getters and setters
thank you very much.
rule "Evalua Anexo 10"
when
jul: ArchJuliano( $lAne: listFraccAne10, $lReg: listReg551 )
accumulate( Anexo10( $fA: fraccion ) from $lAne;
$fraccionsA: collectSet($fA) )
accumulate( Registro551( $fR: fraccion memberOf $fraccionsA ) from $lReg;
$fraccionsR: collectList($fR) )
then
...List<Registro551> $fraccionsR...
end
(I haven't tested this.)

How to define variable in side drools and add values to them

How to define variable and add values to it inside DRL file that can be used between the rules as a static resource.
I tried to use global keyword but when add I add values to it i will not be effected inside Working Memory as it mentioned in documentation. And in my case i conn't add it from Application side.
Example :
global java.util.List myList;
function java.util.List initMyList() {
List list = new ArrayList();
list.add(1);
return list;
}
rule "initListRule"
salience 1001
when
eval(myList == null)
then
myList = initMyList();
end
rule "checkIfListIsStillEmptyRule"
salience 1000
when
eval(myList != null)
then
System.out.println("MyList is Not null");
end
as global are not stored in woking memory then myList will be always null as it is not provided from Application side. is there any alternative to define variables and fill them in DRL ?
A DRL global is not evaluated dynamically and therefore your rule "checkIfListIsStillEmptyRule" will not fire.
You can do
rule "initListFact"
when
not List()
then
insert( new ArrayList() );
end
rule "checkThatThereIsAnEmptyList"
when
$list: List( size == 0 )
then
modify( $list ){ add( "something" ) }
end
If you don't need to observe changes of a global you can initialize it in a rule with very high salience. It will be available as a resource but you cannot base rule conditions on its state.
global list myList
rule "initListRule"
salience 9999999999
when
then
myList = new ArrayList();
end
You can use more than one global:
global List myListOne
global List myListTwo
rule "initListsRule"
salience 9999999999
when
then
myListOne = new ArrayList();
myListTwo = new ArrayList();
end
If you need to react to changes, there's no way around facts.
declare NamedList
name: String
list: ArrayList
end
rule createListOne
when
not NamedList( name == "one" )
then
insert( new NamedList( "one", new ArrayList() ) );
end
rule "checkIfListOneIsStillEmpty"
when
$nl: NamedList( name == "one", eval( list.size() == 0 ) )
then
modify( $nl ){vgetList().add( "something" ) }
end

Kie workbench Guided decision table Execute only one Rule

I am trying to execute the rules written in Kie workbench and integrated with Kie Exceution server and got the response as expected .
But my rquirement is to execute only one specific rule for list of large rules defined. What are the possible ways I can achieve .
I used Activation Group , rule flow group but no luck If any one can help me to achieve this .
I created Guided Decision table in KIE Workbench . and source was generated in this way
package demo.drools_examples;
//from row number: 1
rule "Row 1 Rule1"
dialect "mvel"
when
f1 : Account( accountType == "Regular" )
then
f1.setBaseAmount( 10.0 );
f1.setBaseThreshold( 10.0 );
calculateAmount(f1);
calculateThreshold(f1);
end
//from row number: 2
rule "Row 2 Rule1"
dialect "mvel"
when
f1 : Account( accountType == "Registered" )
then
f1.setBaseAmount( 5.0 );
f1.setBaseThreshold( 15.0 );
calculateAmount(f1);
calculateThreshold(f1);
end
//from row number: 3
rule "Row 3 Rule1"
dialect "mvel"
when
f1 : Account( accountType == "Saving" )
then
f1.setBaseAmount( 20.0 );
f1.setBaseThreshold( 10.0 );
calculateAmount(f1);
calculateThreshold(f1);
end
How can I define saliance , Activation group or any other policy to call only rule 1 rather than calling fireallRules(1).. Please help me
You'll have to call fireAllRules, but with a twist:
AgendaFilter af = new SingleRuleFilter( "Row 1 Rule1" );
int nFired = kieSession.fireAllRules( af );
And you only need to write something like this:
public class SingleRuleFilter implements AgendaFilter {
private String name;
public SingleRuleFilter( String name ){
this.name = name;
}
public boolean accept( Activation activation ){
return activation.getRule().getName().equals( name );
}
}

facing issue to iterate list in drools

facing problem with List iteration in drools
GoodsShipment has the list of GoodsItems and
GoodsItem has the list of Documents
my requirement is, need to check atleast one document is available or no.
iam tried this
but failed
writen a class to checking purpose
public class CheckDocument {
public boolean flag = false;
public CheckPreviousDocument() {
}
public boolean getPreviousDocument(GoodsShipment goodsshipment) {
List<GoodsItem> list = goodsshipment.getGoodsItems();
Iterator<GoodsItem> itr = list.iterator();
while (itr.hasNext()) {
GovernmentAgencyGoodsItem document = itr.next();
if (document.getDocuments().size() > 0) {
flag = true;
break;
}
}
return flag;
}
}
rule "previousDocuments minimum 1"
when
$o: GoodsShipment()
%x: CheckPreviousDocuments(previousDocuments($o) == false)
then
insert(-------------)
end
can anyone please help me..
thanks in advance
Your code is somewhat unusual, but thus rule should do. Note that I have used Document as the type for the elements in the list returned by GovernmentAgencyGoodsItem.getDocuments().
rule atLeastOne
when
$gs: GoodsShipment()
List( size > 0 )
from accumulate ( $gi: GoodsItem() from $gs.getGoodsItems() and
$d: Document() from $gi.getDocuments();
collectList( $d ) )
then
// $gs contains at least one Document
end

Drools Collection Effiency

I have 2 collections of the same type and each object in the collection is key by an id. My goal is to find the same object in both collections and then compare a field against each other. If they are not the same field then store the differences.
My issue is performance, for every rule I re-scan the collection for the same object. Is there a way if the object matches then run all field validations instead of finding the item in the collection multiple times?
Fact Code:
public class ReconcilerFact
{
private List<Security> securitySystem1;
private List<Security> securitySystem2;
public ReconcilerFact(List<Security> securities1, List<Security> securities2)
{
this.securitySystem1 = securities1;
this.securitySystem2 = securities2;
}
public List<Security> getSecuritySystem1()
{
return securitySystem1;
}
public List<Security> getSecuritySystem2()
{
return securitySystem2;
}
}
Drools Code:
rule "ISIN Rule"
no-loop
when
## conditions
##
$recon : ReconcilerFact()
$security1 : Security() from $recon.securitySystem1
$security2 : Security(sSecId == $security1.sSecId, sISIN != $security1.sISIN) from $recon.securitySystem2
then
## For the valid condition
##
result.add($security1, SecurityFields.ISIN, $security1.getsISIN(), $security2.getsISIN());
end
rule "Cusip Rule"
no-loop
when
## conditions
##
$recon : ReconcilerFact()
$security1 : Security() from $recon.securitySystem1
$security2 : Security(sSecId == $security1.sSecId, sCusip != $security1.sCusip) from $recon.securitySystem2
then
## For the valid condition
##
result.add($security1, SecurityFields.CUSIP, $security1.getsCusip(), $security2.getsCusip());
end
rule "Sedol Rule"
no-loop
when
## conditions
##
$recon : ReconcilerFact()
$security1 : Security() from $recon.securitySystem1
$security2 : Security(sSecId == $security1.sSecId, sSedol != $security1.sSedol) from $recon.securitySystem2
then
## For the valid condition
##
result.add($security1, SecurityFields.SEDOL, $security1.getsSedol(), $security2.getsSedol());
end
Instead of using the from Conditional Element you can just insert all the security objects and tag them with a Group field. So you will end up having:
$s1: Security(group == "Group1")
$s2: Security(group == "Group2", sSecId == $security1.sSecId)
That will treat each security as a fact and if you modify one single instance, only that instance will be reevaluated.
Cheers