Don'ing a Trait Looses the Map-Based Fact it Proxys - drools

We are running into an issue with Drools when we try to "don" a trait to a map-based model.
It appears that after don'ing the trait the map-based fact it is proxying disappears (or gets retracted).
There does not seem to be any issue using the trait properties to get to the model properties.
In the scenario below the expectation is:
A populated map-based fact is supplied to the engine.
We modify the map-based fact based on constraints in a rule.
We check the constraints against the map fact and apply the trait.
We use the trait and the model in subsequent rules.
In rule 5, regardless of whether I add a 'bmi' property to the trait and try to set that and/or put the property in the map-based fact, it still does not even get to this rule (or anything after rule 3).
We are trying to use the model, mostly for constraints and updates, then within the rules apply and use traits to give us a stronger-typed fact and easier rule constraining.
Test Case:
public class TraitDonningTest extends CommonTestMethodBase
{
#Test
public void should_see_map_after_don() throws IOException
{
final String rule = Files.asCharSource(new File("src/test/resources/rules/traits.drl"), Charset.defaultCharset())
.read();
KieSession ks = loadKnowledgeBaseFromString(rule).newKieSession();
TraitFactory.setMode(VirtualPropertyMode.MAP, ks.getKieBase());
MyModel model = new MyModel();
model.put("weight", 1.0);
ks.insert(model);
ks.fireAllRules();
//We should have an updated BMI here
assertEquals(3.0, model.get("bmi"));
}
/**
* The real model based off of a map
*/
public static class MyModel extends HashMap<String, Object>
{
}
}
Rules:
package drools
import TraitDonningTest.MyModel
declare MyModel
#Traitable
end
declare trait MyTrait
#PropertyReactive
height : Double
weight : Double
end
rule "1 - Update Model"
when
$model: MyModel(
this['weight'] != null,
this['height'] == null )
then
modify($model) {
put('height', 2.0)};
System.out.println("Rule 1");
end
rule "2 - Apply Trait"
dialect "mvel"
no-loop
when
$model: MyModel(
this['weight'] != null,
this['height'] != null)
then
System.out.println("Rule 2A: " + $model.get('weight') + " (before don)");
MyTrait trait = don($model, MyTrait.class);
System.out.println("Rule 2B: " + $model.get('weight') + " (after don)");
System.out.println("Rule 2C: " + trait.weight );
end
rule "3 - Check Trait"
dialect "mvel"
when
$trait : MyTrait()
then
System.out.println("Rule 3: " + $trait.weight);
end
rule "4 - Check Model"
dialect "mvel"
when
MyTrait()
$model: MyModel()
then
System.out.println("Rule 4A: " + $model.get('height'));
System.out.println("Rule 4B: " + $model.get('weight'));
end
rule "5 - Compute Map"
when
$model : MyModel()
MyTrait(
$height : height,
$weight : weight)
then
double compute = $height + $weight;
modify($model) {
put('bmi', compute)};
System.out.println("Rule 5: " + $model.get('bmi'));
end
rule "6 - Summarize Results"
when
$a: Number() from accumulate( MyModel(), count(1) )
$b: Number() from accumulate( MyTrait(), count(1) )
then
System.out.println("Model: " + $a);
System.out.println("Trait: " + $b);
end
Output:
Rule 1
Rule 2A: 1.0 (before don)
Rule 2B: 1.0 (after don)
Rule 2C: 1.0
Rule 3: 1.0
Model: 0
Trait: 1
More details and testing...
I tried a few different scenarios using a strong-typed class as the fact rather than the map.
If I replace the map-based fact with a strong-typed fact the output is what I would expect:
Rule 1
Rule 2A: 1.0 (before don)
Rule 2B: 1.0 (after don)
Rule 2C: 1.0
Rule 3: 1.0
Rule 4A: 2.0
Rule 4B: 1.0
Rule 5: 3.0
Model: 1
Trait: 1
If I make the strong-typed fact (class) extend HashMap, even though I have the properties in the trait, it reverts back to the non-working result set.
Rule 1
Rule 2A: 1.0 (before don)
Rule 2B: 1.0 (after don)
Rule 2C: null
Rule 3: null
Model: 0
Trait: 1

