REA 表达式语法¶
REA(Rule Expression Assistant)是 RulEuler 的规则文本编辑器,将人类可读的表达式编译为引擎可执行格式。
系统符号全大写。变量 / 参数 / 字符串字面量照库原样,不大写。
表达式结构¶
规则由三部分组成:
| 区域 | 含义 | 语法 |
|---|---|---|
| 如果 | 条件 | 变量 操作符 值 [AND/OR ...],可用值函数 |
| 那么 | 命中动作 | 变量 = 值 或动作函数,分号分隔 |
| 否则 | 未命中动作 | 同那么 |
引用格式¶
| 类型 | 格式 | 示例 |
|---|---|---|
| 变量 | 类别.变量名 |
FlightInfo.arrival_time |
| 参数 | 直接写参数名 | can_score |
属性名可以是保留字:FlightInfo.WEEK、Order.MONTH 合法。类别名和参数名不行:WEEK.foo、裸 DATE 非法。
操作符¶
| 文本 | 含义 | 适用类型 |
|---|---|---|
== |
等于 | 所有 |
!= |
不等于 | 所有 |
> >= < <= |
比较 | 数值、日期 |
CONTAIN / NOTCONTAIN |
包含 | 字符串 |
IN / NOTIN |
在列表中 | 所有 |
MATCH / NOTMATCH |
正则匹配 | 字符串 |
STARTWITH / ENDWITH |
前缀/后缀 | 字符串 |
EQUALSIGNORECASE |
忽略大小写相等 | 字符串 |
只认全大写。Contain、In、and 会报错。
值类型¶
| 类型 | 写法 | 示例 |
|---|---|---|
| 字符串 | 双引号包裹 | "国际" |
| 数字 | 直接写 | 5、-3.14 |
| 布尔 | TRUE / FALSE |
TRUE |
| 变量引用 | 类别.变量名 |
FlightInfo.gate_id |
| 参数引用 | 裸参数名 | threshold |
| 列表 | 圆括号逗号分隔 | ("A", "B", "C") |
| 函数 | 见下方 | TRIM(FlightInfo.name) |
| 运算 | + - * / %,两侧空格 |
FlightInfo.score + 10 |
Boolean 类型¶
Boolean 变量和返回 Boolean 的函数支持隐式和显式两种写法:
条件区:
# 隐式 — 等价于 == TRUE
FlightInfo.is_international
LISTEMPTY(FlightInfo.tags)
# 显式
FlightInfo.is_international == TRUE
FlightInfo.is_international == FALSE
赋值区:
Warning
布尔值只认 TRUE / FALSE,true、True 会报错。
空值:ISNULL / ISNOTNULL¶
条件里的谓词,对齐 LISTEMPTY。编成引擎「为空 / 不为空」,不是函数,不能赋值、不能再比较。
ISNULL(FlightInfo.gate)
ISNOTNULL(FlightInfo.gate)
ISNULL(FlightInfo.gate) AND FlightInfo.score > 0
ISNULL(TRIM(FlightInfo.name))
ISNULL(FlightInfo.score) OR FlightInfo.score == -999
这些非法:FlightInfo.gate NULL(不要后缀)、ISNULL(x) == TRUE、flag = ISNULL(x)、ISNULL(1)、ISNULL(score + 10)。
哨兵默认值(业务用 -999 表示缺失)写 ==,不要另造函数。
同一条 ISNULL,空串在两条路径结果不同
ISNULL(""):引擎 NullAssertor 当空(isBlank → true,全量命中)。灰度 ConditionEvaluator 只认 == null(false,灰度放过)。x="" 时灰度切全量会翻案。不要改灰度去对齐。缺字段 / null 两边都是 true。
灰度 SDK¶
condition_expr 是跨进程契约:服务端下发文本,业务方 ruleuler-client 本地求值。存量后缀 x NULL / x Null 求值器永久认,不会改写成 ISNULL。
新写 ISNULL(...) 或四则的灰度规则,要求业务方 ruleuler-client 升到本期及以后。未升级的老 SDK 遇到 ISNULL( 会报「灰度条件不支持函数」,遇到 * 会报意外字符。
四则:+ - * / %¶
条件左边只能是「一个原子 + 一串数字」:FlightInfo.score + 10 > 80。两个变量相加不能放左边,先赋值到参数再比较。比较左边不能是数字。
赋值右边、比较右边、函数实参可以挂完整运算:
can_score = FlightInfo.score + 10
can_score = FlightInfo.amount * 1.1
can_score = ABS(FlightInfo.score) + 10
can_score = 1.1 * (FlightInfo.score + 10)
ABS(FlightInfo.score + 10) > 5
优先级 * / % 先于 + -(引擎 MVEL)。括号只出现在运算符右边。值不能以括号开头:(score + 10) * 1.1 写成 1.1 * (score + 10);(score + 10) > 80 去掉括号。
二元运算符两侧都要空格:score - 10 合法;score-10、score -10 非法(后者 -10 被认成负数)。
+ 不做字符串拼接承诺。IN ("A1", "A2") 的括号是列表,不是运算。
比较两边都是复合运算(score + a > bonus + b)非法,先赋值。不会自动翻转比较。
比较左边禁止数字
80 < score + bonus 全量、灰度都非法。引擎 left 没有字面量槽,编成参数名会炸。两个变量相加也一样:先赋值到参数再比较。
函数¶
REA 对用户只暴露一套调用语法。编译期落到引擎已有的 Method / CommonFunction,不改运行时。
值函数进条件左/右、赋值右边。动作函数只进那么/否则。LISTSORT 两种都能写。
# 扁平(推荐)
TRIM(FlightInfo.name) == ""
LISTEMPTY(FlightInfo.tags) AND MAPHAS(FlightInfo.extra, "k")
ABS(FlightInfo.score) >= 10
NOW()
# 命名空间(补全分组 / 消歧)
STRING.TRIM(FlightInfo.name)
MATH.ABS(FlightInfo.score)
DATE.NOW()
LIST.SIZE(FlightInfo.tags)
# 集合聚合(第二参是属性名,不是变量)
COUNT(Order.items) > 0
SUM(Order.items, amount) > 1000
# 赋值
gate_type = TRIM(FlightInfo.gate_id)
can_score = ABS(MIN(FlightInfo.score, 100))
# 那么 / 否则(动作函数,无返回值)
LISTADD(FlightInfo.tags, "INTL"); risk_level = "high"
MAPPUT(FlightInfo.extra, "level", "VIP")
这些会报错:
LISTADD(FlightInfo.tags, "VIP") == TRUE # 动作函数不能进条件
can_score = LISTADD(FlightInfo.tags, "VIP") # 动作函数没有返回值
TRIM(FlightInfo.name) # 值函数不能当一句动作,必须赋值
listAdd(xs, "VIP") # 只认全大写
SUM(Order.items.amount) # 第二参是属性名,不要写成点号路径
命名空间前缀 STRING MATH DATE LIST MAP 硬保留,大小写不敏感拦类别名/参数名。Date / date / DATE 都不能当类别。不拦中文:日期.arrival 合法。DateInfo 合法。
字符串 → urule.stringAction¶
TRIM SUBSTRING SUBSTRINGFROM SUBSTRINGTO LOWER UPPER LENGTH CHARAT INDEXOF LASTINDEXOF REPLACE SPLIT
数学 → urule.mathAction¶
ABS MAX MIN(两标量,不是集合)SIN COS TAN COT LN LOG10 ROUND
日期 → urule.dateAction¶
NOW PARSEDATE FORMATDATE ADDYEARS / ADDMONTHS / ADDDAYS / ADDHOURS / ADDMINUTES / ADDSECONDS 以及对应 SUB*。YEAR MONTH WEEK DAY HOUR MINUTE SECOND。DIFFMILLIS / DIFFSECONDS / DIFFMINUTES / DIFFHOURS / DIFFDAYS / DIFFWEEKS / DIFFMONTHS。
日期怪癖(不改引擎)
MONTH返回 0-11,0=一月。MONTH(NOW()) == 9是十月不是九月WEEK是DAY_OF_WEEK:1=周日 … 7=周六,不是 ISO 周一=1DAY对应引擎方法getay(历史拼写错误)
List / Map¶
值:LISTSIZE LISTMAX LISTMIN LISTCONTAINS LISTEMPTY LISTSORT LISTRETRIVE MAPGET MAPSIZE MAPHAS
动作(只进那么/否则):LISTADD LISTREMOVE MAPPUT MAPREMOVE
LISTRETRIVE 对应引擎方法 retrive(拼写如此)。
LISTSORT / LISTRETRIVE 的属性名参数写裸名(或字符串字面量),编译成字符串:
LISTSORT(Order.items, amount, "正序")。
引擎排序方式的升序词面只认 1 / true / 正序,其它(包括英文 "asc")一律倒序。
LISTREMOVE 的字面量实参按字符串处理:从数字元素集合删除时,用变量传要删的值。
集合聚合 → CommonFunction¶
COUNT(xs) 无属性。SUM AVG MAXOF MINOF 第二参是属性 name:SUM(Order.items, amount)。
MAX/MIN 留给两参数值比较。集合对象属性用 MAXOF/MINOF。
灰度条件不支持普通函数(ISNULL / ISNOTNULL 除外),四则按上面的规则可以写。
自定义(项目动作库)¶
动作库里的 Spring Bean 方法,语法和内置命名空间相同。REA 里 Bean、方法都大写,查库忽略大小写:
动作库仍是 riskService / score。没导入或对不上直接报错,不猜。规则集需要 import-action-library。
自定义函数(模板打 jar 上传)见 自定义函数。
不在编辑器里写脚本当函数。本期不重做动作库编辑器,只消费已有 .al.xml 元数据。
条件表达式示例¶
FlightInfo.arrival_time > 5
FlightInfo.arrival_time > 5 AND FlightInfo.is_international
FlightInfo.airline IN ("CA", "MU", "CZ")
FlightInfo.arrival_time > 5 AND (FlightInfo.flight_type == "国内" OR FlightInfo.flight_type == "国际")
ABS(FlightInfo.score) >= 10 AND LISTCONTAINS(FlightInfo.tags, "VIP")
ISNULL(FlightInfo.gate) OR FlightInfo.score + 10 > 80
threshold < FlightInfo.score + FlightInfo.bonus
Note
同层不支持 AND/OR 混用,需要混用时请用括号分组。
赋值表达式示例¶
can_score = 5
can_score = 5; risk_level = "high"
can_score = ABS(FlightInfo.score)
can_score = FlightInfo.amount * 1.1
can_score = 1.1 * (FlightInfo.score + 10)
LISTADD(FlightInfo.tags, "INTL"); risk_level = "high"
自动补全¶
- 无
.时提示:变量类别名、参数名、操作符、AND/OR、TRUE/FALSE、ISNULL()、函数 - 输入
.后提示:该类别下的变量属性 STRING./MATH./DATE./LIST./MAP.以及自定义 Bean 名后只出该组方法- 选中函数插入
TRIM(),光标进括号 - 条件区不出
LISTADD等动作函数