Aviator和MVEL實現Map過濾(通過動態表達式求值)—Java表達式引擎的使用


pom

<dependency>
    <groupId>com.googlecode.aviator</groupId>
    <artifactId>aviator</artifactId>
    <version>4.2.10</version>
</dependency>
<dependency>
    <groupId>org.mvel</groupId>
    <artifactId>mvel2</artifactId>
    <version>2.4.10.Final</version>
</dependency>
View Code

對單一map的簡單操作

import java.util.HashMap;
import java.util.Map;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorBigInt;
import com.googlecode.aviator.runtime.type.AviatorObject;

public class App 
{
    static{
        //注冊函數
        AviatorEvaluator.addFunction(new MinFunction());
    }
    
    public static void main( String[] args )
    {
        Map<String, Object> sourceMapItem = new HashMap<String, Object>();
        sourceMapItem.put("pt", "mi");
        sourceMapItem.put("ab", "abc");
        sourceMapItem.put("xy", "xyz");
        sourceMapItem.put("int1", 135);
        sourceMapItem.put("int2", 68);
        sourceMapItem.put("decimal2", 19.83);
        sourceMapItem.put("mydate", "2020-10-10 28:35:00");
        sourceMapItem.put("value", 999);
        String expStr = "pt == 'mi'";
        Object result;       
        result =  AviatorEvaluator.execute(expStr, sourceMapItem);
        System.out.println(expStr+" ---  "+result);
        //邏輯與 + 簡單表達式        
        expStr = "pt == 'mi' && ab == 'abc' && int1 > 100 && decimal2 < 20.50";
        result =  AviatorEvaluator.execute(expStr, sourceMapItem);
        System.out.println(expStr+" ---  "+result);
        //邏輯與或組合        
        expStr = "pt == 'mi1' || (decimal2 < 20.50 && xy+'X' == 'xyzX')";
        result =  AviatorEvaluator.execute(expStr, sourceMapItem);
        System.out.println(expStr+" ---  "+result);
        //三目運算        
        expStr = "pt == 'mi1' ? ab == 'ab' : ab == 'abc'";
        result =  AviatorEvaluator.execute(expStr, sourceMapItem);
        System.out.println(expStr+" ---  "+result);
        //函數
        expStr = "pt != 'mi1' && string.length(xy) >= 5 ";
        result =  AviatorEvaluator.execute(expStr, sourceMapItem);
        System.out.println(expStr+" ---  "+result);      
        //自定義函數
        expStr = "pt == 'mi1' || myMin(int2 , int1) > 10 ";
        result =  AviatorEvaluator.execute(expStr, sourceMapItem);
        System.out.println(expStr+" ---  "+result);
        

    }
    
    static class MinFunction extends AbstractFunction {
        @Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
            Number left = FunctionUtils.getNumberValue(arg1, env);
            Number right = FunctionUtils.getNumberValue(arg2, env);
            return new AviatorBigInt(Math.min(left.doubleValue(), right.doubleValue()));
        }

        public String getName() {
            return "myMin";
        }
    }
}
View Code

輸出結果

pt == 'mi' ---  true
pt == 'mi' && ab == 'abc' && int1 > 100 && decimal2 < 20.50 ---  true pt == 'mi1' || (decimal2 < 20.50 && xy+'X' == 'xyzX') --- true pt == 'mi1' ? ab == 'ab' : ab == 'abc' --- true pt != 'mi1' && string.length(xy) >= 5 --- false pt == 'mi1' || myMin(int2 , int1) > 10 --- true

一個擴展的任務,Aviator對嵌套map進行stream過濾

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import com.googlecode.aviator.AviatorEvaluator;

public class MapForFilter {
    
    final static String EXPSTR = "(pt == 'mi' && ab == 'abc' && int1 > 100) || "
            + "(decimal2 < 20.50 && string.length(xy) >= 5)";
    
    public static void main( String[] args )
    {
        aviatorEvaluatorDemo();                  
    }

    private static void aviatorEvaluatorDemo() {
        Map<String, Map<String, Object>> sourceMap = fillData();
 
        Map<String, Map<String, Object>> resultdMap = parseMapForFilter(sourceMap,EXPSTR);
        
        System.out.println("過濾后:"+resultdMap);
    }

