目次
ルールに基づく論理式と条件の抽象化
複数の条件を含む任意のルールは、次の JSON 形式に抽象化できます。
このうち、1、2、3はそれぞれ3つの条件を表しており、このルールの実装方法は「1 & (2 | 3)」ですが、もちろん同様の形でも構いません。以下のjson定義
{
"logic": "1 & (2 | 3)",
"conditions":[
{
"isNot":true,
"itemId":1,
"left":{
"value":"1",
"isVar":true
},
"operator":"<",
"right":{
"value":"3",
"isVar":true
}
},
{
"isNot":false,
"itemId":2,
"left":{
"value":"1",
"isVar":true
},
"operator":"==",
"right":{
"value":"3",
"isVar":true
}
},
{
"isNot":false,
"itemId":3,
"left":{
"value":"number",
"isVar":true
},
"operator":"startsWith",
"right":{
"value":"666",
"isVar":true
}
}
]
}
各条件の抽象化には、論理式 (つまり 1、2、3) を関連付けるために使用される対応するIDと、条件の 3 つの要素 (左変数、右変数、演算子) が含まれます。
{
"isNot":false,
"itemId":1,
"left":{
"value":"1",
"isVar":true
},
"operator":"<",
"right":{
"value":"3",
"isVar":true
}
}
表現ロジックのエンコードと抽象化
論理式の処理1 & (2 | 3)
は接尾辞式と同様であり、2 | 3 は & の結果を 1 で実行します。1 & (2 | 3)
解析後は次のようになります。
演算子式:次のように定義される演算データと演算子が含まれます。
public class OperatorExpr implements Expr {
// 操作数列表
private List<Expr> operands;
// 操作符的名称
private String operator;
/**
* !标志,not在表达式和操作符中都可能出现,用这个key来区分这两种情况,主要用于统计分析
*
* 其余取值无意义
*/
private String notToken;
/**
* 表达式的标识,不是所有的Operator都有
*/
private int id = -1;
public OperatorExpr(String operator, List<Expr> operands) {
this(operator, operands, "");
}
public OperatorExpr(String operator, List<Expr> operands, String notToken) {
this.operator = operator;
this.operands = operands;
this.notToken = notToken;
}
public List<Expr> getOperands() {
return operands;
}
public void setOperands(List<Expr> operands) {
this.operands = operands;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public void accept(ExprVisitor visitor) {
visitor.visit(this);
}
}
ルール
ルール上の条件
public class Condition {
/**
* 序号。
*
* 一般从 1 开始。
*/
private int itemId;
/**
* 操作符。
*
* 比如 : 大于,不等于 等
*/
private String operator;
/**
*
*/
private boolean isNot;
/**
* 左变量
*/
private Operand left;
/**
* 右变量
*/
private Operand right;
演算子の抽象化
public abstract class AbstractBaseOperator extends AbstractIdentifiableOperator{
/**
* @param context 引擎执行上下文
* @return 识别结果
*/
@Override
public EvalResult doEval(EngineExecutionContext context) {
Variable op1 = operands.get(0);
Variable op2 = operands.get(1);
if (op1 == null || op2 == null) {
throw new IllegalArgumentException("argument in operator can't be null.");
}
Object a, b = null;
a = op1.eval(context);
//op2.eval()是个耗时操作。如果op1是Unknown,op2.eval就没必要做了。
if (a == EvalResult.Unknown) {
return EvalResult.Unknown;
}
if (op2 != null) {
b = op2.eval(context);
}
if (b == EvalResult.Unknown) {
return EvalResult.Unknown;
}
return invokeOperator(a, b, context);
}
private EvalResult invokeOperator(Object a, Object b, EngineExecutionContext context) {
try {
// 第一遍求值时,忽略高耗时操作符
return EvalResult.valueOf(apply(a, b, context));
} catch (Throwable e) {
throw e;
} finally {
}
}
/**
* 操作符具体逻辑
*
* @param a 左操作实参
* @param b 右操作实参
* @param context 上下文
* @return true/false
*/
protected abstract Boolean apply(Object a, Object b, EngineExecutionContext context);
@Override
public void accept(EvaluableVisitor visitor) {
visitor.visit(this);
}
}
- 左右のオペランドを取得して評価するロジックが必要です。eval メソッド
- オペレーター操作を実行し、メソッドを受け入れます
このインターフェースが使用される理由は主に、遅延した操作や時間のかかる操作、つまり実行する必要があるときに実行するためです。 /詳細/121593411
ルールクラスを定義する
public class Rule implements Serializable, Evaluable<RuleResult>{
protected String id;
protected String title;
private RuleEntry ruleEntry;
protected volatile Evaluable<EvalResult> expression;
public Rule() {
}
public Rule(RuleEntry entry) {
this.id = entry.getId();
this.expression = parse(entry);
this.ruleEntry = entry;
}
@Override
public RuleResult eval(EngineExecutionContext context) {
long startTime = System.nanoTime();
RuleResult result = new RuleResult(EvalResult.False, this);
try {
EvalResult evalResult = getExpression().eval(context);
result.setEvalResult(evalResult);
} catch (Exception ab) {
result = new RuleResult(ab);
} finally {
result.setCost(System.nanoTime() - startTime);
}
return result;
}
@Override
public void accept(EvaluableVisitor visitor) {
visitor.visit(this);
}
//
public Evaluable<EvalResult> getExpression() {
return expression;
}
// parse表达式并执行
public Evaluable<EvalResult> parse(RuleEntry rawRule) {
try {
Expr expr = parseExpr(rawRule.getExpression());
return toEvaluable(rawRule, expr);
} catch (Exception e) {
throw new IllegalArgumentException("parse rule error, ruleId: " + rawRule.getId(), e);
}
}
public Expr parseExpr(Expression rawExpr) {
if (rawExpr == null) {
throw new IllegalArgumentException("absence of raw expression");
}
return parseExpr(rawExpr.getLogic(), rawExpr.getConditions());
}
public Expr parseExpr(String logic, List<Condition> conditions) {
ExprParser parser = new ExprParser(logic, conditions);
return parser.expr();
}
public static Evaluable<EvalResult> toEvaluable(RuleEntry rawRule, Expr expr) {
if (expr == null) {
return null;
}
String ruleId = rawRule.getId();
EvalExprVisitor visitor = new EvalExprVisitor();
expr.accept(visitor);
return visitor.getEvalExpr();
}
}
- ルールのプロパティには条件式とその操作ロジックが含まれます
- ルールが初期化されると、条件式が初期化されて検証されます。
- ルールの実行は、論理式を実行する
1 & (2 | 3)
プロセスです。
ルールの施行
public class RuleTest {
public static void main(String[] args) {
String logic = "1 &(2|3)";
List<Condition > conditions = new ArrayList<>();
Condition condition1 = new Condition();
condition1.setItemId(1);
Operand operandLeft = new Operand();
operandLeft.setIsVar(true);
operandLeft.setValue("age");
operandLeft.setModifier("age");
condition1.setLeft(operandLeft);
condition1.setOperator(Operator.GT);
Operand operandRight = new Operand();
operandRight.setIsVar(false);
operandRight.setValue("18");
operandRight.setType("int");
condition1.setRight(operandRight);
conditions.add(condition1);
Condition condition2 = new Condition();
condition2.setItemId(2);
Operand operandLeft2 = new Operand();
operandLeft2.setIsVar(false);
operandLeft2.setValue("2");
condition2.setLeft(operandLeft2);
condition2.setOperator(Operator.LT);
Operand operandRight2 = new Operand();
operandRight2.setIsVar(false);
operandRight2.setValue("1");
condition2.setRight(operandRight2);
conditions.add(condition2);
Condition condition3 = new Condition();
condition3.setItemId(3);
Operand operandLeft3 = new Operand();
operandLeft3.setIsVar(true);
operandLeft3.setValue("number");
operandLeft3.setModifier("number");
condition3.setLeft(operandLeft3);
condition3.setOperator(Operator.CONTAINS_STRING);
Operand operandRight3 = new Operand();
operandRight3.setIsVar(false);
operandRight3.setValue("666");
condition3.setRight(operandRight3);
conditions.add(condition3);
Expression expression = new Expression(logic, conditions);
RuleEntry ruleEntry = new RuleEntry();
ruleEntry.setId("1");
ruleEntry.setExpression(expression);
// 构造规则
Rule rule = new Rule(ruleEntry);
// 构造规则执行的上下文
EngineExecutionContext engineExecutionContext = new EngineExecutionContext();
Map<String, Object> ctx = new HashMap<>();
ctx.put("age", 19);
ctx.put("number", "666abc");
engineExecutionContext.setData(ctx);
// 执行规则并打印结果
RuleResult ruleResult = rule.eval(engineExecutionContext);
System.out.println(ruleResult);
}
}
演算子計算加算のための式トラバーサル
public class EvalExprVisitor implements ExprVisitor {
private Operator<?, EvalResult> expr = null;
private Operator<Evaluable<?>, ?> currentParent = null;
@Override
public boolean visit(IdentifierExpr x) {
if (null != currentParent && null != x) {
currentParent.addOperand(new Identifier(x.getConditionId(),x.getName()));
}
return false;
}
@Override
public boolean visit(LiteralExpr x) {
if (null != currentParent && null != x) {
currentParent.addOperand(new Literal(x.getValue()));
}
return false;
}
@SuppressWarnings({
"unchecked", "rawtypes" })
@Override
public boolean visit(OperatorExpr x) {
String operator = x.getOperator();
// 获取操作符实际例子
Operator opexpr = OperatorLoader.getOperator(operator);
if (expr == null) {
expr = opexpr;
}else {
currentParent.addOperand(opexpr);
}
Operator<Evaluable<?>, ?> oldParent = currentParent;
currentParent = opexpr;
List<Expr> operands = x.getOperands();
if (operands != null && !operands.isEmpty()) {
for (Expr expr : operands) {
expr.accept(this);
}
}
currentParent = oldParent;
return false;
}
public Evaluable<EvalResult> getEvalExpr() {
return expr;
}
}
さまざまな式タイプ: 左の変数、演算子、右の変数はすべて式に抽象化され、特定の実行プロセス中に解析されて実行されます。
public interface ExprVisitor {
/**
* 访问变量表达式
*
* @param identifierExpr
* @return
*/
boolean visit(IdentifierExpr identifierExpr);
/**
* 访问常量表达式
*
* @param literalExpr
* @return
*/
boolean visit(LiteralExpr literalExpr);
/**
* 访问操作符表达式
*
* @param operatorExpr
* @return
*/
boolean visit(OperatorExpr operatorExpr);
}
例証します:
- 式はプログラムの起動前にキャッシュできます。
- SPIを開いてユーザー定義式をサポートできる
ルールに基づく特定の条件の実行
ルールに基づく条件の実行は、実際には演算子の実行となります。
- ステップ 1: 左オペランドと右オペランドを取得します。最終値を取得するには、コンテキストに従って動的に実行する必要があります。
- ステップ 2: オペレータの特定の判定比較を実行する
- ステップ 3: 結果を取得するか、次の演算子を実行するためのオペランドとして取得します。
public abstract class AbstractBaseOperator extends AbstractIdentifiableOperator{
/**
* @param context 引擎执行上下文
* @return 识别结果
*/
@Override
public EvalResult doEval(EngineExecutionContext context) {
Variable op1 = operands.get(0);
Variable op2 = operands.get(1);
if (op1 == null || op2 == null) {
throw new IllegalArgumentException("argument in operator can't be null.");
}
Object a, b = null;
a = op1.eval(context);
//op2.eval()是个耗时操作。如果op1是Unknown,op2.eval就没必要做了。
if (a == EvalResult.Unknown) {
return EvalResult.Unknown;
}
if (op2 != null) {
b = op2.eval(context);
}
if (b == EvalResult.Unknown) {
return EvalResult.Unknown;
}
return invokeOperator(a, b, context);
}
private EvalResult invokeOperator(Object a, Object b, EngineExecutionContext context) {
try {
// 第一遍求值时,忽略高耗时操作符
return EvalResult.valueOf(apply(a, b, context));
} catch (Throwable e) {
throw e;
} finally {
}
}
/**
* 操作符具体逻辑
*
* @param a 左操作实参
* @param b 右操作实参
* @param context 上下文
* @return true/false
*/
protected abstract Boolean apply(Object a, Object b, EngineExecutionContext context);
@Override
public void accept(EvaluableVisitor visitor) {
visitor.visit(this);
}
ルールの実行後に最終結果を取得する
説明: ルールの実行プロセス中に、時間のかかる、異常、アクセスの問題などの各条件の実行ステータスを記録できます。返された結果には、その後のトラブルシューティングを容易にするために記録することもできます。