Drools - 如何访问 THEN 子句中的对象
Drools - How to access an object in THEN clause
我有一个场景,当输入中的数据针对一个事实具有不同的列,即转置数据,例如
EmployeeID | EmpDept1 | EmpDept2 | EmpDept3 | EmpDept4 | EmpDept5
现在我也有一个查找部门table,例如
DeptId | DeptName
我要进行查找,一种方法是创建 5 条规则,例如:
rule "Lookup dept-1 data"
when
e: EmployeeData()
d: DeptData(deptId == $e.EmpDept1)
then
System.out.println(e.toString());
end
这里的问题是我必须创建 5 个规则(在我的例子中还有更多)。有没有一种方法可以访问 "THEN" 子句中的 DeptData,然后编写 JAVA 查找?
或者如果有其他好的方法,请告诉我。
谢谢!
所以基本上你有一个员工,他们的部门可以列在五个地方中的任何一个。解决这个问题的最好方法是在触发规则之前弄清楚部门所在的位置,然后修改传递给规则的对象。只要它们序列化正常,您就不会限制将哪种对象传递给规则;如果 EmployeeData 当前是来自数据库调用或其他东西的原始结果,请将其中您需要的部分转换为新的 POJO 并将其传递到规则中。
所以对于你的例子,我会做类似的事情:
class Employee {
private Integer employeeId;
private Integer departmentId; // assuming the only thing you need out of Department is id
public static Employee fromEmployeeData(EmployeeData data) {
Employee employee = new Employee();
employee.setEmployeeId(data.getEmployeeId());
if (data.getDept1() != null) {
employee.setDepartmentId(data.getDept1());
} else if (...) {} // other else-if omitted for brevity
return employee;
}
// getters and setters here
}
public void invokeRules(EmployeeData data) {
KieSession kieSession = ...;
Employee employee = Employee.fromEmployeeData(data);
kieSession.insert( employee );
kieSession.fireAllRules();
}
然后你可以编写关闭部门 id 的规则:
rule "Employee is in department 6"
when
Employee( departmentId == 6 )
then
// do something
end
或者,您可以 从 when 子句中调用 Java 方法。假设我们创建了一个名为 EmployeeUtils
的 class,它具有从 EmployeeData 解析部门的方法:
class EmployeeUtils {
public static Integer resolveDepartment(Employee e) {
Integer deptId = null;
if (e.getDept1Id() != null) {
deptId = e.getDept1Id();
} else if (...) {} // other else-if omitted for brevity
return deptId;
}
}
然后您可以像这样在左侧调用此方法:
rule "Employee in department 6"
when
$employee: Employee()
Integer(this == 6) from EmployeeUtils.resolveDepartment($employee)
then
// do something
end
显然您也可以在右侧调用它:
rule "Example"
when
$employee: Employee()
then
System.out.println("Employee is in department " + EmployeeUtils.resolveDepartment($employee));
end
一般 不推荐 从左侧调用 Java 方法,因为这样的调用无法优化.当您像 Employee(department == 6)
那样进行过滤时,Drools 可以对断言进行大量优化以使您的规则触发得更快。但是,如果您正在调用 Java 方法,那对于 Drools 引擎来说就像一个黑匣子;它不知道 Java 方法将要做什么,所以它必须让它按原样评估,而不是做一些工作来让它更快。
此外,通常也不建议将任意数据传递到规则集中。 只需传入您需要的数据 规则即可触发。从另一个数据源转换时,例如。从 SQL 查询结果或从电子表格转换,解析您实际需要的其他数据部分,并将解析的输入对象传递到规则中。
我在一家将数据库结果直接传递到规则中的公司工作。逻辑是代码已编译,因此如果您需要对规则输入对象进行更改,则需要重新编译代码并发布新版本。但是数据库模式和规则可能在编译后的代码之外,因此它们可以在不需要重新编译的情况下进行更改,因此只需提供所有数据即可,而不必为新版本而烦恼。当公司是新公司并且我们传递的数据只是几个连接表时,这很有效;到我离开时,我们正在传递数百个数据表,在内存中总计达数百 KB,尽管规则只需要可能五个表中的几位数据。它既缓慢又浪费。不要走这条路。
像这样,您有一个 Employee 并且有一个 Department 外部数据——加入这两者是在(例如)SQL 查询中最佳完成的事情。这种关系连接实际上通常不需要对其做出任何决定。如果它确实需要决策(例如,如果部门代码在 Dept5 中,那么它的含义与在 Dept2 中的含义不同),那么多个规则 are 实际上是合适的。但是,当它只是一个相同逻辑的问题但可能在 5 个地方时?不要费心按照规则去做;没有任何好处。在 Java 中执行,然后将结果传递到规则中以进行断言。
我使用 "collect"
解决了这个问题
rule "Lookup dept data"
when
e: EmployeeData()
d1: ArrayList() from collect (DeptData(deptId == $e.EmpDept1))
d2: ArrayList() from collect (DeptData(deptId == $e.EmpDept2))
d3: ArrayList() from collect (DeptData(deptId == $e.EmpDept3))
d4: ArrayList() from collect (DeptData(deptId == $e.EmpDept4))
d5: ArrayList() from collect (DeptData(deptId == $e.EmpDept5))
then
System.out.println("Employee == " + e);
if (d1.size() > 0) System.out.println("Department 1 == " + d1.get(0));
if (d2.size() > 0) System.out.println("Department 1 == " + d2.get(0));
if (d3.size() > 0) System.out.println("Department 1 == " + d3.get(0));
if (d4.size() > 0) System.out.println("Department 1 == " + d4.get(0));
if (d5.size() > 0) System.out.println("Department 1 == " + d5.get(0));
end
此处的"collect"功能确保员工记录不重复,然后可以找到各个适用的部门。
我有一个场景,当输入中的数据针对一个事实具有不同的列,即转置数据,例如
EmployeeID | EmpDept1 | EmpDept2 | EmpDept3 | EmpDept4 | EmpDept5
现在我也有一个查找部门table,例如
DeptId | DeptName
我要进行查找,一种方法是创建 5 条规则,例如:
rule "Lookup dept-1 data"
when
e: EmployeeData()
d: DeptData(deptId == $e.EmpDept1)
then
System.out.println(e.toString());
end
这里的问题是我必须创建 5 个规则(在我的例子中还有更多)。有没有一种方法可以访问 "THEN" 子句中的 DeptData,然后编写 JAVA 查找?
或者如果有其他好的方法,请告诉我。
谢谢!
所以基本上你有一个员工,他们的部门可以列在五个地方中的任何一个。解决这个问题的最好方法是在触发规则之前弄清楚部门所在的位置,然后修改传递给规则的对象。只要它们序列化正常,您就不会限制将哪种对象传递给规则;如果 EmployeeData 当前是来自数据库调用或其他东西的原始结果,请将其中您需要的部分转换为新的 POJO 并将其传递到规则中。
所以对于你的例子,我会做类似的事情:
class Employee {
private Integer employeeId;
private Integer departmentId; // assuming the only thing you need out of Department is id
public static Employee fromEmployeeData(EmployeeData data) {
Employee employee = new Employee();
employee.setEmployeeId(data.getEmployeeId());
if (data.getDept1() != null) {
employee.setDepartmentId(data.getDept1());
} else if (...) {} // other else-if omitted for brevity
return employee;
}
// getters and setters here
}
public void invokeRules(EmployeeData data) {
KieSession kieSession = ...;
Employee employee = Employee.fromEmployeeData(data);
kieSession.insert( employee );
kieSession.fireAllRules();
}
然后你可以编写关闭部门 id 的规则:
rule "Employee is in department 6"
when
Employee( departmentId == 6 )
then
// do something
end
或者,您可以 从 when 子句中调用 Java 方法。假设我们创建了一个名为 EmployeeUtils
的 class,它具有从 EmployeeData 解析部门的方法:
class EmployeeUtils {
public static Integer resolveDepartment(Employee e) {
Integer deptId = null;
if (e.getDept1Id() != null) {
deptId = e.getDept1Id();
} else if (...) {} // other else-if omitted for brevity
return deptId;
}
}
然后您可以像这样在左侧调用此方法:
rule "Employee in department 6"
when
$employee: Employee()
Integer(this == 6) from EmployeeUtils.resolveDepartment($employee)
then
// do something
end
显然您也可以在右侧调用它:
rule "Example"
when
$employee: Employee()
then
System.out.println("Employee is in department " + EmployeeUtils.resolveDepartment($employee));
end
一般 不推荐 从左侧调用 Java 方法,因为这样的调用无法优化.当您像 Employee(department == 6)
那样进行过滤时,Drools 可以对断言进行大量优化以使您的规则触发得更快。但是,如果您正在调用 Java 方法,那对于 Drools 引擎来说就像一个黑匣子;它不知道 Java 方法将要做什么,所以它必须让它按原样评估,而不是做一些工作来让它更快。
此外,通常也不建议将任意数据传递到规则集中。 只需传入您需要的数据 规则即可触发。从另一个数据源转换时,例如。从 SQL 查询结果或从电子表格转换,解析您实际需要的其他数据部分,并将解析的输入对象传递到规则中。
我在一家将数据库结果直接传递到规则中的公司工作。逻辑是代码已编译,因此如果您需要对规则输入对象进行更改,则需要重新编译代码并发布新版本。但是数据库模式和规则可能在编译后的代码之外,因此它们可以在不需要重新编译的情况下进行更改,因此只需提供所有数据即可,而不必为新版本而烦恼。当公司是新公司并且我们传递的数据只是几个连接表时,这很有效;到我离开时,我们正在传递数百个数据表,在内存中总计达数百 KB,尽管规则只需要可能五个表中的几位数据。它既缓慢又浪费。不要走这条路。
像这样,您有一个 Employee 并且有一个 Department 外部数据——加入这两者是在(例如)SQL 查询中最佳完成的事情。这种关系连接实际上通常不需要对其做出任何决定。如果它确实需要决策(例如,如果部门代码在 Dept5 中,那么它的含义与在 Dept2 中的含义不同),那么多个规则 are 实际上是合适的。但是,当它只是一个相同逻辑的问题但可能在 5 个地方时?不要费心按照规则去做;没有任何好处。在 Java 中执行,然后将结果传递到规则中以进行断言。
我使用 "collect"
解决了这个问题rule "Lookup dept data"
when
e: EmployeeData()
d1: ArrayList() from collect (DeptData(deptId == $e.EmpDept1))
d2: ArrayList() from collect (DeptData(deptId == $e.EmpDept2))
d3: ArrayList() from collect (DeptData(deptId == $e.EmpDept3))
d4: ArrayList() from collect (DeptData(deptId == $e.EmpDept4))
d5: ArrayList() from collect (DeptData(deptId == $e.EmpDept5))
then
System.out.println("Employee == " + e);
if (d1.size() > 0) System.out.println("Department 1 == " + d1.get(0));
if (d2.size() > 0) System.out.println("Department 1 == " + d2.get(0));
if (d3.size() > 0) System.out.println("Department 1 == " + d3.get(0));
if (d4.size() > 0) System.out.println("Department 1 == " + d4.get(0));
if (d5.size() > 0) System.out.println("Department 1 == " + d5.get(0));
end
此处的"collect"功能确保员工记录不重复,然后可以找到各个适用的部门。