You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 23:13:30