    private static Map<String, Map<String, Object>> fillData() {
        Map<String, Map<String, Object>> sourceMap = new HashMap<String, Map<String, Object>>();
        
        Map<String, Object> sourceMapItem1 = new HashMap<String, Object>();
        sourceMapItem1.put("pt", "mi");
        sourceMapItem1.put("ab", "abc");
        sourceMapItem1.put("xy", "xyz");
        sourceMapItem1.put("int1", 135);
        sourceMapItem1.put("int2", 68);
        sourceMapItem1.put("decimal2", 19.83);
        sourceMapItem1.put("mydate", "2020-10-10 28:35:00");
        sourceMapItem1.put("value", 999);
        
        Map<String, Object> sourceMapItem2 = new HashMap<String, Object>();
        sourceMapItem2.put("pt", "oppo");
        sourceMapItem2.put("ab", "ab");
        sourceMapItem2.put("xy", "xy");
        sourceMapItem2.put("int1", 24);
        sourceMapItem2.put("int2", 56);
        sourceMapItem2.put("decimal2", 519.83);
        sourceMapItem2.put("mydate", "2020-10-11 28:35:00");
        sourceMapItem2.put("value", 888);
        
        Map<String, Object> sourceMapItem3 = new HashMap<String, Object>();
        sourceMapItem3.put("pt", "oppo");
        sourceMapItem3.put("ab", "cde");
        sourceMapItem3.put("xy", "xy");
        sourceMapItem3.put("int1", 213);
        sourceMapItem3.put("int2", 473);
        sourceMapItem3.put("decimal2", 119.83);
        sourceMapItem3.put("mydate", "2020-10-12 28:35:00");
        sourceMapItem3.put("value", 777);
        
        Map<String, Object> sourceMapItem4 = new HashMap<String, Object>();
        sourceMapItem4.put("pt", "oppo");
        sourceMapItem4.put("ab", "fgh");
        sourceMapItem4.put("xy", "xyz123");
        sourceMapItem4.put("int1", 156);
        sourceMapItem4.put("int2", 215);
        sourceMapItem4.put("decimal2", 15.92);
        sourceMapItem4.put("mydate", "2020-10-13 28:35:00");
        sourceMapItem4.put("value", 666);
        
        sourceMap.put("2020-10-10",sourceMapItem1);
        sourceMap.put("2020-10-11",sourceMapItem2);
        sourceMap.put("2020-10-12",sourceMapItem3);
        sourceMap.put("2020-10-13",sourceMapItem4);
        return sourceMap;
    }
    
    public static Map<String, Map<String, Object>> parseMapForFilter(Map<String, Map<String, Object>> sourceMap,String exp) {
        
        return Optional.ofNullable(sourceMap).map(
                (v) -> {
                    Map<String, Map<String, Object>> params = v.entrySet().stream()
                            .filter(map -> checkItem(map.getValue(),exp)) 
                            .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));  

                    return params;
                }
        ).orElse(null);
    }

    private static boolean checkItem(Map<String, Object> itemMap,String exp) {

        Object result = AviatorEvaluator.execute(exp, itemMap);
        return (boolean) result;
    }
}

MVEL的實現同樣的過濾任務

為了適應MVEL,注意表達式的字符有少許不同。

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.mvel2.MVEL;

public class MapForFilterMvel {

    final static String EXPSTR = "(m.pt == 'mi' && m.ab == 'abc' && m.int1 > 100) || "
            + "(m.decimal2 < 20.50 && m.xy.length()>5)"; 
    
    public static void main( String[] args )
    {
        mvelDemo();             
    }
    
    private static void mvelDemo() {
        Map<String, Map<String, Object>> sourceMap = fillData();
        
        Map<String, Map<String, Object>> resultdMap = parseMapForMvelFilter(sourceMap);
        
        System.out.println("過濾后:"+resultdMap);
    }

