zoukankan      html  css  js  c++  java
  • 重写逻辑表达式

    最近碰到一个动态查询条件的问题,比如,前端界面上可选择输入姓名、性别、年龄、电话等查询条件,后端根据是否输入来动态构建查询sql where条件(即如果未输入则不作为查询条件)。如果这些条件最终是全and或全or起来则相对容易处理,但如果既有and又有or则动态构建就困难了。

    这个问题经抽象可表达为动态重写逻辑表达式,即先写出完整的表达式,然后排除无效部分。例如,对于下面的完整逻辑表达式字符串:

    (a) or (b) and (c)
    1. 如果 a 无效则重写为 (b) and (c) 
    2. 如果 b 无效则重写为 (a) or (c) 
    3. 如果 c 无效则重写为 (a) or (b)
    4. 如果 a 和 b 都无效则重写为 (c)  
    5. ...
    6. 如果全都无效则返回null

    这个问题如何通用地解决呢?初看起来似乎很难,无从下手,但用ANTLR来解决则简单到出乎你的意料。

    1、定义逻辑表达式语法

     1 # LogicExpr.g4文件
     2 
     3 grammar LogicExpr;
     4 
     5 stat: expr ;
     6 
     7 expr: expr AND expr             # and
     8     | expr OR expr              # or
     9     | '(' expr ')'              # group
    10     | VAR                       # var
    11     ;
    12 
    13 VAR : '(' KEY ')' ;
    14 
    15 AND: 'and' ;
    16 OR: 'or' ;
    17 KEY: [a-zA-Z0-9_]+ ;
    18 WS: [ 	
    ]+ -> skip ;
    View Code

    2、用ANTLR工具生成Java代码(略)

    3、接着写排除算法

      1 package com.mycomp.antlr4.le;
      2 
      3 import java.util.Collection;
      4 
      5 import org.antlr.v4.runtime.ANTLRInputStream;
      6 import org.antlr.v4.runtime.CommonTokenStream;
      7 import org.antlr.v4.runtime.misc.Interval;
      8 
      9 import com.mycomp.antlr4.le.LogicExprParser.AndContext;      // 以下5个import是ANTLR生成的代码
     10 import com.mycomp.antlr4.le.LogicExprParser.GroupContext;
     11 import com.mycomp.antlr4.le.LogicExprParser.OrContext;
     12 import com.mycomp.antlr4.le.LogicExprParser.StatContext;
     13 import com.mycomp.antlr4.le.LogicExprParser.VarContext;
     14 
     15 public class LogicExprRewriter extends LogicExprBaseVisitor<String> {
     16 
     17     public static String rewrite(String le, Collection<String> invalidKeys) {
     18         ANTLRInputStream input = new ANTLRInputStream(le);
     19 
     20         LogicExprLexer lexer = new LogicExprLexer(input);
     21         CommonTokenStream tokens = new CommonTokenStream(lexer);
     22 
     23         LogicExprParser parser = new LogicExprParser(tokens);
     24         StatContext tree = parser.stat();
     25 
     26         LogicExprRewriter rewriter = new LogicExprRewriter(invalidKeys);
     27         return rewriter.visit(tree);
     28     }
     29 
     30     /** 无效的逻辑键集合 */
     31     private final Collection<String> invalidKeys;
     32 
     33     /**
     34      * 创建{@link LogicExprRewriter}对象。
     35      *
     36      * @param invalidKeys 无效的逻辑键集合。
     37      */
     38     private LogicExprRewriter(Collection<String> invalidKeys) {
     39         this.invalidKeys = invalidKeys;
     40     }
     41 
     42     @Override
     43     public String visitAnd(AndContext ctx) {
     44         String left = visit(ctx.expr(0));
     45         String right = visit(ctx.expr(1));
     46 
     47         String result;
     48         if (left == null) {
     49             result = (right == null) ? null : right;
     50         }
     51         else {
     52             if (right == null) {
     53                 result = left;
     54             }
     55             else {
     56                 Interval interval = Interval.of(ctx.expr(0).getStop().getStopIndex() + 1, ctx.expr(1).getStart().getStartIndex() - 1);
     57                 String and = ctx.getStart().getInputStream().getText(interval);
     58                 result = left + and + right;
     59             }
     60         }
     61 
     62         return result;
     63     }
     64 
     65     @Override
     66     public String visitOr(OrContext ctx) {
     67         String left = visit(ctx.expr(0));
     68         String right = visit(ctx.expr(1));
     69 
     70         String result;
     71         if (left == null) {
     72             result = (right == null) ? null : right;
     73         }
     74         else {
     75             if (right == null) {
     76                 result = left;
     77             }
     78             else {
     79                 Interval interval = Interval.of(ctx.expr(0).getStop().getStopIndex() + 1, ctx.expr(1).getStart().getStartIndex() - 1);
     80                 String and = ctx.getStart().getInputStream().getText(interval);
     81                 result = left + and + right;
     82             }
     83         }
     84 
     85         return result;
     86     }
     87 
     88     @Override
     89     public String visitVar(VarContext ctx) {
     90         // 去掉左右的括号。例如(a) => a
     91         String key = ctx.getText().substring(1, ctx.getText().length() - 1);
     92         boolean invalid = isInvalidKey(key);
     93         return invalid ? null : ctx.getText();
     94     }
     95 
     96     @Override
     97     public String visitGroup(GroupContext ctx) {
     98         String expr = visit(ctx.expr());
     99         return (expr == null) ? null : "(" + expr + ")";
    100     }
    101 
    102     private boolean isInvalidKey(String key) {
    103         return invalidKeys.contains(key);
    104     }
    105 
    106 }
    View Code

     4、最后看看单元测试

     1 package com.mycomp.antlr4.le;
     2 
     3 import static org.junit.Assert.assertEquals;
     4 import static org.junit.Assert.assertNull;
     5 
     6 import java.util.Arrays;
     7 import java.util.Collections;
     8 
     9 import org.junit.Test;
    10 
    11 public class LogicExprRewriterTest {
    12 
    13     @Test
    14     public void 无无效键值时_应原样输出() {
    15         String le = "(a) and (b) and ((c) or (d) and (e))";
    16         String result = LogicExprRewriter.rewrite(le, Collections.emptyList());
    17         assertEquals(le, result);
    18     }
    19 
    20     @Test
    21     public void 全部都为无效键值时_应输出null() {
    22         String le = "(a) and (b) and ((c) or (d) and (e))";
    23         String result = LogicExprRewriter.rewrite(le, Arrays.asList("a", "b", "c", "d", "e"));
    24         assertNull(result);
    25     }
    26 
    27     @Test
    28     public void 保留空白字符() {
    29         String le = "(a) 	
     and (b) or (c)";
    30         String result = LogicExprRewriter.rewrite(le, Collections.emptyList());
    31         assertEquals(le, result);
    32     }
    33 
    34     @Test
    35     public void 对于and操作_如果左边无效_则只返回右边() {
    36         String le = "(a) and (b)";
    37         String result = LogicExprRewriter.rewrite(le, Arrays.asList("b"));
    38         assertEquals("(a)", result);
    39     }
    40 
    41     @Test
    42     public void 对于and操作_如果右边无效_则只返回左边() {
    43         String le = "(a) and (b)";
    44         String result = LogicExprRewriter.rewrite(le, Arrays.asList("a"));
    45         assertEquals("(b)", result);
    46     }
    47 
    48     @Test
    49     public void 对于or操作_如果左边无效_则只返回右边() {
    50         String le = "(a) or (b)";
    51         String result = LogicExprRewriter.rewrite(le, Arrays.asList("b"));
    52         assertEquals("(a)", result);
    53     }
    54 
    55     @Test
    56     public void 对于or操作_如果右边无效_则只返回左边() {
    57         String le = "(a) or (b)";
    58         String result = LogicExprRewriter.rewrite(le, Arrays.asList("a"));
    59         assertEquals("(b)", result);
    60     }
    61 
    62     @Test
    63     public void 各类值组合测试() {
    64         String le = "(a) and (b) and ((c) or (d) and (e))";
    65         String result;
    66 
    67         result = LogicExprRewriter.rewrite(le, Arrays.asList("c", "e"));
    68         assertEquals("(a) and (b) and ((d))", result);
    69 
    70         result = LogicExprRewriter.rewrite(le, Arrays.asList("a", "b", "e"));
    71         assertEquals("((c) or (d))", result);
    72     }
    73 
    74 }
    View Code

    就这么简单!

  • 相关阅读:
    centos下安装Anaconda
    centos下安装python2.7.9和pip以及数据科学常用的包
    mysql基础(5)-关联(mysql+pandas)
    mysql基础(4)-数据导入
    mysql基础(3)-高级查询
    mysql基础(2)-数据处理(mysql+pandas)
    mysql基础(1)-基本操作
    创建线程的三种方法
    Jar 包 及运行Jar包
    导出成可运行jar包时所遇问题的解决办法
  • 原文地址:https://www.cnblogs.com/yang-wu/p/5111522.html
Copyright © 2011-2022 走看看