Writing rules in Drools without using sailence -- checking not exists - drools

I am trying to write rules avoiding ordering. The scenario I have is --
rule "common rule"
when
// check if it is a "xyz" customer
then
insertLogical(new MyCondition("Is a xyz customer", true));
end
In the other rules, I make decisions based on above condition --
rule "rule1"
when
MyCondition("Is a xyz customer", true)
then
System.out.println("This is a xyz customer");
end
I have another rule --
rule "rule2"
when
not MyCondition("Is a xyz customer", true)
then
System.out.println("This is NOT a xyz customer");
end
As I am not using any sailence, in some cases, both my rule1 and rule2 are getting fired. Initially, there is no MyCondition object in knowledgebase, "rule2" is getting fired. Later, an object for MyCondition is being inserted and "rule1" is getting fired.
1. When I don't have rules that check "not" condition, everything works perfectly. I am having problems when I use the "not" exists condition. What are the ways to overcome this?

"I am trying to write rules avoiding ordering"
It sounds like you need to order your rules using salience in order to get the desired behavior.
I suggest that you use a higher salience on the rule(s) that insert a "MyCondition" fact than the rules that check for the presence or absence of a "MyCondition" fact.
For example:
rule "common rule"
salience 10
when
// check if it is a "xyz" customer
then
insertLogical(new MyCondition("Is a xyz customer", true));
end
rule "rule1"
salience 0
when
MyCondition("Is a xyz customer", true)
then
System.out.println("This is a xyz customer");
end
rule "rule2"
salience 0
when
not MyCondition("Is a xyz customer", true)
then
System.out.println("This is NOT a xyz customer");
end

Related

Drools: adding multiple groups in a rule

I need to assign more than one group in the agenda-group to a rule to be able to reuse it. For example, I would like to do something like this:
rule "Row 1 PromotionGlobalTest"
agenda-group "global", "group1" //I want to do something like this
enabled true
salience 0
dialect "mvel"
when
c : Contract( serviceType not in ( 2, 3 ) )
then
c.createMessage( "2, 3" );
end
The idea is that there will be groups that share rules and that if you want to execute the rules of "group1", those that are exclusive to this group and those that are shared with another group will be executed
Any idea how to achieve this?

Drools: How to get return values from Condition in Decision tables?

I have a Decision table in Drools and I'm trying to retrieve the return values of the Conditions (columns) that evaluates to see if a particular Rule (rows) is executed. May I know how if it's possible? Please find below for a simple example of my problem.
Condition 1 | Condition 2 | Condition 3 | Condition 4
Age < 60 Employed=Yes Owns a house=Yes Single=Yes
Rule 1: YES YES
Rule 2: YES NO YES
Rule 3: NO YES
Let's say if Rule 2 should be ideally executed and yet Rule 1 is executed, I would like to know the reason why Rule 1 was executed by obtaining the return values of the Condition 1 and 3 (whether it is true or false). Is there a way to do so?
The DRL rule for your Rule 1 would be something like
rule "Rule 1"
when
Person( $age: age < 60, $owner: owner == true )
then
...( $age, $owner )...
end
and on the right hand side you have binding variables $age and $owner set to the actual values as contained in the Person fact. You can use Java code in an ACTION column in a decision table to do whatever you want with those values.
Edit If you need the values for the negative case, you'll have to use another rule
rule "Rule 2"
when
Person( $age: age, $owner: owner,
$age >= 60 || $owner == false )
then
another_action...( $age, $owner )...
end
Most likely, you'll need to do some other action anyway.
Of course, individual rules for all four cases are also possible. Note that decision tables let you write rules for all four cases TT, TF, FT, FF. You can combine the action cells so you can define the action for TF FT FF in a single cell.

How to handle lots of rules with shared conditions in Drools