    private static Map<String, Map<String, Object>> parseMapForMvelFilter(Map<String, Map<String, Object>> sourceMap) {
        
        return Optional.ofNullable(sourceMap).map(
                (v) -> {
                    Map<String, Map<String, Object>> params = v.entrySet().stream()
                            .filter(map -> checkItemByMvel(map.getValue())) 
                            .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));  

                    return params;
                }
        ).orElse(null);
    }

    private static boolean checkItemByMvel(Map<String, Object> value) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("m", value);
        Object object = MVEL.eval(EXPSTR, paramMap);
        return (boolean)object;
    }
    
    private static Map<String, Map<String, Object>> fillData() {
        Map<String, Map<String, Object>> sourceMap = new HashMap<String, Map<String, Object>>();
        
        Map<String, Object> sourceMapItem1 = new HashMap<String, Object>();
        sourceMapItem1.put("pt", "mi");
        sourceMapItem1.put("ab", "abc");
        sourceMapItem1.put("xy", "xyz");
        sourceMapItem1.put("int1", 135);
        sourceMapItem1.put("int2", 68);
        sourceMapItem1.put("decimal2", 19.83);
        sourceMapItem1.put("mydate", "2020-10-10 28:35:00");
        sourceMapItem1.put("value", 999);
        
        Map<String, Object> sourceMapItem2 = new HashMap<String, Object>();
        sourceMapItem2.put("pt", "oppo");
        sourceMapItem2.put("ab", "ab");
        sourceMapItem2.put("xy", "xy");
        sourceMapItem2.put("int1", 24);
        sourceMapItem2.put("int2", 56);
        sourceMapItem2.put("decimal2", 519.83);
        sourceMapItem2.put("mydate", "2020-10-11 28:35:00");
        sourceMapItem2.put("value", 888);
        
        Map<String, Object> sourceMapItem3 = new HashMap<String, Object>();
        sourceMapItem3.put("pt", "oppo");
        sourceMapItem3.put("ab", "cde");
        sourceMapItem3.put("xy", "xy");
        sourceMapItem3.put("int1", 213);
        sourceMapItem3.put("int2", 473);
        sourceMapItem3.put("decimal2", 119.83);
        sourceMapItem3.put("mydate", "2020-10-12 28:35:00");
        sourceMapItem3.put("value", 777);
        
        Map<String, Object> sourceMapItem4 = new HashMap<String, Object>();
        sourceMapItem4.put("pt", "oppo");
        sourceMapItem4.put("ab", "fgh");
        sourceMapItem4.put("xy", "xyz123");
        sourceMapItem4.put("int1", 156);
        sourceMapItem4.put("int2", 215);
        sourceMapItem4.put("decimal2", 15.92);
        sourceMapItem4.put("mydate", "2020-10-13 28:35:00");
        sourceMapItem4.put("value", 666);
        
        sourceMap.put("2020-10-10",sourceMapItem1);
        sourceMap.put("2020-10-11",sourceMapItem2);
        sourceMap.put("2020-10-12",sourceMapItem3);
        sourceMap.put("2020-10-13",sourceMapItem4);
        return sourceMap;
    }

}

對map集合的過濾:結果:

過濾后:
{
2020-10-10={ab=abc, xy=xyz, int2=68, mydate=2020-10-10 28:35:00, int1=135, pt=mi, decimal2=19.83, value=999},
2020-10-13={ab=fgh, xy=xyz123, int2=215, mydate=2020-10-13 28:35:00, int1=156, pt=oppo, decimal2=15.92, value=666}
}

Aviator的優缺點

final static String EXPSTR = "(pt == 'mi' && ab == 'abc' && int1 > 100) || (decimal2 < 20.50 && string.length(xy) >= 5)";
  • 更輕量級。
  • 對map的支持非常好,可以說就是為了Map量身定做的,對key變量直接使用就可以了。
  • 函數只能使用內置函數或者自定義函數。
  • 不支持對象的屬性和方法。
  • 不支持if else和for循環。

MVEL的優點

 final static String EXPSTR = "(m.pt == 'mi' && m.ab == 'abc' && m.int1 > 100) || (m.decimal2 < 20.50 && m.xy.length()>5)"; 
  • 功能更加強大。
  • 使用map需要再包一層,用於指示map的根。稍微有些限制,但也不影響使用,固定一個map變量名就好了。
  • 支持對象屬性、方法,這就厲害了,可以復用全部的java內置class,也可以使用你自己寫的class的屬性和方法。也可以自定義方法。
  • 支持對象new操作,支持斷言,支持對象賦值。
  • 支持if else流程,支持循環。

操作符列表

序號 操作符 結合性 操作數限制
0 () [ ] 從左到右 ()用於函數調用,[ ]用於數組和java.util.List的元素訪問,要求[indx]中的index必須為整型
1 ! - ~ 從右到左 ! 能用於Boolean,- 僅能用於Number,~僅能用於整數
2 * / % 從左到右 Number之間
3 + - 從左到右 + - 都能用於Number之間, + 還能用於String之間,或者String和其他對象
4 << >> >>> 從左到右 僅能用於整數
5 < <= > >= 從左到右 Number之間、String之間、Pattern之間、變量之間、其他類型與nil之間
6 == != =~ 從左到右 ==和!=作用於Number之間、String之間、Pattern之間、變量之間、其他類型與nil之間以及String和java.util.Date之間,=~僅能作用於String和Pattern之間
7 & 從左到右 整數之間
8 ^ 從左到右 整數之間
9 ¦ 從左到右 整數之間
10 && 從左到右 Boolean之間,短路
11 ¦¦ 從左到右 Boolean之間,短路
12 ? : 從右到左 第一個操作數的結果必須為Boolean,第二和第三操作數結果無限制

內置函數

函數名稱 說明
sysdate() 返回當前日期對象java.util.Date
rand() 返回一個介於0-1的隨機數,double類型
print([out],obj) 打印對象,如果指定out,向out打印,否則輸出到控制台
println([out],obj) 與print類似,但是在輸出后換行
now() 返回System.currentTimeMillis
long(v) 將值的類型轉為long
double(v) 將值的類型轉為double
str(v) 將值的類型轉為string
date_to_string(date,format) 將Date對象轉化化特定格式的字符串,2.1.1新增
string_to_date(source,format) 將特定格式的字符串轉化為Date對象,2.1.1新增
string.contains(s1,s2) 判斷s1是否包含s2,返回Boolean
string.length(s) 求字符串長度,返回Long
string.startsWith(s1,s2) s1是否以s2開始,返回Boolean
string.endsWith(s1,s2) s1是否以s2結尾,返回Boolean
string.substring(s,begin[,end]) 截取字符串s,從begin到end,end如果忽略的話,將從begin到結尾,與java.util.String.substring一樣。
string.indexOf(s1,s2) java中的s1.indexOf(s2),求s2在s1中的起始索引位置,如果不存在為-1
string.split(target,regex,[limit]) Java里的String.split方法一致,2.1.1新增函數
string.join(seq,seperator) 將集合seq里的元素以seperator為間隔連接起來形成字符串,2.1.1新增函數
string.replace_first(s,regex,replacement) Java里的String.replaceFirst 方法,2.1.1新增
string.replace_all(s,regex,replacement) Java里的String.replaceAll方法 ,2.1.1新增
math.abs(d) 求d的絕對值
math.sqrt(d) 求d的平方根
math.pow(d1,d2) 求d1的d2次方
math.log(d) 求d的自然對數
math.log10(d) 求d以10為底的對數
math.sin(d) 正弦函數
math.cos(d) 余弦函數
math.tan(d) 正切函數
map(seq,fun) 將函數fun作用到集合seq每個元素上,返回新元素組成的集合
filter(seq,predicate) 將謂詞predicate作用在集合的每個元素上,返回謂詞為true的元素組成的集合
count(seq) 返回集合大小
include(seq,element) 判斷element是否在集合seq中,返回boolean值
sort(seq) 排序集合,僅對數組和List有效,返回排序后的新集合
reduce(seq,fun,init) fun接收兩個參數,第一個是集合元素,第二個是累積的函數,本函數用於將fun作用在集合每個元素和初始值上面,返回最終的init值
seq.eq(value) 返回一個謂詞,用來判斷傳入的參數是否跟value相等,用於filter函數,如filter(seq,seq.eq(3)) 過濾返回等於3的元素組成的集合
seq.neq(value) 與seq.eq類似,返回判斷不等於的謂詞
seq.gt(value) 返回判斷大於value的謂詞
seq.ge(value) 返回判斷大於等於value的謂詞
seq.lt(value) 返回判斷小於value的謂詞
seq.le(value) 返回判斷小於等於value的謂詞
seq.nil() 返回判斷是否為nil的謂詞
seq.exists() 返回判斷不為nil的謂詞

常量和變量

說明
true 真值
false 假值
nil 空值
$digit 正則表達式匹配成功后的分組,$0表示匹配的字符串,$1表示第一個分組 etc.

參考資料

5.0版以后成為了aviatorscript

https://github.com/killme2008/aviatorscript

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM