通用的规则匹配算法(原创)(java+.net)
1.java里可以使用Spring的 Spel或者Google的Aviator
如果使用 Aviator 则添加以下依赖
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>4.1.</version>
</dependency>
不过,推荐使用Spel
一般的规则匹配最终都会采用如下表达式来计算
如 ( {status} in "2,3" && ({level} in "p1,p2" || {times} in "1,9"))
但是存储在DB中一般采用 List<Model>的方式来存储,这样方便管理界面的前端的渲染 (当然也不排除直接存储表达式的,不过前端的渲染就有些难度了)
整个解析过程实现过程有以下几步
1.存储的List中的规则转换为表达式
1.1 增加括号
1.2 替换变量
1.3 构造spel表达式
1.4 连接下一个规则
2.计算表达式
代码如下:
import com.google.common.collect.ImmutableMap; import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
static class RuleItem {
/**
* 左变量
*/
private String left; /**
* 比较表达式
*/
private ComparelOpration comparelOpration; /**
* 右变量或者常量
*/
private String right; /**
* 连接下一个表达式的逻辑运算符
*/
private LogicalOpration logicalOpra;
} @NoArgsConstructor
@AllArgsConstructor
@Data
static class RuleModel {
/**
* 规则列表
*/
private List<RuleItem> ruleItems; /**
* 左括号放在第几个Item之前
*/
private List<Integer> leftParenthesesIndex; /**
* 右括号放在第几个Item之后
*/
private List<Integer> rightParenthesesIndex;
} @Data
@AllArgsConstructor
@NoArgsConstructor
static class SpelResult {
private String express;
private StandardEvaluationContext context;
}
使用的两个连接器(比较连接和逻辑连接)
enum ComparelOpration {
In,
NotIn,
GreaterThan,
LessThan,
GreaterEqualThan,
LessEqualThan,
Equal,
NotEqual;
public static boolean isDecimalCompareLogicalOpration(ComparelOpration opration) {
return opration.ordinal() == ComparelOpration.GreaterThan.ordinal()
|| opration.ordinal() == ComparelOpration.GreaterEqualThan.ordinal()
|| opration.ordinal() == ComparelOpration.LessEqualThan.ordinal()
|| opration.ordinal() == ComparelOpration.LessThan.ordinal();
}
public static boolean isEqualLogicalOpration(ComparelOpration opration) {
return opration.ordinal() == ComparelOpration.Equal.ordinal()
|| opration.ordinal() == ComparelOpration.NotEqual.ordinal()
;
}
}
enum LogicalOpration {
None,
And,
Or;
static String toStr(LogicalOpration logicalOpration) {
return logicalOpration.ordinal() == LogicalOpration.None.ordinal()
? ""
: (logicalOpration.ordinal() == LogicalOpration.And.ordinal() ? "&&" : "||");
}
}
匹配工厂如下
static class SpelMatchFactory {
private static final ExpressionParser parser = new SpelExpressionParser();
static SpelResult toSpelExpress(RuleModel model, Map<String, String> userFeature) {
List<RuleItem> ruleItemList = model.getRuleItems();
StringBuilder sb = new StringBuilder();
StandardEvaluationContext ctx = new StandardEvaluationContext();
for (int i = ; i < ruleItemList.size(); i++) {
RuleItem item = ruleItemList.get(i);
if (model.leftParenthesesIndex.contains(i)) {
sb.append("(");
}
String listKey = "list" + i;
String valueKey = "item" + i;
String subExpress = compute(item, listKey, valueKey);
sb.append(subExpress);
String leftValue = item.getLeft();
if (leftValue.startsWith("{") && leftValue.endsWith("}")) {
leftValue = userFeature.get(leftValue.substring(, leftValue.length() - ));
}
String rightValue = item.getRight();
if (rightValue.startsWith("{") && rightValue.endsWith("}")) {
rightValue = userFeature.get(rightValue.substring(, rightValue.length() - ));
}
if (ComparelOpration.isDecimalCompareLogicalOpration(item.comparelOpration)) {
ctx.setVariable(listKey, Integer.parseInt(rightValue));
ctx.setVariable(valueKey, Integer.parseInt(leftValue));
} else if (ComparelOpration.isEqualLogicalOpration(item.comparelOpration)) {
ctx.setVariable(listKey, rightValue);
ctx.setVariable(valueKey, leftValue);
} else {
ctx.setVariable(listKey, Arrays.asList(rightValue.split(",")));
ctx.setVariable(valueKey, leftValue);
}
if (model.rightParenthesesIndex.contains(i)) {
sb.append(")");
}
if (item.logicalOpra.ordinal() != LogicalOpration.None.ordinal()) {
sb.append(LogicalOpration.toStr(item.getLogicalOpra()));
}
}
return new SpelResult(sb.toString(), ctx);
}
public static boolean compute(RuleModel model, Map<String, String> userFeature) {
SpelResult spelExpressResult = SpelMatchFactory.toSpelExpress(model, userFeature);
Boolean execResult = parser.parseExpression(spelExpressResult.getExpress()).getValue(
spelExpressResult.getContext(),
Boolean.class);
return execResult;
}
private static String compute(RuleItem matchItem, String listKey, String valueKey) {
if (matchItem.getComparelOpration().ordinal() == ComparelOpration.Equal.ordinal()) {
return "#" + listKey + ".equals(#" + valueKey + ")";
}
if (matchItem.getComparelOpration().ordinal() == ComparelOpration.NotEqual.ordinal()) {
return "!#" + listKey + ".equals(#" + valueKey + ")";
}
if (matchItem.getComparelOpration().ordinal() == ComparelOpration.In.ordinal()) {
return "#" + listKey + ".contains(#" + valueKey + ")";
}
if (matchItem.getComparelOpration().ordinal() == ComparelOpration.NotIn.ordinal()) {
return "!#" + listKey + ".contains(#" + valueKey + ")";
}
if (matchItem.getComparelOpration().ordinal() == ComparelOpration.GreaterEqualThan.ordinal()) {
return "#" + valueKey + ">=" + "#" + listKey;
}
if (matchItem.getComparelOpration().ordinal() == ComparelOpration.LessEqualThan.ordinal()) {
return "#" + valueKey + "<=" + "#" + listKey;
}
if (matchItem.getComparelOpration().ordinal() == ComparelOpration.GreaterThan.ordinal()) {
return "#" + valueKey + ">" + "#" + listKey;
}
if (matchItem.getComparelOpration().ordinal() == ComparelOpration.LessThan.ordinal()) {
return "#" + valueKey + "<" + "#" + listKey;
}
throw new IllegalArgumentException("不支持的逻辑运算类型");
}
}
最后 ,测试代码如下:
public static void main(String[] args) {
List<RuleItem> ruleItems = new ArrayList<>();
ruleItems.add(new RuleItem("{status}", ComparelOpration.In, "2,3", LogicalOpration.Or));
ruleItems.add(new RuleItem("{level}", ComparelOpration.In, "1,2", LogicalOpration.And));
ruleItems.add(new RuleItem("{hours}", ComparelOpration.GreaterEqualThan, "", LogicalOpration.And));
ruleItems.add(new RuleItem("{phone1}", ComparelOpration.Equal, "{phone2}", LogicalOpration.None));
RuleModel model = new RuleModel();
model.setRuleItems(ruleItems);
//左括号在0的位置之前
model.setLeftParenthesesIndex(Arrays.asList());
//右括号在1的位置之后
model.setRightParenthesesIndex(Arrays.asList());
//以上表达式相当于 ({status} in '2,3' or {level} in '1,2') && {hours}>=48 && {phone1}=={phone2}
//1. {phone1} != {phone2} ,结果为false
Map<String, String> userFeature1 = ImmutableMap.of("status", "", "level", "", "phone1",
"", "phone2", "", "hours", "");
boolean computeResult = SpelMatchFactory.compute(model, userFeature1);
System.out.println("userFeature1的匹配结果:" + computeResult);
//2.{hours} < 48 ,结果为false
Map<String, String> userFeature2 = ImmutableMap.of("status", "", "level", "", "phone1",
"", "phone2", "", "hours", "");
computeResult = SpelMatchFactory.compute(model, userFeature2);
System.out.println("userFeature2的匹配结果:" + computeResult);
//3. {status} 不在 2,3 中,但是 level 在 1,2中,结果为true
Map<String, String> userFeature3 = ImmutableMap.of("status", "", "level", "", "phone1",
"", "phone2", "", "hours", "");
computeResult = SpelMatchFactory.compute(model, userFeature3);
System.out.println("userFeature3的匹配结果:" + computeResult);
//4. {status} 不在 2,3 中,且 level 不在 1,2中,结果为false
Map<String, String> userFeature4 = ImmutableMap.of("status", "", "level", "", "phone1",
"", "phone2", "", "hours", "");
computeResult = SpelMatchFactory.compute(model, userFeature4);
System.out.println("userFeature4的匹配结果:" + computeResult);
//4.一切都匹配,返回true
Map<String, String> userFeature5 = ImmutableMap.of("status", "", "level", "", "phone1",
"", "phone2", "", "hours", "");
computeResult = SpelMatchFactory.compute(model, userFeature5);
System.out.println("userFeature5的匹配结果:" + computeResult);
}
输出结果为:
表达式:(#list0.contains(#item0)||#list1.contains(#item1))&&#item2>=#list2&&#list3.equals(#item3)
userFeature1的匹配结果:false
表达式:(#list0.contains(#item0)||#list1.contains(#item1))&&#item2>=#list2&&#list3.equals(#item3)
userFeature2的匹配结果:false
表达式:(#list0.contains(#item0)||#list1.contains(#item1))&&#item2>=#list2&&#list3.equals(#item3)
userFeature3的匹配结果:true
表达式:(#list0.contains(#item0)||#list1.contains(#item1))&&#item2>=#list2&&#list3.equals(#item3)
userFeature4的匹配结果:false
表达式:(#list0.contains(#item0)||#list1.contains(#item1))&&#item2>=#list2&&#list3.equals(#item3)
userFeature5的匹配结果:true
c#.net的代码如下
c#.net使用 ExpressionEvaluator.2.0.4.0 来做表达式的计算
通用的规则匹配算法(原创)(java+.net)的更多相关文章
- [原创]Java静态代码检查工具介绍
[原创]Java静态代码检查工具介绍 一 什么是静态代码检查? 静态代码分析是指无需运行被测代码,仅通过分析或检查源程序的语法.结构.过程.接口等来检查程序的正确性,找出代码隐藏的错误和缺陷,如参数 ...
- 规则引擎集成接口(七)规则引擎调用Java类
规则引擎调用Java类 通过myEclipse编写一个简单工程,其中方法是两数相加等到结果,既结果1=输入值1+输入值2.实现规则调用外部接口的方法有三种. 1:接口实例:在myEclipse中制作一 ...
- [原创]java WEB学习笔记95:Hibernate 目录
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- [原创]Java性能优化权威指南读书思维导图
[原创]Java性能优化权威指南读书思维导图 书名:Java性能优化权威指南 原书名:Java performance 作者: (美)Charlie Hunt Binu John 译者: 柳飞 ...
- [原创]java WEB学习笔记75:Struts2 学习之路-- 总结 和 目录
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- [原创]java WEB学习笔记66:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- Fortify规则与CERT JAVA 安全编程规范的对照表
Fortify规则与CERT JAVA 安全编程规范的对照表http://www.automationqa.com/forum.php?mod=viewthread&tid=4353& ...
- [原创]Java性能优化权威指南读书思维导图4
[原创]Java性能优化权威指南读书思维导图4
- [原创]Java性能优化权威指南读书思维导图3
[原创]Java性能优化权威指南读书思维导图3
随机推荐
- maven 配置发布仓库
·首先,在工程的pom.xml中添加仓库信息 <distributionManagement> <repository> <id>releases</id&g ...
- Test 6.29 T1 预算方案
问题描述 "我需要你为我制订一个购物的方案.我将要为我的宫殿增置一些家具.有n 种备选家具,家具有主件和附件之分.在购买某个主件的附件之前,我必须先购买其对应的主件.某一主件的附件不会是另一 ...
- Task3.特征选择
参考:https://www.jianshu.com/p/f3b92124cd2b 互信息 衡量两个随机变量之间的相关性,两个随机变量相关信息的多少. 随机变量就是随机试验结果的量的表示,可以理解为按 ...
- php str_replace()函数 语法
php str_replace()函数 语法 作用:字符串替换操作,区分大小写大理石构件 语法:str_replace(find,replace,string,count) 参数: 参数 描述 fin ...
- 牛客多校第一场 Random Point in Triangle
https://ac.nowcoder.com/acm/contest/881/F 打表代码: #include<bits/stdc++.h> using namespace std; ] ...
- Photon学习(一)——Photon Networking Free网络组件学习
一般前端untiy程序员都很想自己学会后端网络编程,这样一个人就可以把前后端都做了,做网络游戏可比单机游戏好玩多了,笔者我对喜欢的就是mmo多人对战游戏,一起组队打副本,一起体验多人对战的乐趣.从业以 ...
- matplot在Mac下显示中文的方案
使用matplotlib经常会出现中文显示异常的问题. 网上很多都讲需要下载中文字体包...偶然看到别人发的一种简单的解决放啊.Mac上本身就有支持中文的字体包啊.引入就好了 贴上代码 plt.rcP ...
- WebService-.Net:添加web引用和添加服务引用有什么区别?
ylbtech-WebService-.Net:添加web引用和添加服务引用有什么区别? 1.返回顶部 1. 添加web引用和添加服务引用有什么区别,Add Service References 和 ...
- DB-MD:MD/主数据
ylbtech-DB-MD:MD/主数据 主数据(MD Master Data)指系统间共享数据(例如,客户.供应商.账户和组织部门相关数据).与记录业务活动,波动较大的交易数据相比,主数据(也称基准 ...
- Jquery.extend()和jQuery.fn.extend(object);
摘自: jquery $.fn $.fx是什么意思有什么用_jquery_脚本之家 jQuery.extend(object); 为扩展jQuery类本身.为类添加新的方法. jQuery.fn.ex ...