一个复杂关系表达式过滤器的实现

本文详细介绍了如何实现一个复杂的关系表达式过滤器,包括解析和构建表达式树的过程。通过定义数据结构和解析算法,解决了前端展示和后端处理的难题,实现了对用户指定ID的条件匹配。重点讨论了表达式的深度、优先级和关联关系的处理,以及如何将表达式转换为便于操作的数组形式。
摘要由CSDN通过智能技术生成

一个复杂关系表达式过滤器的实现

背景

最近,有一个新需求,需要后台设置一个复杂的关系表达式,根据用户指定ID,解析该用用户是否满足该条件,后台设置类似于禅道的搜索条件

但是不同的是禅道有且仅有两个组,每个组最多三个条件

而我们这边组与关系可能是更复杂的,组中有组,每个条件都是有且或关系的。由于保密原因,原型就不发出来了。

看到这个需求,作为一个后端,第一时间想到的是类似QLEpress这类的表达式框架,只要构建一个表达式,通过解析表达式即可快速对目标用户进行筛选,但是可惜的是前端同学不干了,因为作为使用vue或react这类数据驱动的框架来说,将表达式转换为为上述的一个表单太难了 ,所以想了一下,决定自己定义一个数据结构,实现表达式解析。方便前端同学的处理。

分析准备

虽然是用类实现表达式,但是其本质上依旧还是个表达式,我们列举一个简单的表达式:设条件为a,b,c,d,我们随意构造一个表达式:

boolean result=a>100 && b=10 || (c != 3 && d < 50)

我们对表达式进行分析,可以发现表达式 都是共同属性有:

过滤字段(a、b、c、d),判断条件(大于、小于、不等于等等),对比值(a>100 中的100)。

另外,还有关联关系(且、或)和计算优先级这几个属性组成。

于是我们对表达式进行简化:

令a>100 =>A,b=10 =>B,c!=3=>C ,d<50=>D,于是我们得到:

result=A && B || (C && D)

现在问题来了,如何处理优先级呢?

如上表达式,很明显,这是一个大学里学过的标准的中序表达式,于是,我们画一下它的树形图:

根据这个图,我们可以明显的看到,A且B 和C且D是同一级别,于是,我们按照这个理论设计一个层级的概念Deep,我们标注一下,然后再对节点的类型做一下区分,可得:

我们可以看到作为叶子节点(上图绿色部分),相对于其计算计算关系,遇到了一定是优先计算的,所以对于深度的优先级,我们仅需要考虑非叶子节点即可,即上图中的蓝色节点部分,于是我们得到了,计算优先级这个概念我们可以转换为表达式的深度

我们再看上面这个图,Deep1 的关系是Deep2中 A且B 和 C且D两个表达式计算出的结果再进行与或关系的,我们设A 且B 为 G1, C且D为 G2,于是我们发现关系节点关联的类型有两种类型,一种是条件Condition ,一种是Group

至此,这个类的雏形基本就确定了。这个类包含 关联关系(Relation)、判断字段(Field)、运算符(Operator)、运算值(Values)、类型(Type)、深度(Deep)

但是,有个问题,上面的分析中,我们在将表达式转换成树,现在我们试着将其还原,于是我们一眼可以得到其中一种表达式:

result=(A && B)||(C && D)

很显然,和我们的原来的表达式并不一致,这是因为我们上述仅能记录表达式的计算顺序,而不能完全准确的表示这个表达式,这是因为在我们解析表达式的过程中,不仅是有深度、还有一个时序关系,即从左到右的顺序表示,而此时G1中的内容实际上在原表达式中的深度应该是1而不是2,然后我们引入序号的概念,将原来树变成有向的图即:

根据这个图,我们就还原出有且唯一的一个表达式了:result= A && B ||(C && D)

好了,我们分析了半天,原理说完了,回到最初始的问题:前后端怎么实现?对着上图想象一下,貌似还是无法处理,因为这个结构还是太复杂了。对于前端,数据最好是方便遍历的,对于后端,数据最好是方便处理的,于是这时候我们需要将上面这个图转换成一个数组。

实现方式:

上面说到了需要一个数组的结构,我们具体分析一下这个部分

我们发现作为叶子节点,可以始终优先计算,所以我们可以将其压缩,并将关系放置在其中一个表达式中形成 ^A -> &&BA&& -> B$ 的形式,这里我用正则的开始(^)结束($) 表示了一下开始 和 结束 的概念,这里为了与产品原型保持一致我们用第一种方式,即关系符号表示与前一个元素的关系,于是我们再分析一下:

再对序号进行改造:

于是我们得到最终的数据结构:

 @Data
 @AllArgsConstructor
 @NoArgsConstructor
 @Accessors(chain = true)
 public class ExpressDto {
     /**
      * 序号
      */
     private Integer seq;
     /**
      * 深度(运算优先级)
      */
     private Integer deep;
     /**
      * 关系运算符
      */
     private String relation;
     /**
      * 类型
      */
     private String type;
     /**
      * 运算条件
      */
     private String 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值