通用的规则匹配算法(原创)(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
随机推荐
- 四、MyBatis-映射文件
映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义. <?xml version="1.0" encoding="UTF-8" ?&g ...
- 关于pug的笔记
一.简介 Pug 是一款健壮.灵活.功能丰富的模板引擎,专门为 Node.js 平台开发.Pug 是由 Jade 改名而来,他可以帮助我们写html的时候更加的简单明了.安装.使用pug的过程打开cm ...
- JVM内存结构从永久代到元空间
在文章<JVM之内存结构详解>中我们描述了Java7以前的JVM内存结构,但在Java8和以后版本中JVM的内存结构慢慢发生了变化.作为面试官如果你还不知道,那么面试过程中是不是有些露怯? ...
- Js中window.location.href和window.location.replace的区别
href相当于打开一个新页面,replace相当于替换当前页面:这里打开页面都是针对历史记录来说,在页面上看完全相同,只是浏览器的history表现不同如果在1.html中点击链接到2.html,然后 ...
- laravel后台账户登录验证(5.5.48版本)
首先我是菜鸟,对laravel框架也不是很熟悉,突然有一天心血来潮就想研究一下laravel的后台登录用户登录的流程, 虽然公司项目中有这样的一套流程,也看了好几遍,越看越简单,越看我就越会了,当自己 ...
- jmeter接口测试与接口测试工具
接口测试与接口测试工具 1,什么是接口? 接口,Application Programming Interface(API) 通俗的讲 就是HTTP请求 2,什么是接口测试? 接口测试测试组件间接口的 ...
- DELPHI 调试IOS时出现 Please specify exact device preset UUID
右击设备DEVICE, 选择REFRESH刷新, 再重新选择一个设备即可.
- windows系统如何查看物理cpu核数,内存型号等
首先,我们需要打开命令行模式,利用win+r键打开运行,输入cmd回车即会出现 然后在命令行界面输入wmic进入命令行系统管理执行脚本界面 然后我们通过cpu get *可以查看cpu的具 ...
- 《Javascript设计模式与开发实践》关于设计模式典型代码的整理:单例模式、策略模式、代理模式、迭代器模式、发布-订阅模式、命令模式、组合模式
1.单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 使用闭包封装私有变量// 使用闭包创建单例var user = (function () { var _name = 'sven' ...
- web服务器、WSGI跟Flask(等框架)之间的关系
之前对 Nginx,WSGI(或者 uWSGI,uwsgi),Flask(或者 Django),这几者的关系一存存在疑惑.通过查阅了些资料,总算把它们的关系理清了. 总括来说,客户端从发送一个 HTT ...