Locating syntax error in Drools - drools

I have a drool file where rule 2 is always coming out to true.
rule "R2"
salience 1000
when
((residence status = "US Citizen") || ((residence status = "Lawfully-entered Alien") and (residence duration >= 5))) and (fpl <= 133)
then
Add to Programs List "3"
end
the value of
residence status is US Citizen
residence status is null
residence duration is 0
fpl is 278.77
Why it is going into it always though for specified value the condition is false .
Kindly find the all files.
package com.eligibility.service;
import com.platform.enrollment.domain.BasicInfo;
import com.platform.enrollment.domain.FamilyMember;
global java.util.List prog_id_list;
global java.lang.Double fpl;
[condition][] and=and
[condition][] or=or
[condition][]residence status \= "{value}"=FamilyMember(basicInfo.citizenshipStatus == "{value}")
[condition][]residence duration >\= {value}=FamilyMember(basicInfo.permanentResidenceDuration >= {value})
[condition][]job status \= "{value}"=FamilyMember(basicInfo.jobStatus == "{value}")
[condition][]large business status \= "{value}"=FamilyMember(basicInfo.largeBusinessStatus == "{value}")
[condition][]false=eval(false)
[condition][]true=eval(true)
[condition][]age \= {value}=FamilyMember(basicInfo.getAge=={value})
[condition][]age < {value}=FamilyMember(basicInfo.getAge<{value})
[condition][]age > {value}=FamilyMember(basicInfo.getAge>{value})
[condition][]age <\= {value}=FamilyMember(basicInfo.getAge<={value})
[condition][]age >\= {value}=FamilyMember(basicInfo.getAge>={value})
[condition][]fpl \= {value}=eval(fpl=={value})
[condition][]fpl < {value}=eval(fpl<{value})
[condition][]fpl > {value}=eval(fpl>{value})
[condition][]fpl <\= {value}=eval(fpl<={value})
[condition][]fpl >\= {value}=eval(fpl>={value})
[condition][]gender \= "{value}"=FamilyMember(basicInfo.gender == "{value}")
[condition][]pregnant \= true=Applicant(eval(pregnant))
[condition][]pregnant \= false=Applicant(eval(!pregnant))
[condition][]Family member age greater than or equal to 60 \= true=eval(any_member_greater_than_equal_to_60)
[condition][]Family member age greater than or equal to 60 \= false=eval(!any_member_greater_than_equal_to_60)
[condition][]job status \= "{value}"=Applicant(eval(job_status.contains("{value}")))
[condition][]large business status \= "{value}"=Applicant(eval(large_business_status.contains("{value}")))
[condition][]employer benefits \= "{value}"=Applicant(eval(large_business_status.contains("{value}")))
[condition][]spouse \= true=eval(spouse)
[condition][]spouse \= false=eval(!spouse)
[condition][]total assets \= {value}=eval(total_assets=={value})
[condition][]total assets < {value}=eval(total_assets<{value})
[condition][]total assets > {value}=eval(total_assets>{value})
[condition][]total assets <\= {value}=eval(total_assets<={value})
[condition][]total assets >\= {value}=eval(total_assets>={value})
[condition][] Question with code "{code}" has answer "{answer}"=Question(code=="{code}"&& eval(answer.contains("{answer}")))
[consequence][]Add to Programs List {id}=prog_id_list.add({id});
rule "R0"
salience 1000
when
(age <= 0) and ((residence status = "US Citizen") || (residence status = "Lawfully-entered Alien")) and (fpl <= 200)
then
Add to Programs List "1"
end
rule "R1"
salience 1000
when
((residence status = "US Citizen") || (residence status = "Lawfully-entered Alien")) and (fpl >= 400)
then
Add to Programs List "2"
end
rule "R2"
salience 1000
when
((residence status = "US Citizen") || ((residence status = "Lawfully-entered Alien") and (residence duration >= 5))) and (fpl <= 133)
then
Add to Programs List "3"
end
rule "R3"
salience 1000
when
((residence status = "US Citizen") || (residence status = "Lawfully-entered Alien")) and (fpl > 133) and (fpl < 400)
then
Add to Programs List "4"
end
Can anybody help me why it is enetring is list3 though data as provided above is not making it valid

It could also be helpful to look at the generated java class for your rule and see what the issue might be.
Use:
-Ddrools.dump.dir=
to set path for the generated code.
It could also be helpful to set a rules listener and look at the fact insertions updates and rules firing sequence.
Also you could try to break the problematic rule into two rules without DSL as follows for debug purpose:
when
FamilyMember(basicInfo.citizenshipStatus == "US Citizen")
Fact(eval(fpl <= 133))
when
FamilyMember(basicInfo.citizenshipStatus == "Lawfully-entered Alien")
FamilyMember(basicInfo.permanentResidenceDuration >= 5)
Fact(eval(fpl <= 133))
Hope this helps.

Related

Activation Group and Salience - wrong rule being fired

I have the following 3 rules in my system. The facts match all 3 rules. Based on the salience and activation group I would expect either rule 10 or 11 to fire. However rule 12 is always firing.
To complicate matters I have 3 systems this runs on. It runs with the expected rule 10 or 11 firing on one system and the other two systems rule 12 is firing.
Any ideas?
rule "dimensionConfigMotorTubeDefault_10"
salience 390
activation-group "ag_R238_r28-wirefree"
no-loop true
lock-on-active true
when
p: Product(sku != 1)
$mm : InventoryChoiceOption(sku == "mm")
exists (NII(internalPattern in ("R238")) from $mm.value)
$opt : TextChoiceOption(sku == "motorControl")
exists (OptionValue(optionValueValue in ("r28-wirefree")) from $opt.value)
DimensionOption(sku == "dim", widthValue <= "72", heightValue <= "120")
$edi : TextOption(sku == "default_tube_size")
then
$edi.setValue("1.5"); update($edi);
end
rule "dimensionConfigMotorTubeDefault_11"
salience 390
activation-group "ag_R238_r28-wirefree"
no-loop true
lock-on-active true
when
p: Product(sku != 1)
$mm : InventoryChoiceOption(sku == "mm")
exists (NII(internalPattern in ("R238")) from $mm.value)
$opt : TextChoiceOption(sku == "motorControl")
exists (OptionValue(optionValueValue in ("r28-wirefree")) from $opt.value)
DimensionOption(sku == "dim", widthValue <= "96", heightValue <= "96")
$edi : TextOption(sku == "default_tube_size")
then
$edi.setValue("1.5"); update($edi);
end
rule "dimensionConfigMotorTubeDefault_12"
salience 380
activation-group "ag_R238_r28-wirefree"
no-loop true
lock-on-active true
when
p: Product(sku != 1)
$mm : InventoryChoiceOption(sku == "mm")
exists (NII(internalPattern in ("R238")) from $mm.value)
$opt : TextChoiceOption(sku == "motorControl")
exists (OptionValue(optionValueValue in ("r28-wirefree")) from $opt.value)
DimensionOption(sku == "dim", widthValue <= "102", heightValue <= "120")
$edi : TextOption(sku == "default_tube_size")
then
$edi.setValue("2"); update($edi);
end

Drools rule with not condition with multiple condition causing error

When i use below condition with 'not' I am getting an error.
not(Obj1(value == 0) && Obj2(value <= 3))
However if i replace above condition as below I am not getting any casting exception
Obj1(value != 0) or Obj2(value > 3)
The rule looks like this:
rule "test_6"
salience 10
when
not(Obj1(value == 0) && Obj2(value <= 3))
then
.....
end
And this is the error I'm getting:
throwing error Error Message: org.drools.core.rule.GroupElement cannot be cast to org.drools.core.rule.Pattern
The && and || operators can only be used inside a single pattern. For example: Obj1( value > 3 && value < 10 || value == 0). According to the documentation, to separate Patterns, you have to use the and and or operators.
So, in your case, your rule should be:
rule "test_6"
salience 10
when
not(Obj1(value == 0) and Obj2(value <= 3))
then
.....
end
Note that it was not failing when you were using or because that was the right operator to use instead of ||.
Hope it helps,

Drools: how to use abbreviated condition notation together with further conditions?

Using Drools 6.5.0.Final
I want to use abbreviated combined relation condition (e.g. Person( age > 30 && < 40 )) in combination with additional conditions.
I tried it, but the resulting rules are executed more than once.
I have a small example, where temperature deviation from a setpoint is checked and the allowed deviations depend on the setpoint. The allowed deviations are configured with Param facts, see below.
If I execute the example (fire-all-rules):
rule 1 fires two times (bug?)
rule 2 fires once as expected (without abbreviated notation).
Is my usage of the abbreviated notation wrong or is this a bug?
Example rules:
declare Param
from : float
to : float
low : float
high : float
end
declare TemperatureEvent
#role( event )
id : String
setpoint : float
t : float
end
rule "Init abbreviated conditions test"
when
then
insert(new Param(0, 10, 1, 1));
insert(new Param(10,20, 2, 3));
insert(new TemperatureEvent("id1", 13.7f,11.5f));
// rule 1 and rule 2 should fire exactly once
end
rule "rule 1"
when
$p: Param()
$x : TemperatureEvent($p.from <= setpoint && < $p.to, (t < setpoint+$p.low || t > setpoint+$p.high))
then
System.out.println("rule 1: "+$x.getId()+" "+$x.getSetpoint()+" "+$x.getT());
end
rule "rule 2"
when
$p: Param()
$x : TemperatureEvent($p.from <= setpoint, setpoint < $p.to, (t < setpoint+$p.low || t > setpoint+$p.high))
then
System.out.println("rule 2: "+$x.getId()+" "+$x.getSetpoint()+" "+$x.getT());
end
The abbreviated restriction
$p.from <= setpoint && < $p.to
is equivalent to
$p.from <= setpoint && $p.from < $p.to
What you want is
setpoint >= $p.from && < $p.to

