通用的规则匹配算法(原创)(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
随机推荐
- CF1090J Two Prefixes
神仙题++ 还是在某校梁大讲的题qaq 我们考虑容斥 也就是本质不同字串=全部-重复的 我们只需要求重复的即可 考虑相同的s=ab 我们用长度最长的a作为代表串 如果存在一个a'b'且|a'|> ...
- docker中pull镜像,报错 pull access denied for ubantu, repository does not exist or may require 'docker login'
报错说明:拒绝获取ubantu, 仓库不存在或者需要登录docker 1.先尝试注册docker 2.在拉镜像前,先登录docker, 命令:docker login 3.然后执行 docker ...
- dict常用操作
dict是python中的常用数据结构,应该尽量掌握其使用方法 """ 初始化一个dict的四种方式: 1. dict() -> 创建一个空的dict 2. dic ...
- NIO编程模式示例
1. 服务端 import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; im ...
- HashMap测试程序1
package com.iotek.map; import java.util.Collection;import java.util.HashMap;import java.util.Map;imp ...
- 富文本编辑器tinymce支持从word复制粘贴保留格式和图片的插件wordpaster
tinymce是很优秀的一款富文本编辑器,可以去官网下载.https://www.tiny.cloud 这里分享的是它官网的一个收费插件powerpaste的旧版本源码,但也不影响功能使用. http ...
- navigator.userAgent.toLowerCase();判断浏览器做兼容
js简单实例: var ua = navigator.userAgent.toLowerCase(); if (/android/.test(ua)) { $('.date>div>img ...
- SPFA的两个优化
评测题:洛谷[模板]单源最短路径 不加任何优化: queue<int>q; void spfa(ll s) { ;i<=n;i++) d[i]=(ll)(); d[s]=;q.pus ...
- 使用PHPExcel操作Excel用法实例分析
本文实例分析了使用PHPExcel操作Excel用法.分享给大家供大家参考.具体分析如下: PHPExcel下载地址:http://www.codeplex.com/PHPExcel http://w ...
- crfclust.bdb导致磁盘满
检查ora.crf服务 crsctl stat res ora.crf -init -t 关闭ora.crf服务 crsctl stop res ora.crf -init cd $ORACLE_HO ...