项目上需要用到实现自定义的一套 filter dsl, 来增强 es 搜索的能力. 采用了Antlr4作为语法解析工具, Antlr4 需要我们提供一套语法规则来帮助构建语法树, 那我们想要实现的效果大概有以下几个点:
语句之间的逻辑关系支持 "与" 和 "或", 可以支持"&&", "and" "||", "or" 的格式
支持的条件判断有"eq" , "ne", "gt", "ge", "lt", 'le", "in", "has".
支持的数据的类型有两种, 字符串"String"和数字"Number"以及两者的数组类型.
同时需要支持 "( )" 的无限嵌套形式.
下面是个参考示例:
" 'sku.name' has '香水' and ('sku.price' le 120.5 || 'shop.address' in ['湖南', '广州'])"
意思很清晰就是: 商品名包含香水, 价格小于120.5或店铺地址在湖南或者广州.
这种语法其实算比较简单的了,规则不多也比较容易理解, 要说难点的话可能就是如何优雅地处理类似 '(xxx (xxx) (xxx) ) (xxx)' 这样的结构.下面也是我自己摸索出来的tips 可以帮助更快的去构建语法树.
列出一些示例, 基本涵盖我们需要支持的所有规则, 可以先从简单的开始
简单语句: " 'sku.name' eq '手表' "
复合语句: '' 'address' in ['CN', 'US'] and 'price' gt 10"
嵌套语句: " ('address' in ['CN', 'US'] and 'price' gt 10) || ('sku.name' eq '手表' && 'shop.name' has '中国')"
模式拆解. 不难看出,这个语法的组成模式就是表达式之间的逻辑组合, 抽象成 expr bool expr , expr是最小的语句单元, 可以得到true或者false的结果, bool是'&&' '||'. 那么对于嵌套的语句呢? 一个嵌套语句是多个嵌套语句或者expr的组合, 也可以得到true或者false的结果. 那么使用一个更上层的符号stmt来表示, stmt的child可以是expr或者stmt.
针对列出来的语句, 尝试动手画出来, 通过不断的尝试和优化找到合理的语法树结构,例如
使用antlr的语法进行描述. 在有了合理的语法树之后,再将它转换成对应的描述方式就比较容易了. 下面是我的g4文件
grammar EsFilter;
filter : stmt? EOF;
stmt
: expr
| '(' stmt ')'
| stmt Condition stmt
;
expr: val Op val;
val: const | arr ;
Op
: 'eq'
| 'ne'
| 'gt'
| 'ge'
| 'lt'
| 'le'
| 'has'
| 'in'
;
Condition
: '&&'
| 'and'
| '||'
| 'or'
;
const
: NUMBER
| STRING
;
arr : '[' const (',' const )* ']';
NUMBER
: '-'? INT ('.' [0-9]+)? EXP?
;
fragment INT
: '0' | [0]* [1-9] [0-9]*
;
fragment EXP
: [Ee] [+-]? INT
;
//STRING : ''' (ESC | SAFECODEPOINT)* ''';
STRING : ''' .*? ''';
WS : ('\t' | ' ' | '\n')+ -> skip;