Drool run twice with globals and without facts? - drools

global java.util.List rules;
global java.util.Map ruleParamsMap;
global java.util.List invoiceRowIds;
function BigDecimal sum(List invoiceRowIdsValue, String docType) {
return RuleHelper.sum(invoiceRowIdsValue, docType);
}
rule "example"
when
Boolean(booleanValue == true) from rules contains "TestedValue"
$amount : Integer() from ruleParamsMap.get("amount")
$sum : BigDecimal() from sum(invoiceRowIds, "IV")
Boolean(booleanValue == true) from $sum.compareTo(BigDecimal.valueOf($amount)) <= 0
then
System.out.println("RUN");
end
Here is my rule and for some reason, it prints RUN twice.
Any ideas why?

Related

Exception "Unable to resolve ObjectType 'getAmount' "

In Drools, I created a drl file with the following content:
import com.myorg.model.UserAccount;
import function com.myorg.utils.UserAccountHelper.getAmount;
rule "Classification userC"
when
$user : UserAccount(_age < 50);
$amount: getAmount($user, "single");
then
$user.set_userClassification("userC");
end
in Java I have a static method UserAccountHelper.getAmount
public static double getAmount(UserAccount account, String status)
{
double amount = 0d;
switch(status)
{
case "single":
if (account.canBeFullyRefunded)
amount = 1000;
else
amount = 100;
default:
amount = 0d;
}
return amount;
}
I am getting an Exception "Unable to resolve ObjectType 'getAmount'" when validating the drl file.Someone can help?
I am using Drools 7.37.
That's not the way you invoke static methods of a class in DRL. I would recommend you to take a look at the documentation to understand the syntax better.
If you want to invoke a static (or instance) method in a Pattern, you can do it like this:
rule "Classification userC"
when
$user : UserAccount(
_age < 50,
$amount: getAmount(this, "single")
)
then
$user.set_userClassification("userC");
end

Random Agenda Group get focused

I have created two different rules, which belong to two different agenda-groups.
First one:
rule "32-30-33.32"
dialect "java"
salience 0
agenda-group "32-30"
when
map : Map((this["Product Name"].toUpperCase().contains("PREMIUM ADPRODUCT")) && ((this["Size Length"] != 5) || (this["Size"].toUpperCase() not contains "300X600") || (this["Size"].toUpperCase() not contains "280X130") || (this["Size"].toUpperCase() not contains "300X250") || (this["Size"].toUpperCase() not contains "970X250") || (this["Size"].toUpperCase() not contains "320X50")));
then
JSONObject jObject = new JSONObject("{\"error34\":\"Premium Adproduct doesn't contain required Creative size!\"}");
Iterator<?> keys = jObject.keys();
while(keys.hasNext()) {
String key = (String)keys.next();
Object value = jObject.get(key);
map.put(key, value);
}
debug(drools);
end
Another rule, in another agenda group:
rule "47-37-1.0"
dialect "java"
salience 0
agenda-group "47-37"
when
map : Map((this["OrderName"] == null));
then
JSONObject jObject = new JSONObject("{\"error1\":\"OrderName should not be null \"}");
Iterator<?> keys = jObject.keys();
while(keys.hasNext()) {
String key = (String)keys.next();
Object value = jObject.get(key);
map.put(key, value);
}
debug(drools);
end
After this, I set focus to the group "47-37",
kieSession.getAgenda().getAgendaGroup("47-37").setFocus();
All rules within the group "32-30" are also getting evaluated. I'm using Drools 7.0.0. How can I control execution of rules only within the focused group?

Function on left hand side of rule on Drools

Consider the following rule for "locations" r and s:
∀r,s[(danger(r)∧adjacent(r,s))→danger(s)]
I tried to implement as follows:
function boolean adjacent(Location l1, Location l2) {
if (l1.x == l2.x)
return Math.abs(l1.y - l2.y) == 1;
if (l1.y == l2.y)
return Math.abs(l1.x - l2.x) == 1;
return false;
}
rule danger
when
$s : Location(danger == true)
$r : Location()
adjacent($s,$r)
then
modify($r) { setDanger(true) }
end;
But it does not compile saying that adjacent cannot be resolved.
I tried eval(adjacent($s,$r)) but it does not work because rete keeps visiting the same combinations of $s and $r forever.
I tried implementing adjacent() method on Location, but it does not compile either:
$r : Location(adjacent($s) == true)
I thought on some alternatives like: making each Location has a list of adjacent locations; etc. But none of them sounded right for me.
How would be the right way to implement this?
rule danger
when
$s : Location( danger )
$r : Location(! danger, adjacent($s,$r) )
then
modify($r) { setDanger(true) }
end
You can write a boolean expression as a constraint, or inside an eval CE (but never as a pattern all by itself, as you tried).
To avoid the loop, add a constraint that fails after the modify.

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

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