Learn Drools (Part 7): Salience
Salience is how Drools determines priorities for your rules. You can use it to filter Drools facts down and lower the overhead of your system.
Join the DZone community and get the full member experience.
Join For FreeLet's quickly summarize how Drools works. Rules are written on .drl files, facts or POJOs are inserted in the Knowledge Base, then rules are applied on each fact. If a "When" part of a rule satisfies a fact, the "Then" part will execute.
Having said that, one question just popped into my mind. If multiple rules match a fact, in which order will they be fired?
The answer is that they will fire arbitrarily. There is no sequence for executing rules. So, if we need a sequence or want a rule to fire first, how do we go about doing that?
Setting a Priority
In Drools, the answer is salience. Salience is a keyword in the .drl file that we can assign a positive or negative number. The number determines the salience priority. A higher number denotes a higher priority, so rules with a higher salience will be executed first by the Drools engine.
So, if Rule 1 has a salience of 10, Rule 2 has a 1, and Rule 3 has a 5, the order should be:
Rule 1>Rule 3>Rule 2.
The default salience value is 0. So, if we do not mention the salience, all rules get that default figure.
Conflicting Priorities
If multiple rules have the same salience, there won't be an execution order among them, but the engine will execute them at the same level.
So If Rule 1 has salience 10, Rule 2 has 10, and Rule 3 has 5, then Rule 1 or 2 will be executed first, followed by Rule 3. We cannot say which order Rule 1 or Rule 2 will be executed — as they have the same salience value — but we can say that they will execute before Rule 3.
Negative Salience
It is possible to assign negative value in Salience. But the question is why and when to use a negative value.
The answer is very simple. If we want a rule to be fired last, we provide a negative value. Just think, if salience only accepted positive values, then with a default value of 0, you would have to change the salience of each one in order to guarantee that one would fire last.
Think if a .drl file had 500 entries. We would have to provide positive salience for each rule. But because a negative value is accepted, we just define the rule with negative salience in order to execute last.
Solving Problems With Salience
Let's see salience in action.
Say we want to give a laptop to managers in IT department?
One way to approach this problem is to write a rule to check that if an employee's department is IT and their designation is "manager", then they get a laptop. But this is not efficient. Our setup will run through every fact irrespective of the department. If you've got a huge number of facts, that's going to cause problems.
But we can view this problem in another way. We can filter out facts based on the department first, then based on job designation, then give a laptop to the remaining facts.
So first, give a higher salience to rules that filter departments. Here, we will check if the department is IT. Then, give medium salience to the designation rule, where we check if an employee has been desigated as a manager. Finally, give the lowest or default saliance to the rule that gives laptops out so that it fires last — and only fires on the facts that have been filtered.
Let's see it in action.
First, we'll check the Drools file employeeSalience.drl:
package com.rules
import com.example.droolsExample.pojo.Employee
import com.example.droolsExample.pojo.Department
import com.example.droolsExample.pojo.ITManager
rule "Not IT" salience 10
lock-on-active
when
$emp: Employee("IT" != dept.getName());
then
System.out.println("filter Dept ::" + $emp.getName());
$emp.setFilter(true);
update($emp);
end
rule "Not Manager" salience 5
lock-on-active
when
$emp: Employee(manager==false);
then
System.out.println("filter Manger ::" + $emp.getName());
$emp.setFilter(true);
update($emp);
end
rule "give Manager Laptop"
when
$emp: Employee(filter==false);
then
$emp.setMessage("Give Laptop");
System.out.println($emp.getName()+ ": "+$emp.getDept().getName()+ ":"+$emp.getMessage());
end
And here's our employee object:
package com.example.droolsExample.pojo;
public class Employee {
String name;
boolean manager;
String message;
Department dept;
boolean filter;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isManager() {
return manager;
}
public void setManager(boolean manager) {
this.manager = manager;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
public boolean isFilter() {
return filter;
}
public void setFilter(boolean filter) {
this.filter = filter;
}
@Override
public String toString() {
return "Employee [name=" + name + ", manager=" + manager + ", message="
+ message + ", dept=" + dept + "]";
}
}
And our department object:
package com.example.droolsExample.pojo;
public class Department {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And our DroolsTest Java file:
package com.example.droolsExample;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List;
import org.drools.compiler.compiler.DroolsParserException;
import org.drools.compiler.compiler.PackageBuilder;
import org.drools.core.RuleBase;
import org.drools.core.RuleBaseFactory;
import org.drools.core.WorkingMemory;
import org.drools.core.event.AgendaEventListener;
import com.example.drools.listener.TrackingRuleFiredEventListener;
import com.example.droolsExample.pojo.Department;
import com.example.droolsExample.pojo.Employee;
public class DroolsTest {
public static void main(String[] args) throws DroolsParserException,
IOException {
DroolsTest droolsTest = new DroolsTest();
//droolsTest.executeDrools();
droolsTest.executeDroolsEmployee("/com/rules/employeeSalience.drl");
}
public void executeDroolsEmployee(String ruleFile) throws DroolsParserException, IOException {
PackageBuilder packageBuilder = new PackageBuilder();
//String ruleFile = "/com/rules/employee.drl";
InputStream resourceAsStream = getClass().getResourceAsStream(ruleFile);
Reader reader = new InputStreamReader(resourceAsStream);
packageBuilder.addPackageFromDrl(reader);
org.drools.core.rule.Package rulesPackage = packageBuilder.getPackage();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(rulesPackage);
WorkingMemory workingMemory = ruleBase.newStatefulSession();
Department dep = new Department();
dep.setName("Civil");
Department dep1 = new Department();
dep1.setName("IT");
Employee emp = new Employee();
emp.setName("Shamik Mitra");
emp.setManager(true);
emp.setDept(dep1);
Employee emp2 = new Employee();
emp2.setName("Swastika Mitra");
emp2.setManager(false);
emp2.setDept(dep1);
Employee emp1 = new Employee();
emp1.setName("Samir Mitra");
emp1.setManager(true);
emp1.setDept(dep);
workingMemory.insert(dep);
workingMemory.insert(dep1);
workingMemory.insert(emp);
workingMemory.insert(emp1);
workingMemory.insert(emp2);
workingMemory.fireAllRules();
}
}
And our output:
filter Dept ::Samir Mitra
filter Manager ::Swastika Mitra
Shamik Mitra: IT:Give Laptop
Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments