I have a drl file which has rules inside 2 ruleflow-groups: "first-ruleflow-group" and "second-ruleflow-group" . The activation of these groups depend on "rule A" and "rule B". Is there any way in which I can deactivate rule B to fire when rule A condition matches, so that the focus is set only to "first-ruleflow-group"?
rule "rule A"
when
eval(true);
then
drools.setFocus("first-ruleflow-group");
end
rule "rule B"
when
eval(true);
then
drools.setFocus("second-ruleflow-group");
end
Change your rules rely on exclusive conditions.
Simple example. Let's say we have an application dealing with calendar events. We have a rule flow for public holidays. We have a rule flow for religious holidays. There are some religious holidays which are also public holidays; for these we only want to fire the public holiday rules.
rule "Public Holidays"
when
Holiday( isPublic == true )
then
drools.setFocus("publicholiday-flow");
end
rule "Religious Holidays"
when
Holiday( isReligious == true,
isPublic == false )
then
drools.setFocus("religiousholiday-flow");
end
So, basically, modify your "rule B" to include a condition that negates the rule A condition, so that if Rule A matches, Rule B necessarily does not.
A second solution would be to have a rule in your first rule flow (triggered by A) set a condition such that rule B doesn't trigger. It's basically similar to the previous solution except that the condition keeping Rule B from triggering is dynamic.
As an example, imagine an application that determines how much someone owes for parking. Parking rates are set by day -- there's one rate for Monday through Friday, a rate for Saturday, and Sundays are free. In addition, there are other discounts applied -- for example senior citizens, or if the total amount time parked is less than 5 minutes. The day-of-the-week rates are determined using the first rule flow (A), and the discounts are determined using the second rule flow (B). If it is Sunday, there's no reason to fire the second set of rules.
You could either write the discount rule flow trigger to explicitly not fire for Sundays, or you could have the Sunday rate rule retract or insert data such that it makes the discount rules no longer valid for running.
rule "Day Rates"
when
then
drools.setFocus("dayrates-flow");
end
rule "Discounts"
when
exists(Rate( hourly > 0 ))
then
drools.setFocus("discounts-flow");
end
// The Sunday rule, part of the dayrates-flow, sets hourly = 0, which makes
// the "Discounts" rule no longer valid to fire.
rule "Sunday Rate"
ruleflow-group "dayrates-flow"
when
not(Rate())
Request( day == DayOfWeek.SUNDAY ) // when parking on Sunday...
then
Rate rate = new Rate();
rate.setHourlyRate(0.0);
insert(rate);
end
Yet another option would be to trigger the second rule flow from within the first rule flow, but only as needed.
Reusing the previous example with parking:
rule "Day Rates"
when
then
drools.setFocus("dayrates-flow");
end
// Here are some day rate rules. Most are omitted for brevity. We include three (3)
// to show regular rates, the special case of Sunday, and how we trigger the discount
// rate rules
rule "Saturday Rate"
ruleflow-group "dayrates-flow"
when
not(Rate())
Request( day == DayOfWeek.SATURDAY )
then
Rate rate = new Rate();
rate.setHourly(1.0); // Saturday rate: $1/hr
insert(rate);
end
rule "Sunday Rate"
ruleflow-group "dayrates-flow"
when
not(Rate())
Request( day == DayOfWeek.SUNDAY )
then
Rate rate = new Rate();
rate.setHourlyRate(0.0); // Sunday rate: free
insert(rate);
end
// This rule only triggers the next rule flow when the rate is positive (eg not Sunday)
rule "Transition to Discounts"
ruleflow-group "dayrates-flow"
when
exists(Rate( hourly > 0 ))
then
drools.setFocus("discount-flow");
end
Related
I have the following 2 rules in my system. I am using 7.73.0. It is being run in a stateful session.
Rule 11 will fire but Rule 10 is not fired as a result of Rule 11. I would expect rule 11 to fire and then rule 10 because of the update statement.
I have debugged my code with a breakpoint on the setValue("A") in rule 11 and see that the method is being called and the value is set properly.
Can anyone tell me why rule 10 will not fire as a result of the update statement?
rule "MYRULES_10"
when
prod : Product(sku != "1")
$txt1 : TextOption(sku == "mm", value == "A")
then
prod.setOptionEnabled( "to2", false, "Not available rule 10" );
end
// rule values at B11, header at B5
rule "MYRULES_11"
when
prod : Product(sku != "1")
$opt1 : TextChoiceOption(sku == "mt")
exists (OptionValue(optionValueValue in ("val1")) from $opt1.value)
$txt1 : TextOption(sku == "mm")
then
$txt1.setValue("A"); update($txt1);
end
additional info:
If I fireAllRules on the session immediately after the initial fireAllRules, Rule 10 still will not fire.
If I take the result of the first stateful session and put all the facts into a second session and fireAllRules then rule 10 is fired and then rule 11 is fired.
If I put the fact in initially with fact mm having value A then rule 10 fires first and then rule 11 will fire.
Are you using a Stateless Session or Sequential Mode for your session? If yes, then this is the expected behaviour. https://docs.drools.org/8.31.0.Final/drools-docs/docs-website/drools/rule-engine/index.html#phreak-sequential-mode-con_rule-engine
Another option is that property reactivity is now working as expected because you are using the discouraged update() function to let Drools know about the modification of your facts. Try to use modify() instead:
...
then
modify($txt1){
setValue("A")
}
end
Need help with calculation in Tableau. I have field as priority High, Medium and Low.
For High and medium i have the TAT as 2 hours and for low it is 4 hours.
I need to add a calculation which for each ticket based on their priority level shows respective priority hours as either 2 or 4 hours.
I have added a calculation :
if [Priority]= "High" then "2 Hours"
ELSEIF [Priority]= "Medium" then "2 Hours"
ELSE "4 Hours"
END
however this is a string whereas i need it in hour format so that i can add a flag whether ticket missed or met the SLA.
This i would do basis another column which is time taken to acknowledge.
Let’s consider Start Time = 07/06/2017 5:30:00 AM and End Time = 07/06/2017 8:40:00 AM
Create a calculated field (time_diff_seconds) to calculate time difference between start & end time
(DATEDIFF('hour’,[Start Time],[End Time]) * 3600) + (DATEDIFF(‘minute’,[Start Time],[End Time]) * 60) + DATEDIFF(’second’,[Start Time],[End Time])
Now coming back to your calculated field (SLA_seconds) definition, I would modify it to look something like:
IF [Priority]= "High" then 2*3600
ELSEIF [Priority]= "Medium" then 2*3600
ELSE 4*3600
END
Then finally create your flag as another calculated field i.e. SLA_met
IF time_diff_seconds <= SLA_seconds THEN “Y”
ELSE “N”
END
Hope this helps! Pls don't forget to mention if it solves your problem :)
I'm new to Drools and have hit a problem.
I've simplified the rule to exhibit the problem:
rule "test"
when
$ev : TestEvent()
$evList : ArrayList( size >= 3 ) from collect
(
TestEvent(linkId == $ev.getLinkId())
)
then
System.out.println("Rule fired!")
end
Basically, I want to count Events occurring on a particular Link (a Link is a section of road). When 3 events occur on the same Link I want the rule to fire.
The rule above is almost working, but when it fires, it fires 3 times, once for each event. I only want it to fire once.
What am I missing?
Many thanks in advance.
The first pattern picks any TestEvent irrespective of its linkId. If there are n TestEvent facts with a certain linkId, the acivation proceeds n times.
To restrict this rule to fire once you could select a single TestEvent out of any such group of n. Any attribute with a unique ordered value may be used, and if you have events also the event timestamp is available.
rule "test"
when
$ev: TestEvent( $lid: linkId )
not TestEvent( linkId == $lid, this before $ev )
$evList : ArrayList( size >= 3 ) from collect
(
TestEvent(linkId == $lid)
)
then
System.out.println("Rule fired!")
end
I got this working by changing my approach to the problem. I've created Link objects now and then tie the events back to the Link.
The rule ends up
rule "test"
when
$link : Link()
$events : ArrayList( size >= 3 ) from collect (TestEvent(link == $link))
then
System.out.println("Rule fired!")
end
This only fires once per link which is what I need.
I am currently writing an application for course scheduling using OptaPlanner and Drools.
One of our rules written in Drools collects Entries (time slots of a schedule) of the same day into an ArrayList.
I noticed during testing our rules that this rule fired multiple times, namely the exact amount of entries that are found by collecting them.
I assume the reason why this rule fires so many times is due to recombination of the entries that are found (aka backtracking)
ie. when there are 10 entries on the same day, this rules fires 10 times
Is there any way to cancel this behaviour?
In case it might help to reason about my problem, here's the rule of which I'm talking about:
rule spareHoursViolated
when
$traject : Traject()
Date($day := day, $month := month, $year := year)
$lecturesOnSameDay: ArrayList() from collect
(Entry($day := startingDate.day,
$month := startingDate.month,
$year := startingDate.year, courseComponent.course
memberOf $traject.courses))
then
sort($lecturesOnSameDay);
scoreHolder.addSoftConstraintMatch(kcontext,
checkSpareHoursAndNoonBreak($lecturesOnSameDay));
end
By implementing checkSpareHoursAndNoonBreak() in the RHS side, it doesn't do delta based score calculation as much as it could. (See docs section on incremental / delta based score calculation).
I'd try to write the rule something like this:
when
$t : Traject()
$l1 : Lecture(traject == $t, $d : day, $p1 : period)
// Another lecture on the same day, but after the original one
$l3 : Lecture(traject == $t, day == $d, period > $p1, $p3: period)
// No lecture in between
not Lecture(traject == $t, day == $d, period > $p1, period < $p3)
// It's not lunch
eval(!Utils.isLunch($l1, $l3)) // Even better is to move this into the $l3 Lecture part
then
// It's a spring hour
scoreHolder.addSoftConstraintMatch(kcontext,
// Punish more spring hours more
- ($p3.index - $1.index));
end
I am trying hands on at the Drools rule engine , I am quite a beginner.
I have the following rules in place in a single rule file:
rule "A stand alone rule"
salience 2
no-loop
when
$account : Account()
Account($account.balance>100)
then
System.out.println("balance>100");
System.out.println($account.getBalance());
System.out.println($account.getCustomer().getName());
end
rule "A second Rule"
salience 1
no-loop
when
$account : Account()
Account($account.balance<100)
then
System.out.println("balance<100");
System.out.println($account.getBalance());
System.out.println($account.getCustomer().getName());
end
In the StatefulKnowledgeSession I am passing TWO accounts , one with balance 15000 another with balance 15 ,
Account account=new Account(7l,15000l);
Account account1=new Account(5l,15l);
Customer customer = new Customer("Samrat", 28, "Sector51", account);
Customer customer1 = new Customer("Alexi", 28, "Sector50", account1);
account.setCustomer(customer);
account1.setCustomer(customer1);
session.insert(account);
session.insert(account1);
session.fireAllRules();
According to me the expected result should be that each rule should be fired only once and the corresponding object should be printed.
But the result I am getting is :
balance>100
15000
Samrat
balance>100
15000
Samrat
balance<100
15
Alexi
balance<100
15
Alexi
I am not able to understand why each rule is running twice ????
Using multiple patterns (and not specifying any relation between them) will create a full Cartesian product (just like a select on multiple tables without a join clause).
So, the rule:
rule A
when
Account()
Account()
then
...
end
will be activated N^2 times for N objects of type Account.
One solution could be to use the magic field 'this' to specify that the second account is the same as the first one:
rule A
when
$a: Account()
Account(this == $a)
then
...
end
But, going back to your example, I think you don't even need to use 2 different patterns. You could rewrite your rules as following:
rule "A stand alone rule"
salience 2
no-loop
when
$account: Account(balance>100)
then
System.out.println("balance>100");
System.out.println($account.getBalance());
System.out.println($account.getCustomer().getName());
end
rule "A second Rule"
salience 1
no-loop
when
$account: Account(balance<100)
then
System.out.println("balance<100");
System.out.println($account.getBalance());
System.out.println($account.getCustomer().getName());
end
Hope it helps,
I was comparing two objects of same class and was wondering why the rules are getting fired multiple times. However after reading explanation from Esteban Aliverti I thought that my rule might also be creating Cartesian product.
So I replaced "and" from the rules to "," and it worked perfectly. However, I could not understand why "and" was creating Cartesian product.
Earlier my rule was --
rule "Rule 1"
when
$first : RuleC() and
second : RuleC(this != $first) and
RuleC($first.outcome < outcome) and
RuleC($first.target == target)
then
System.out.println("The rule has been fired ");
end
Later my rule became (And it is working absolutely fine) --
rule "Rule 1"
when
$first : RuleC() and
second : RuleC(this != $first, $first.outcome < outcome, $first.target == target)
then
System.out.println("The rule has been fired ");
end