Drools规则引擎MVEL方言是否支持double比较的内置epsilon参数?
问题描述
我正在使用MVEL方言为Drools规则引擎定义规则,规则的when部分包含如下算术表达式:
rule "Test rule" dialect "mvel" when context : ActionContext( context.get("test") == 0.8 ) then context.add("Success" , "test"); end
我希望执行规则时,所有double类型比较都能纳入epsilon值进行考量。我查阅了MVEL库及相关资料,未找到直接解决方案,但这似乎是个常见问题。请问MVEL是否以某种方式支持该特性?
注:考虑到性能影响,我不想使用BigDecimal。
我可以为每个double比较编写自定义函数,但这是最后的选择,是否有更好的方法?
解决方案
1. 利用Drools自定义运算符重载(推荐)
Drools支持通过OperatorOverride扩展重载默认比较运算符,能全局替换double类型的==比较逻辑,自动加入epsilon判断,无需修改每条规则的表达式。
具体实现步骤:
- 实现
OperatorOverride接口,针对EQUAL运算符添加epsilon比较逻辑:
import org.drools.core.base.evaluators.OperatorOverride; import org.drools.core.base.evaluators.OperatorEvaluator; import org.drools.core.base.evaluators.OperatorEvaluatorWrapper; import org.drools.core.spi.Evaluator; public class DoubleEpsilonOperatorOverride implements OperatorOverride { // 可根据业务需求调整epsilon值 private static final double EPSILON = 1e-9; @Override public OperatorEvaluator override(Evaluator evaluator) { // 仅针对double类型的==比较进行重载 if (evaluator.getOperator().equals("==") && (evaluator.getLeftOperandType() == Double.class || evaluator.getLeftOperandType() == double.class) && (evaluator.getRightOperandType() == Double.class || evaluator.getRightOperandType() == double.class)) { return new OperatorEvaluatorWrapper(evaluator) { @Override public boolean evaluate(Object left, Object right) { if (left == null || right == null) return left == right; double d1 = ((Number) left).doubleValue(); double d2 = ((Number) right).doubleValue(); return Math.abs(d1 - d2) < EPSILON; } }; } // 其他运算符保持原有逻辑 return evaluator; } }
- 在Drools初始化时注册该运算符重载:
import org.kie.api.KieServices; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.drools.core.base.evaluators.OperatorOverrideRegistry; // 全局注册epsilon比较的运算符重载 OperatorOverrideRegistry.getInstance().addOperatorOverride(new DoubleEpsilonOperatorOverride()); // 初始化Kie容器与会话 KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer(); KieSession kieSession = kieContainer.newKieSession();
完成后,所有规则中double类型的==比较都会自动应用epsilon判断,无需修改现有规则代码。
2. 全局注册MVEL自定义比较函数
如果不想修改Drools核心逻辑,可以自定义epsilon比较函数并全局注册到MVEL上下文,简化规则中的调用方式。
- 定义比较工具类:
public class MvelDoubleUtils { private static final double EPSILON = 1e-9; // 处理基本类型double public static boolean eq(double a, double b) { return Math.abs(a - b) < EPSILON; } // 处理包装类型Number(兼容Integer、Long等转double比较) public static boolean eq(Number a, Number b) { if (a == null || b == null) return a == b; return Math.abs(a.doubleValue() - b.doubleValue()) < EPSILON; } }
- 在Drools会话中注册全局函数:
kieSession.setGlobal("eq", MvelDoubleUtils.class);
- 规则中直接调用:
rule "Test rule" dialect "mvel" when context : ActionContext( eq(context.get("test"), 0.8) ) then context.add("Success" , "test"); end
这种方式比手动写Math.abs(...) < EPSILON更简洁,且只需注册一次即可在所有规则中复用。
3. 扩展MVEL类型处理器(进阶)
MVEL允许自定义TypeConverter或修改VariableResolverFactory,在表达式解析阶段替换double比较逻辑,但该方式需要深入理解MVEL内部机制,实现复杂度高,一般不推荐作为首选方案。
内容的提问来源于stack exchange,提问作者Ramya Loganathan
相关产品推荐
相关产品推荐