Related

Optaplanner: trying to understand unwanted pattern rule in nurse rostering example

I'm new to drools and java and I'm trying to understand how this rule from the nurse rostering example works, especially the first part about $pattern.
rule "unwantedPatternShiftType3DaysPattern"
when
$pattern : ShiftType3DaysPattern(
$dayIndex0ShiftType : dayIndex0ShiftType,
$dayIndex1ShiftType : dayIndex1ShiftType,
$dayIndex2ShiftType : dayIndex2ShiftType
)
PatternContractLine(
pattern == $pattern, $contract : contract
)
ShiftAssignment(
shiftType == $dayIndex0ShiftType,
contract == $contract,
$employee : employee, $firstDayIndex : shiftDateDayIndex
)
ShiftAssignment(
shiftType == $dayIndex1ShiftType,
employee == $employee,
shiftDateDayIndex == ($firstDayIndex + 1)
)
ShiftAssignment(
shiftType == $dayIndex2ShiftType,
employee == $employee,
shiftDateDayIndex == ($firstDayIndex + 2)
)
then
scoreHolder.addSoftConstraintMatch(kcontext, - $pattern.getWeight());
end
Specifically, how does drools know what value is in: dayIndex0ShiftType, dayIndex1ShiftType, dayIndex2ShiftType? It calls the ShiftType3DaysPattern class with these values, but how are these values determined?
In addition, when it makes this call:
ShiftType3DaysPattern (dayIndex0ShiftType, dayIndex1ShiftType, dayIndex2ShiftType)
which refers to the following:
#XStreamAlias("ShiftType3DaysPattern")
public class ShiftType3DaysPattern extends Pattern {
private ShiftType dayIndex0ShiftType;
private ShiftType dayIndex1ShiftType;
private ShiftType dayIndex2ShiftType;
public ShiftType getDayIndex0ShiftType() {
return dayIndex0ShiftType;
}
public void setDayIndex0ShiftType(ShiftType dayIndex0ShiftType) {
this.dayIndex0ShiftType = dayIndex0ShiftType;
}
public ShiftType getDayIndex1ShiftType() {
return dayIndex1ShiftType;
}
public void setDayIndex1ShiftType(ShiftType dayIndex1ShiftType) {
this.dayIndex1ShiftType = dayIndex1ShiftType;
}
public ShiftType getDayIndex2ShiftType() {
return dayIndex2ShiftType;
}
public void setDayIndex2ShiftType(ShiftType dayIndex2ShiftType) {
this.dayIndex2ShiftType = dayIndex2ShiftType;
}
#Override
public String toString() {
return "Work pattern: " + dayIndex0ShiftType + ", " + dayIndex1ShiftType + ", " + dayIndex2ShiftType;
}
}
Is this shorthand for ShiftType3DaysPattern.getDayIndex0ShiftType, ShiftType3DaysPattern.getDayIndex1ShiftType, and ShiftType3DaysPattern.getDayIndex2ShiftType?
And if this is the case, how does ShiftType3DaysPattern know which pattern to return if there are more than one "3 day patterns" in the xml source files? What am I missing?
Furthermore, if there are more than one "3 day pattern" then, how does drool automatically apply this rule to all of these "3 day patterns"?
Specifically, how does drools know what value is in: dayIndex0ShiftType, dayIndex1ShiftType, dayIndex2ShiftType? It calls the ShiftType3DaysPattern class with these values, but how are these values determined?
Every ShiftType3DaysPattern in your problem fact collection will be evaluated against this rule in combination with the other conditions in the when clause. So every combination of a ShiftType3DaysPattern, PatternContractLine, and three ShiftAssignments available as problem fact in the Kie session will be evaluated. If all the conditions match (so the PatternContractLine matches the ShiftType3DayPattern and all three ShiftAssignment types are assigned in the order of the unwanted ShiftType3DayPattern, the rule will be fired and the score will be impacted negatively. So the value of dayIndex0ShiftType, dayIndex1ShiftType and dayIndex2ShiftType will be whatever is set in the ShiftType3DaysPattern in the problem fact collection.
Is this shorthand for ShiftType3DaysPattern.getDayIndex0ShiftType,
ShiftType3DaysPattern.getDayIndex1ShiftType, and
ShiftType3DaysPattern.getDayIndex2ShiftType?
I'm not sure what you are referring to exactly, but if you are referring to this:
$pattern : ShiftType3DaysPattern(
$dayIndex0ShiftType : dayIndex0ShiftType,
$dayIndex1ShiftType : dayIndex1ShiftType,
$dayIndex2ShiftType : dayIndex2ShiftType
)
This is just syntax for assigning dayIndex0ShiftType to the variable $dayIndex0ShiftType so it can be referred to in the rule later. The pattern itself is also assigned to a variable $pattern. The dollar sign itself is just a convention.
And if this is the case, how does ShiftType3DaysPattern know which pattern to return if there are more than one "3 day patterns" in the xml source files? What am I missing?
Furthermore, if there are more than one "3 day pattern" then, how does drool automatically apply this rule to all of these "3 day patterns"?
As I said before, the rule will be evaluated for every problem fact combination available, so every ShiftType3DaysPattern will be evaluated against the other facts stated in the when clause of the rule.
I recommend you to read the Drools documentation, it will help you to improve your basic understanding of Drools. It is a long read, but will be worth it. At least read the User guide chapter.

Drools dynamic salience

global Integer rank1;
global Integer rank2;
rule "Data"
salience 10
when
req : Requests(status == "ON")
then
drools.getWorkingMemory().setGlobal("rank1", 8);
drools.getWorkingMemory().setGlobal("rank2", 6);
end
rule "1" extends "Data"
salience 8
when
req1 : Requests()
then
System.out.print("1");
end
rule "2" extends "Data"
salience 6
when
req2 : Requests()
then
System.out.print("2");
end
Here rank1 and rank2 are initialised as 0 in java code.
the output is 11112222.
i want the same output but instead of using static salience, i want it dynamic.
rule "1" extends "Data"
salience rank1
.......
but the code output is 21212121.
You need to set the globals before you insert the facts. Globals are not evaluated dynamically.
If you really need a more dynamic salience you should create a class
class Salience {
private int rank1;
private int rank2;
// ...
}
and insert a fact with appropriate values and change them as required. You'll then need the pattern added to the root rule:
rule "Data"
when
Salience( $rank1: rank1, $rank2: rank2 )
Requests(...)
then
and you can use $rank1 and $rank2 in salience attributes.
Note: Almost certainly there is a solution to your problem that can be expressed in logic and without salience, the usage which is almost always a design flaw.

EqualsIgnoreCase on Drools

I am trying to rewrite my drl from using regex to equalsIgnoreCase as I think its faster. I am not sure its faster though. However, drools doesn't like it for some reason and I get unknown error.
The one on top works, but the one using equalsIgnoreCase doesn't
rule "name"
salience 0
activation-group "flow"
dialect "mvel"
no-loop true
when
$vurderinger: Vurderinger(vurdering1909 != null &&
vurdering1909.verdi matches "(?i)^FOO$")
then
modify( $vurderinger ) { setVurdering1913(new DroolsType("SHOW")) }
end
rule "name"
salience 0
activation-group "flow"
dialect "mvel"
no-loop true
when
$vurderinger: Vurderinger(vurdering1909 != null &&
eval("FOO".equalsIgnoreCase(vurdering1909.verdi)))
then
modify( $vurderinger ) { setVurdering1913(new DroolsType("SHOW")) }
end
Can anyone spot the mistake?
Within eval, stick to Java: refer to bound variables, use getter.
when
$vurderinger: Vurderinger($v: vurdering1909 != null &&
eval("FOO".equalsIgnoreCase($v.getVerdi())))
then
Edit Not knowing the class definition, the error and the version, I advise using eval/Java, to be on the safe side, no matter what the Drools version is. For 6.3.0, you can omit eval, and it works.
when
$vurderinger: Vurderinger(vurdering1909 != null &&
"FOO".equalsIgnoreCase(vurdering1909.verdi))
then
I have given solution for decision table i.e:
javaObject.getRisk().equalsIgnoreCase("$param")
for drools rules
rule "Rule To Check String Contains"
when
Pojo(name.contains("Loans"))
then
System.out.println(drools.getRule().getName());
end
rule "Rule To Check String Equals"
when
Pojo(name.equals("Personal Loans, Insuarnce"))
then
System.out.println(drools.getRule().getName());
end
rule "Rule To Check String EqualsIgnoreCase"
when
Pojo(name.equalsIgnoreCase("Personal loans, insuarnce"))
then
System.out.println(drools.getRule().getName());
end```

Drools ignores the rest of rules if one of rules didn't pass

I'm starting a project with Drools. My project's drl file looks like:
rule "Rule 1"
when
Item( id > 100)
// consequence implementation
rule "Rule 2"
when
Item( type == "phone")
// consequence implementation
rule "Rule 3"
when
Item( screenSize == 5)
// consequence implementation
rule "Rule 4"
when
Item( branch == "motorola")
// consequence implementation
rule "Rule 5"
when
Item( price > 200)
// consequence implementation
When Drools runs I have 5 fired rules. If an item don't pass one of 5 fire rules, I want Drools will ignore the rest 4 fired rules. Does Drools have feature for my purpose?
Another important thing is my Drools runs with AgendaFilter. Does that feature have AgendaFilter support?
First, the phrase in the title "rest of rules" seems to indicate that you think that the rules are evaluated in a certain order (e.g. as written in the file). This is not so: there is no order. Thus, if rules #1 and #2 fired (ìd > 0 and type == "phone") and screenSize != 5, rules #4 and rules #5 will still fire, or they may already have been fired.
To discover whether some fact whose class is referenced in rules 1 to 5 participates in all or some or none of these rules you'll have to create some logic of your own. For instance, declare a type/class with
class Marker {
Set<Integer> rules;
TheClassOfYourItem item;
//...
}
and insert this along with an item. Each rule would then, when firing, add its number to the set.
rule "Rule 1"
when
$item: TheClassOfYourItem(...)
$m: Marker( item == $item )
then
$m.add( 1 );
end
And a rule with low salience can then check whether the set has all/some/none elements.
Edit due to some comment: If no rule should fire unless all rules can fire: simply put all conditions into a single rule and combine all consequences.
rule "Rule 1-5"
when
Item( id > 100, type == "phone". screenSize == 5,
branch == "motorola", price > 200)
then
// ... Do what needs to be done if all five conditions are true
end

Can I not use contains operator on a collection assigned to a variable?

I would appreciate it if someone could explain to me why this is illegal:
rule "some rule name"
when
$a : A($bset : bset)
$bset contains B(x == "hello")
then
//do something
end
Where:
public class A {
private Set<B> bset = new HashSet<B>();
//getters and setters for bset
//toString() and hashCode for A
public static class B {
private String x
//getters and setters for x
//toString() and hashCode() for B
}
}
The error from the Drools eclipse plugin is not very helpful. It provides the following error:
[ERR 102] Line 23:16 mismatched input 'contains' in rule "some rule name"
The error appears on the line with "bset contains..."
I have searched through the Drools documentation, as well as a book that I have, and have not found the examples to be very illustrative in this regard.
'contains' is an operator that must be used inside a pattern. $bset contains B(x == "hello") is not a valid pattern in this case.
There are a couple of ways to achieve what you are trying to do. This is one of them:
rule "some rule name"
when
$a: A($bset : bset)
$b: B(x == "hello") from $bset
then
//you will have one activation for each of the B objects matching
//the second pattern
end
Another:
rule "some rule name"
when
$a: A($bset : bset)
exists (B(x == "hello") from $bset)
then
//you will have one activation no matter how many B objects match
//the second pattern ( you must have at least one of course)
end
If you want to see how contains operation is used, and if the B objects are also facts in your session, you can write something like this:
rule "some rule name"
when
$b: B(x == "hello")
$a: A(bset contains $b)
then
//multiple activations
end
or:
rule "some rule name"
when
$b: B(x == "hello")
exists( A(bset contains $b) )
then
//single activation
end
Hope it helps,