在程序开发中,遇到需要在后端动态过滤数据的情况,使用逻辑运算and和or针对多个条件进行匹配。在初始设计版本中,只使用了简单的and和or单层结构。实现A并且B或者C没有问题,但是(A并且B)或者(C并且D)就没有办法了。
针对这种状况,需要进行程序升级,实现类似sql的写法,可以加括号提升运算优先级,并且尽量优化算法,提高匹配效率。
1、在原来的基础上,增加规则名称,用于说明这条规则的用途。
2、去除单层的连接符。
3、增加多层规则配置界面。
灵感来源与navicat的筛选条件
数据使用json字符串来存储
[
{
"type": "rule",
"name": "规则1",
"index": 0,
"operator": "and"
},
{
"type": "group",
"data": [
{
"type": "rule",
"name": "规则2",
"index": 1,
"operator": "and"
},
{
"type": "rule",
"name": "规则3",
"index": 2,
"operator": "or"
},
{
"type": "group",
"data": [
{
"type": "rule",
"name": "规则4",
"index": 3,
"operator": "or"
},
{
"type": "rule",
"name": "规则5",
"index": 4,
"operator": ""
}
],
"operator": "and"
},
{
"type": "rule",
"name": "规则6",
"index": 5,
"operator": ""
}
],
"operator": "and"
},
{
"type": "rule",
"name": "规则7",
"index": 6,
"operator": ""
}
]分为两种类型,规则与分组。规则类型表示实际的规则,分组类型表示一个括号。每个相同层级的最后一项没有运算符。
代码实现
public static Boolean cacluateSingle(JSONObject single,Map obj,List<Map> materialOrderExecuteItemRules){
// 单个条件计算结果
Boolean conditionResult = false;
// 获取规则类型 rule规则 group分组
String ruleType = single.getString("type");
// 分组
if("group".equals(ruleType)){
// 获取分组数据
JSONArray groupData = single.getJSONArray("data");
// 递归计算分组结果
conditionResult = calculateRulesMatchingResultTest(groupData,obj,materialOrderExecuteItemRules);
// 规则
} else if("rule".equals(ruleType)) {
// 获取规则下标
Integer ruleIndex = single.getInteger("index");
// 获取匹配规则
Map itemRule = ruleIndex < materialOrderExecuteItemRules.size() ? materialOrderExecuteItemRules.get(ruleIndex) : null;
if(Objects.nonNull(itemRule)){
String targetVal = StrHelper.getObjectValue(itemRule.get("targetVal"));
conditionResult = StrHelper.getObjectValue(obj.get("targetVal")).equals(targetVal);
System.out.println(ruleIndex+"["+single.getString("name")+"]进行了一次计算"+conditionResult);
}
}
return conditionResult;
}
/**
* 计算规则匹配结果
* @param conditionArr
* @param materialOrderExecuteItemRules
* @return
*/
public static Boolean calculateRulesMatchingResultTest(JSONArray conditionArr,Map obj,List<Map> materialOrderExecuteItemRules){
boolean result = false;
if(CollectionUtils.isEmpty(conditionArr)){
return false;
}
// 遍历多条件
for (int i = 0; i < conditionArr.size(); i++) {
// 获取单个条件
JSONObject single = conditionArr.getJSONObject(i);
// 获取与上一项的逻辑运算符
String lastOperator = i == 0 ? "": conditionArr.getJSONObject(i-1).getString("operator");
// 第一项
if(StringUtils.isBlank(lastOperator)){
// 直接计算结果
result = cacluateSingle(single,obj,materialOrderExecuteItemRules);
}
// 逻辑运算符为并且
if (lastOperator.equals("and")) {
// 增加 短路与
result = result && cacluateSingle(single,obj,materialOrderExecuteItemRules);
} else if (lastOperator.equals("or")) {
// 增加 短路或
result = result || cacluateSingle(single,obj,materialOrderExecuteItemRules);
}
}
return result;
}
public static void main(String[] args) {
String multilayerConditionJson = "[\n" +
" {\n" +
" \"type\": \"rule\",\n" +
" \"name\": \"规则1\",\n" +
" \"index\": 0,\n" +
" \"operator\": \"and\"\n" +
" },\n" +
" {\n" +
" \"type\": \"group\",\n" +
" \"data\": [\n" +
" {\n" +
" \"type\": \"rule\",\n" +
" \"name\": \"规则2\",\n" +
" \"index\": 1,\n" +
" \"operator\": \"and\"\n" +
" },\n" +
" {\n" +
" \"type\": \"rule\",\n" +
" \"name\": \"规则3\",\n" +
" \"index\": 2,\n" +
" \"operator\": \"or\"\n" +
" },\n" +
" {\n" +
" \"type\": \"group\",\n" +
" \"data\": [\n" +
" {\n" +
" \"type\": \"rule\",\n" +
" \"name\": \"规则4\",\n" +
" \"index\": 3,\n" +
" \"operator\": \"or\"\n" +
" },\n" +
" {\n" +
" \"type\": \"rule\",\n" +
" \"name\": \"规则5\",\n" +
" \"index\": 4,\n" +
" \"operator\": \"\"\n" +
" }\n" +
" ],\n" +
" \"operator\": \"and\"\n" +
" },\n" +
" {\n" +
" \"type\": \"rule\",\n" +
" \"name\": \"规则6\",\n" +
" \"index\": 5,\n" +
" \"operator\": \"\"\n" +
" }\n" +
" ],\n" +
" \"operator\": \"and\"\n" +
" },\n" +
" {\n" +
" \"type\": \"rule\",\n" +
" \"name\": \"规则7\",\n" +
" \"index\": 6,\n" +
" \"operator\": \"\"\n" +
" }\n" +
"]";
// 条件数组
JSONArray conditionArr = new JSONArray();
try{
// 转换条件数组
conditionArr = JSONArray.parseArray(multilayerConditionJson);
}catch (Exception e){
log.error("参数["+multilayerConditionJson+"]转换JSONArray失败!"+e.getMessage(),e);
}
JSONArray finalConditionArr = conditionArr;
List<Map> rules = new ArrayList<>();
Map rule1 = new HashMap();
rule1.put("targetVal","1");
rules.add(rule1);
Map rule2 = new HashMap();
rule2.put("targetVal","0");
rules.add(rule2);
Map rule3 = new HashMap();
rule3.put("targetVal","1");
rules.add(rule3);
Map rule4 = new HashMap();
rule4.put("targetVal","1");
rules.add(rule4);
Map rule5 = new HashMap();
rule5.put("targetVal","1");
rules.add(rule5);
Map rule6 = new HashMap();
rule6.put("targetVal","1");
rules.add(rule6);
Map rule7 = new HashMap();
rule7.put("targetVal","1");
rules.add(rule7);
Map obj =new HashMap();
obj.put("targetVal","1");
Boolean reulst = calculateRulesMatchingResultTest(finalConditionArr,obj,rules);
System.out.println(reulst);
}模拟参数
运行结果
代码详解
1、使用简单的List<Map>模拟多条规则,实际内容更丰富,示例代码中仅存储一个targetVal值,用作简单的匹配返回结果。
2、使用简单的Map模拟单条数据,实际场景为List<Map>多条目标数据,根据多条规则进行过滤,提取满足全部条件的数据。
3、cacluateSingle方法为单条规则的匹配计算,其中针对单个规则与分组分情况处理,使用递归方法调用多条规则的方法calculateRulesMatchingResultTest。
4、calculateRulesMatchingResultTest为多条规则的匹配计算,同一层级中,第一条直接计算,后面的每一条都与上一条进行短路计算。
5、&与&&,|与||的效果是不一样的。写两个的称为短路运算,运算符之前的满足条件了后面的就不会运算了。使用短路运算,可以减少运算,提高效率。
举例:
a||b,a短路或b,当a为真,b就不会再运算了,整个表达式已经为真,不管b是真是假
a&&b,a短路与b,当a为假,b就不会再运算了,整个表达已经为假,不管b是真是假
6、短路计算,必须提取单条的方法写在逻辑运算表达式中,才能触发短路操作。以下两种写法,其二,无论result的值是什么,下一次的运算都会触发。
要这样写
result = result && cacluateSingle(single,obj,materialOrderExecuteItemRules);
而不能这样写
boolean singleResult = cacluateSingle(single,obj,materialOrderExecuteItemRules); result = result && singleResult;







发表评论