Optaplanner newbie: nurse softconstraint for weekends

I'm studying Optaplanner, and am doing some experiments with the Nursing Roster.
My goal, for this experiment, is simple: to have nurse "1" be more in favor, and more likely, to work weekends.
I have written the following rules to help make this happen:
rule "nurseNamed1WorksWeekends"
when
$oneNurse: Employee( name = "1")
$wk : ShiftAssignment( isWeekend = true)
then
scoreHolder.addSoftConstraintMatch(kcontext, 1);
end
rule "nurseNamed1MustNotWorkWeekdays"
when
$oneNurse: Employee( name = "1")
not $wk : ShiftAssignment( isWeekend = false)
then
scoreHolder.addSoftConstraintMatch(kcontext, 1);
end
However, after running the sample for some time, nurse "1" still never ends up working weekends.
What am I doing wrong?
Thanks
Edit of rule according to laune's suggestions but optaplanner is still reluctant to put the nurse on weekend shifts:
rule "nurseNamed1WorksWeekends"
when
$oneNurse: Employee( name == "1", )
$wk : ShiftAssignment( isWeekend == true, employee == $oneNurse)
then
scoreHolder.addSoftConstraintMatch(kcontext, 1);
end
rule "nurseNamed1MustNotWorkWeekdays"
when
$oneNurse: Employee( name == "1")
not ShiftAssignment( isWeekend = false, employee == $oneNurse)
then
scoreHolder.addSoftConstraintMatch(kcontext, 1);
end
Don't use = in your constraints - test for equality is expressed using ==.
If the getter for a boolean is called isWeekend, the constraint should be written as
ShiftAssignment( weekend == true )
ShiftAssignment( weekend == false )
or, (for me) preferably
ShiftAssignment( weekend )
ShiftAssignment( ! weekend )
A binding variable in a Conditional Element such as $wk in
not $wk : ShiftAssignment( ! isWeekend )
doesn't make sense. The rule fires if there is no such ShiftAssignment - and then what would $wk being bound to?
The CE
not ShiftAssignment( ! weekend )
is strange: the rule fires if and only if there is no ShiftAssignment for any weekday around at all - not likely.
Adding a value higher than one in the "WorksWeekends" rule should favour nurse 1 on weekends.
Later
rule dislikeNurseOneOnWeekdays
when
$oneNurse: Employee( name == "1")
ShiftAssignment( isWeekend = false, employee == $oneNurse)
then
scoreHolder.addSoftConstraintMatch(kcontext, -1);
end
Using a smaller value (e.g. -10) will make it even harder for the First Nurse to work on weekdays: ten shifts during the weekend are needed to balance one during the week.

How do I add a condition to an existing conditional expression?

I had a programmer write a Perl script for my site.
One of the functions is to update price/stock when a certain condition is met.
# update when price/stock conditions met
if ( ($force_price_updates == 1) ||
($data->{'price'} <= $product_price && $data->{'quantity'} > 0) ||
($product_quantity == 0 && $data->{'quantity'} > 0) ) {
What the above is not doing is not updating the price if the new price is higher. It updates the stock value, but if the new stock comes at a higher price, I lose out. Stock gets updated and but the price is not.
The script goes through a number of feeds and if the same product is found in any of the feeds, the script should amend price/stock change according to the rule above.
I can't find the programmer and my Perl knowledge is limited. I understand what the code is doing, but don't know what it should do if the price is higher and stock is greater than zero.
You can add the extra condition you're looking for to that statement.
The condition you're looking to match is:
$data->{'price'} > $product_price && $product_quantity > 0
So the final version would look like this:
if (($force_price_updates == 1) || ($data->{'price'} <= $product_price && $data->{'quantity'} > 0) || ($product_quantity == 0 && $data->{'quantity'} > 0) || ($data->{'price'} > $product_price && $product_quantity > 0)) {