在程序开发中,遇到需要在后端动态过滤数据的情况,使用逻辑运算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;
发表评论