I have two rules in my .drl file
rule "Monitor"
when
s : Test1( type == Test1.X )
n : Test123()
then
n.monitor();
drools.setFocus("Rules");
end
rule "Utilization"
agenda-group "Rules"
when
s : Test1( type == Test1.X , newValue > oldValue )
n : Test123()
then
//Do something
end
monitor() is a method in the class Test123, and this method sets values to some variables in the class Test1, by using Getters and Setters. And this method returns an object 'object'. In my second rule I want to compare values (newValue > oldValue) in the object 'object'. How can I perform this operation.
You can save the old value in global variable and compare with it in second rule.
Related
I am new to Drools and am having a tough time writing rules
Here is my data structure :
public class Premium{
private List<InsuranceType> insuranceTypes;
}
public class InsuranceType {
private String name;
}
So a Premium object will contain a List of Insurance types and I need to check if any of the insurance types has a name of "TPD"
Have tried the following :
rule "rule#3"
when
$fact:Premium($insuranceTypes : InsuranceType(name == 'TPD'))
then
System.out.println("Error");
end
However app server fails to start with the following error:
2021-11-30 12:16:37.004 ERROR 23500 --- [ main]
o.d.c.k.builder.impl.AbstractKieModule : Unable to build KieBaseModel:defaultKieBase
Unable to Analyse Expression InsuranceType(name == "TPD"):
[Error: unable to resolve method using strict-mode: com.xyz.Premium.name()]
[Near : {... InsuranceType(name == "TPD") ....}]
^
[Line: 29, Column: 5] : [Rule name='rule#3']
Unable to analyze expression 'InsuranceType(name == "TPD")' : [Rule name='rule#3']
Field Reader does not exist for declaration '$insuranceTypes' in '$insuranceTypes :
InsuranceType(name == "TPD")' in the rule 'rule#3' : [Rule name='rule#3']
I'm going to assume there's a public getName method on the InsuranceType class, and a public getInsuranceTypes method on the Premium class. If either of those isn't true, you need to add either getters or make those properties public.
Your rule was pretty close. However the problem you have is that insuranceTypes is a list but you were treating it as an object.
You have several options here, depending on your needs. However I'd go with the simplest, which is this:
rule "Example"
when
Premium( $insuranceTypes: insuranceTypes )
exists( InsuranceType( name == "TPD" ) from $insuranceTypes )
then
System.out.println("Error");
end
In the first line, I get the insurance types and assign them to the variable $insuranceTypes. This variable is now the list of types.
Then in the second line, I assert that there exists at least one InsuranceType in the list that has the name "TPD".
Note that Drools also has a memberOf operator, and a contains operator, which come in useful when working with lists and other iterable collections. These are inverses of each other, eg. you'd do Example( foo memberOf $someList ) or Example( myList contains $something ).
How can I generate something like this in my rule using PackageDescr ?
$var: Number (doubleValue > 100 ) from myPredefinedFunction()
I tried the following :
PatternDescr pt = new PatternDescr("Number","$var");
RelationalExprDescr ex = new RelationalExprDescr(">", false, null, new ExprConstraintDescr("myPredefinedFunction()"), new ExprConstraintDescr("100"));
pt.addConstraint(ex);
but this is what I get :
$var : Number( myPredefinedFunction() > 100 )
You're trying to set the myPredefinedFuntion() as a constraint. Constraints are the part of the drools declaration between the parentheses, eg. MyObject( foo == "bar" ) ... the foo == "bar" is a constraint.
Instead you need to set the source using the setSource method. This is the 'from' part of the declaration. This method takes a instance of a PatternSourceDescr subclass -- likely a FromDescr for this particular scenario.
(Alternatively, you might need setResource instead of setSource. The problem with using internal-only APIs is that they're not documented and subject to change without notice. I strongly suggest not going down this route.)
I want use the 'in' keyword in 'eval' function in which I am getting error that 'in' is not recognized by drools. So I have multiple values which I want check against a particular fact's attribute
when
$person : Person(PIN in ("123","456","789"))
then
//do something
end
//Like this I want use it in eval
when
$person : Person()
eval($person.PIN in ("123","456","789"))
then
//do something
end
But it is showing compile time error.
is there any other way to do it.
Edited
So I have some conditions in Decision Table where I want to use eval because other ways are not helpful in my scenario, below snapshot will Explain
SnapShot 1: Decision Table without eval()
SnapShot 2: Decision Table with eval()
Issue in first snapshot:
When compiling the spreadsheet the condition goes to the second lines object like below code : this is how it gets interpreted
when
personMap : PersonMap ()
basicEligiblePerson : Person( personalAddress.PIN in ($param) ) from
personMap.AddressesList
addresses : Address() from basicEligiblePerson.AddressesList
personalAddress : PersonalAddress() from addresses.PersonalAddress
then
basicEligiblePerson.setEligibility(true);
end
Issue in second snapshot :
When compiling this spreadsheet the condition goes to eval() function but 'in' keyword does not work in eval().
when
personMap : PersonMap ()
basicEligiblePerson : Person( personalAddress.PIN in ($param) ) from
personMap.AddressesList
addresses : Address() from basicEligiblePerson.AddressesList
personalAddress : PersonalAddress() from addresses.PersonalAddress
eval( personalAddress.PIN in ($param) )
then
basicEligiblePerson.setEligibility(true);
end
what should I do?
First sample given in your question is sufficient for the validation. You don't need to use eval.
when
$person : Person(PIN in ("123","456","789"))
then
//do something
end
If your requirement is to set eligibility to true for a given set of PINs, then you don't really need a decision table. I don't completely understand your POJO structure, so if Person class has a member variable addressList and AddressList class has a member personalAddress which has the member variable pin, you can achieve the results using the following rule. Please note that the nested fields are referred using the member variable names, not the class names. Also when you access the nested elements, if any of the elements can be null, please add the null check as well to avoid null pointer exceptions.
when
$basicEligiblePerson : Person( addressesList.personalAddress.pin in ("1234", "4567") )
then
$basicEligiblePerson.setEligibility(true);
end
In the below rule the logic in then-part is getting executed for all Child-objects which pass the given condition, I want to break the loop after the logic
in then-part is executed only once, how to do this
rule "test"
when
Parent( $childList : childList != null, childList.empty == false)
Child('testing'.equalsIgnoreCase(attribute)) from $childList
then
// testLogic
end
If you don't need a reference to the Child object (or any of its attributes) in the RHS, then you can use an exists operator:
rule "test"
when
Parent( $childList : childList != null, childList.empty == false)
exists Child('testing'.equalsIgnoreCase(attribute)) from $childList
then
// testLogic
end
If for some reason you do need the Child object or any of its attributes you can do something like this (although is not very nice):
rule "test"
when
Parent( $childList : childList != null, childList.empty == false)
$c: Child('testing'.equalsIgnoreCase(attribute)) from $childList.get(0)
then
// testLogic
end
Hope it helps,
Reason for infinite loop should be known by identifying whether it is self-loop or complex-loop.
Fact modification within the rule activates the same rule (Self)
Fact modification within the rule activates the different rule (complex) and it activates the original rule.
You can use 'no-loop', next to rule name as no-loop true
You can also restrict by using agenda-group, by checking conditions or like a flag. It depends upon your complexity.
How can I, using CQLINQ, get collection of input arguments for current method? There is any collection like "Arguments" or "Parameters" only "NbParamenter" which is not suitable for my purposes.
Indeed, CQLinq doesn't have this feature yet. However, in many cases you can compensate thanks to the fact that the properties ICodeElement.Name and IMember.FullName, for a IMethod, contain the coma separated list of names of the parameters types. For example here is the FullName of a method:
System.Windows.Forms.Control.BeginInvoke(Delegate,Object[])
and here is its Name:
BeginInvoke(Delegate,Object[])
Here is for example a CQLinq rule that harnesses parameters types names, to match event handlers methods:
// <Name>Event handler methods should be declared private</Name>
warnif count > 0
from m in Application.Methods where
!m.IsPrivate &&
// A method is considered as event handler if...
m.NbParameters == 2 && // ...it has two parameters..
m.Name.Contains("Object") && // ...of types Object...
m.Name.Contains("EventArgs") && // ...and EventArgs
// Discard special cases
!m.ParentType.IsDelegate &&
!m.IsGeneratedByCompiler
select new { m,m.Visibility }
// This rule implementation relies on the facts that:
// -> A method name contains the type of its parameters.
// -> All EventArgs derived types have the suffix "EventArgs".