Let's supposed I'm working on a sales tax calculation application that has lots of rules for each of the states; e.g., I might have
rule "Florida Food Sales Tax"
when
$item : OrderItem( state == States.FL, category == Categories.FOOD )
then
modify($item) { setSalesTax(new BigDecimal("5") }
end
rule "Florida Non-Food Sales Tax"
when
$item : OrderItem( state == States.FL, category == Categories.NONFOOD )
then
modify($item) { setSalesTax(new BigDecimal("8") }
end
In this application I might have dozens, even hundreds of rules about Florida sales tax...and for every one of the other states as well. It gets tedious having to put the state condition in every rule. Is there a way to create some kind of nested context around rules so I only have to do that once for a whole block? Something like (and I know this isn't valid, consider this pseudo-code):
when $item : OrderItem( state == States.FL )
rule "Florida Food Sales Tax"
when
$item : OrderItem( category == Categories.FOOD )
then
modify($item) { setSalesTax(new BigDecimal("5") }
end
.
.
.
end
I know rules can extend other rules, but as far as I can tell they can only have one parent and I might want to split things up into multiple blocks; e.g., have sub-blocks for the food vs. non-food rules.
Extending rules can be continued over more than one level so that you could have a parent rule
rule "Florida Sales Tax"
when
$florida: OrderItem( state == States.FL )
then end
and at the next level two rules for food and non-food with a couple of rules like this one:
rule "Florida Food Sales Tax" extends "Florida Sales Tax"
when
$ffi: OrderItem( this == $florida, category == Categories.FOOD )
then end
Equally well, you could start the hierarchy by selecting the category first, but there's no way to inherit from more than one rule. (Besides, I doubt that the gain would be worth doing it.)
Note that you'll run into the same dilemma when using a decision table. Joining cells with the same value for a property is only possible if the resulting rules are placed next to each other - and you can't do this for all rules for more than one property at the same time.

Missing varibale binding in Drools guided decision table

I am using kie workbench's 6.2 guided decision table and I am stuck with the following scenario
I want to set the promocode in promotion only if the PromoCode from EligibilityCriteria is checked against the some values in the rule. like shown below in code generated by workbench from the decision table
//from row number: 1
rule "Row 1 test"
dialect "mvel"
when
$e : EligibilityCriteria( $code : PromoCode in ( "code1", "code2" ) , $make : vehMake == "BMW" )
then
Promotion p = new Promotion();
p.setPromoId("123");
p.setPromoCode($code);
insertLogical( p );
end
If no value is specified for the promocode on the text box on guided decision table the generated does not creates the variable $code and the code fails since $code is not created
//from row number: 1
rule "Row 1 test"
dialect "mvel"
when
$e : EligibilityCriteria($make : vehMake == "BMW" )
then
Promotion p = new Promotion();
p.setPromoId("123");
p.setPromoCode($code);
insertLogical( p );
end
I just want to somehow indicate whether the rule field for promocode is empty or it has some value.
If no value is given in the column where the list of PromoCode values is provided you need another rule, i.e., the right hand side cannot contain the same statement sequence. Omit the selection mark for the RHS action, to avoid the generation of a statement referencing $code and provide an alternative action which obtains the PromoCode value by accessing the fact:
p.setPromoCode($e.getPromoCode());
Note that you may use this form of the code in any case - so you might get by with a single action column.

Sorting a list of objects using Drools rules engine

I am trying to sort a list of objects using a set of rules defined in drools rules engine.
The sample object structure is as follows
public class A {
String name;
Date createdDate;
}
I want to
Define a set of rules for sorting a list of objects .
Ex : rule 1 : "Sort objects using name ascending"
       rule 2 : "Sort objects using createdDate descending"
Define the order which the rules needs to be executed .
Ex : set order 1 to rule 1
       set order 2 to rule 2
So the objects will be sorted by name ascending and createdDate descending.
Can I achieve this using the drools engine ?
I thought of using the compareTo() for the sorting but since the sorting criteria can be changed
at runtime the logic is becoming complex and hard to maintain.
Thanks,
Kolitha.
Drools does not sort objects the way quicksort or some similar sorting algorithm rearranges objects within an array or some other aggregate. What you can do is to have a rule fire, repeatedly, once for each fact from a set of facts (such as your class A objects) and with constraints guaranteeing this happening in a certain order. Also, you need to keep track of
facts that have already been processed.
Therefore, the question to be answered first is: why do you need the objects in a certain order?
If the facts need to be processed in this order, you don't have to sort them in the ususual sense of the word, and the aforementioned rule would be sufficient:
declare ListOfA
listOfA: List
end
rule noListOfA
when
not ListOfA()
then
ListOfA loa = new ListOfA();
loa.setListOfA( new ArrayList() );
insert( loa );
end
rule sortA
when
$a: A( $name: name, $createdDate: createdDate )
$loa: ListOfA( $listOfA: listOfA not contains $a )
not A( this != $a, this not memberOf $listOfA,
name < $name ||
name == $name && createdDate > $createdDate )
then
System.out.println( $a );
modify( $loa ){ getListOfA().add( $a ) }
end
This is the way to sort in drools.
rule "Rule 03"
when
$number : Number( )
not Number( intValue < $number.intValue )
then
System.out.println("Number found with value: " + $number.intValue() );
retract( $number );
end