表达式语言指南(Expression Language Guide)
编辑人: le.zw 邮箱: [email protected]
1.概述
Apache NiFi 中的所有数据都通过流文件“FlowFile”的抽象表示。FlowFile由两个主要部分组成:内容和属性。FlowFile的内容部分表示要操作的数据。例如,如果使用GetFile 组件从本地文件系统中获取文件,则该文件的内容将成为FlowFile 的内容。
FlowFile的属性部分表示有关数据本身或额外的一些元数据信息。属性是键值对,表示对数据信息以及对于适当的输出和处理数据有用的信息。继续以上面的获取文件的组件为例,FlowFile将具有一个名为
filename
的属性反映文件系统上文件的名称。另外,FlowFile还有一个
path
属性反映此文件所在的文件系统上的目录。FlowFile还将具有一个名为
uuid
的属性,这是此FlowFile 的唯一标识符。有关核心属性的完整列表,请查看开发人员指南的
FlowFile
部分
如果用户无法使用这些属性,那么将这些属性放在FlowFile上就没什么用处。NiFi表达式语言提供了访问这些属性并将它们与其他值进行比较以及操作的功能
2.NiFi表达式的结构
NiFi表达式语言始终以起始分隔符
${
开头并以结束分隔符
}
结束。在开始和结束分隔符之间是表达式本身的文本。在最基本的形式中,表达式只包含一个属性名称。 例如,
${filename}
将返回
filename
属性的值。
在稍微复杂的示例中,我们可以返回对此值的操作。例如,我们可以通过调用
toUpper
函数:
${filename:toUpper()}
返回文件名的大写值。在这种情况下,我们访问
filename
属性,然后使用
toUpper
函数来操作它的值。函数调用由5个元素组成。首先,有一个函数调用分隔符
:
。第二个是函数的名称 - 在本例中就是
toUpper
。接下来是一个左括号(
(
),后跟函数参数,参数必填与否取决于调用哪个函数。在这个例子中,我们使用
toUpper
函数,不需要任何参数,因此可忽略不填。 最后,右括号(
)
)表示函数调用的结束。表达式语言支持许多不同的功能,以实现许多不同的目标。有些函数提供String(文本)操作,例如
toUpper
功能。其他的, 比如
equals
和
matches
函数,提供比较功能。还有用于操纵日期和时间以及执行数学运算的函数。下面在
<<functions>>
部分描述了这些函数,并解释了函数的作用,它需要的参数以及它返回的信息类型。
当我们对属性执行函数调用时,如上所述,我们将属性称为函数的主题(
subject
),因为属性是函数运行的实体。然后我们可以将多个函数调用链接在一起,其中第一个函数的返回值成为第二个函数的主题,其返回值成为第三个函数的主题, 依此类推。继续我们的示例,我们可以使用表达式将多个函数链接在一起
${filename:toUpper():equals('HELLO.TXT')}
。可以链接在一起的函数数量没有限制。
可以使用表达式语言引用FlowFile上的任何属性。但是,如果属性名称包含特殊字符,必须通过转义引用属性名称。以下字符都是特殊字符:
-
$
(美元符号) -
|
(管道符) -
{
(大括号开始符) -
}
(大括号结束符) -
(
(小括号开始符) -
)
(小括号结束符) -
[
(中括号开始符) -
]
(中括号结束符) -
,
(逗号) -
:
(冒号) -
;
(分号) -
/
(斜杠) -
*
(星号) -
'
(单引号) -
(空格)
-
\t
(制表符) -
\r
(回车符) -
\n
(换行符)
此外,如果一个数字是属性名称的第一个字符,它将被视作特殊字符。如果属性中存在任何特殊字符,则使用单引号或双引号转义。表达式语言允许单引号和双引号可互换使用。例如,以下内容可用于转义名为
my attribute
的属性:
${"my attribute"}
或者
${'my attribute'}
。
在此示例中,要返回的值是“my attribute”属性的值(如果存在)。如果该属性不存在,则表达式语言将查找名为“my attribute”的系统环境变量。如果无法找到它,它将查找名为“my attribute”的JVM系统属性。最后,如果这些都不存在,表达语言将返回一个空字符串
null
值。表达式语言将在层次结构中搜索匹配的属性。有关层次结构的描述,请参见
表达式语言层次结构
结构
还存在一些函数没有主题。这些函数只需通过表达式的开头来调用,例如
${hostname()}
。然后,这些函数也可以一起更改,例如,
${hostname():toUpper()}
。尝试使用主题来验证函数将出错。在下面的
<<functions>>
部分中,一些函数将在其描述中清楚地表明它们不需要主题。
通常,我们需要将两个不同属性的值相互比较。我们可以通过使用嵌入式表达式来实现这一目标。例如,我们可以检查一下
filename
属性与
UUID
属性是否相同:
${filename:equals( ${uuid} )}
。另请注意,我们在括号里面有一个空格,在
equals
方法和嵌入式表达式之间。这不是必需的,也不会影响以任何验证表达式的方法。它旨在使表达更容易阅读。分隔符之间的表达式语言忽略空格。因此,我们可以使用表达式
$ {filename:equals($ {uuid})}
或者
$ {filename:equals($ {uuid})}
两个表达式表达相同意思。但是,我们不能使用
${file name:equals(${uuid})}
,因为这将导致在
file
和
name
被视作不同的属性, 而不是单个属性
filename
。
2.1表达式语言的层次结构
当使用表达式语言按名称引用属性时,NiFi 将按照规定好的层次结构搜索属性值的定义。 NiFi中当前属性匹配的层次结构如下:
-
在 FlowFile 中搜索属性/键
-
搜索属性/键的进程组变量
-
在文件注册表文件中搜索属性/键
-
在 NiFi JVM 属性中搜索属性/键
-
在系统环境变量中搜索属性/键
NiFi 将搜索并返回匹配属性第一次出现的值。如果没有找到匹配的属性,则返回null
3.应用程序中的表达式语言
表达式语言在整个 NiFi 应用程序中被大量使用,用于配置组件属性。但是,并非所有组件属性都支持表达式语言。属性是否支持表达式语言由组件的开发者在组件开发时就确定好了。但是,该应用程序应该清楚地说明每个属性是否支持表达式语言。
在应用程序中,配置组件属性时,用户界面上的属性名称旁边会提供一个信息图标( Info )。将鼠标悬停在此图标上将显示提示,提供有关该属性的有用信息。此信息包括属性的描述,默认值(如果有),历史配置值(如果有)以及此属性的表达式语言的取值范围。有三个值,分别是:NONE →VARIABLE_REGISTRY → FLOWFILE_ATTRIBUTES。
-
NONE - 此属性不支持表达式语言。
-
VARIABLE_REGISTRY 按层次结构构造如下:
-
在模块级别定义的变量,然后递归地定义到更高的模块,直到根模块。
-
自定义properties文件中的变量,通过NiFi nifi.properties文件中的nifi.variable.registry.properties属性进行设置。
-
在JVM级别和系统属性中定义的环境变量。
-
-
FLOWFILE_ATTRIBUTES - 数据流的属性。
4.转义表达式
有时,属性支持表达式语言,但用户希望使用遵循与表达式语言相同语法的特殊符号。例如,用户可能希望将属性值配置为文字
Hello ${UserName}
。在这种情况下,这可以通过在表达式之前使用额外的
$
(美元符号)来转义它(即,
Hello $${UserName}
)来完成。除非该
$
字符用于转义表达式,否则不应转义。例如,该值
Hello $$User$$Name
不应转义
$
字符,因此将使用的文字值为
Hello $$User$$Name
.
如果
$
在 a 之前连续遇到两个以上的字符
{
,则每对
$
字符都将被视为字符的转义
$
。转义将从左到右执行。为了帮助说明这一点,假设属性
abc
值为
xyz
。参考下表的表达式及其相应的值:
表达式 | 值 | 备注 |
---|---|---|
${abc} | xyz | |
$${abc} | ${abc} | |
$$${abc} | $xyz | |
$$$${abc} | $${abc} | |
$$$$${abc} | $$xyz | |
I owe you $5 | I owe you $5 | 这里没有实际的表达式 |
You owe me $$5 too | You owe me $$5 too | $ 字符不会被转义,因为它不是紧跟在表达式之前 |
Unescaped $$${5 because no closing brace | Unescaped $$${5 because no closing brace | 因为这里没有右大括号,所以没有实际的表达式,因此 $ 字符不会被转义 |
Unescaped $$${5} because no closing brace |
<Error>
|
此表达式无效,因为它等于转义的$,后跟${5},并且${5}不是有效的表达式。这里数字必须转义才能表示成字符串 |
Unescaped $$${'5'} because no closing brace | Unescaped $ because no closing brace | 没有名为 5 的属性,因此表达式的计算结果为空字符串。 $$ 评估为单个(转义)$,因为它紧接在表达式之前 |
5.表达式编辑器
配置组件属性的值时,NiFi 用户界面可使用表达式语言编辑器提供表达式语言的帮助。通过键入开始表达式
${
,编辑器开始突出显示括号和大括号,以便用户可以轻松地分辨哪个左括号或左大括号匹配哪个右括号或右大括号。
编辑器还提供了可在当前光标位置使用的所有函数的列表,从而提供了上下文相关的帮助。要激活此功能,请按键盘上的Ctrl + Space。用户还可以键入函数名称的一部分,然后按Ctrl + Space查看可以使用以相同前缀开头的所有函数。例如,如果我们输入编辑器
${filename:to
然后按Ctrl + Space,我们会弹出一个列出六种不同功能的弹出窗口:
toDate
,
toLower
,
toNumber
,
toRadix
,
toString
和
toUpper
。然后我们可以继续输入以缩小显示哪些函数的范围,或者我们可以通过用鼠标双击它或使用箭头键突出显示所需功能并按Enter键从列表中选择一个功能。
6.函数
函数提供了一种操作和比较属性值的便捷方法。表达式语言提供了许多不同的函数来满足对属性操作的需求。每个函数接受零到多个参数并返回单个值。然后可以将这些函数链接在一起以构建强大的表达式来验证条件和操作值。有关如何调用和链接函数的更多信息,请参见
NiFi 表达式的结构
。
6.1数据类型
函数的每个参数和函数返回的每个值都具有特定的数据类型。表达式语言支持四种不同的数据类型:
-
String :String是一系列字符,可以包含数字,字母,空格和特殊字符。
-
Number :Number是由一个或多个数字组成的整数(
0
到9
)。从Date数据类型转换为数字时,它们表示为自格林威治标准时间1970年1月1日午夜以来的毫秒数。 -
Decimal :Decimal是一个数值,可以支持小数和更大的值,而精度损失最小。更确切地说,它是一个双精度64位IEEE 754浮点数。由于这种最小的精度损失,这种数据类型不应该用于非常精确的值,例如货币。有关此数据类型中存储的值范围的更多文档,请参阅此内容 link 。 以下是表达式语言支持的小数形式的一些示例 (“E”也可以是小写):
- 1.1
- .1E1
- 1.11E-12
-
Date :Date是一个包含日期和时间的对象。利用 日期操作 和 类型强制转换 函数可以将这种数据类型转换为字符串和数字。如果整个表达式语言表达式被定义为日期,那么将可被转换为具有以下格式的字符串:"<星期> <月> <日> <时>:<分>:<秒> <时区> <年>"。 在Java SimpleDateFormat格式中也表示为“E MMM dd HH:mm:ss z yyyy”。例如:“Wed Dec 31 12:00:04 UTC 2016”。
-
Boolean :Boolean布尔值是
true
或者false
。
表达式语言函数计算后所得的最终值都存储为String类型。
表达式语言通常能够自动将一种数据类型的值强制转换为适合函数的适当数据类型。但是,也提供将值手动强制转换为特定数据类型的函数。有关更多信息,请参阅
类型强制转换
部分。
Number和Decimal类型支持十六进制值,但在被解释为文字时,引用它们必须以“0x”为前缀。例如,如下两个表达式都是有效的(没有引号或“0x”表达式将无法正常运行):
-
${literal("0xF"):toNumber()}
-
${literal("0xF.Fp10"):toDecimal()}
7.布尔表达式
7.1 isNull
表达式语言最强大的功能之一是能够将属性值与其他值进行比较。用于例如通过组件配置属性来处理如何路由输出数据。以下函数用于执行布尔逻辑,例如比较两个值。这些函数中的每一个都设计用于处理Boolean类型的值。
描述
: 如果主题为空,
isNull
函数返回
true
,否则返回
false
。这通常用于确定属性是否存在。
主题类型
: 任意
参数
:无
返回值类型
: Boolean
例子
:
${filename:isNull()}
如果“filename”属性不存在,返回
true
, 如果属性存在,返回
false
。
7.2 notNull
描述
:
notNull
函数返回
isNull
函数相反的值。也就是说,如果主题存在它会返回
true
,否则返回
false
。
主题类型
: 任意
参数
:无
返回值类型
: Boolean
例子
:
${filename:notNull()}
如果“filename”属性存在,返回
true
。如果属性不存在,返回
false
。
7.3 isEmpty
描述
: 如果主题为null、不包含任何字符,或仅包含空格(换行,回车符,空格,制表符等),
isEmpty
函数返回
true
,否则返回
false
。
主题类型
: String
参数
:无
返回值类型
: Boolean
例子
: 如果“filename”属性不存在或仅包含空格,
${filename:isEmpty()}
返回
true
,
${literal(" "):isEmpty()}
以及
${literal(""):isEmpty()}
都返回
true
。
7.4 equals
描述
:
equals
函数被广泛使用并确定其主题是否等于另一个String值。请注意
equals
函数执行两个String值的直接比较。注意不要将此函数与[matches](#10.6 matches)函数混淆 ,后者根据正则表达式计算其主题。
主题类型
: 任意
参数
:
-
value : 要比较的主题的值。必须与主题相同的类型。
返回值类型
: Boolean
例子
: 通过使用表达式
${filename:equals('hello.txt')}
,我们可以检查数据流的文件名是否为“hello.txt”。或者可以检查
hello
是否等于
filename
属性的值:
${hello:equals( ${filename} )}
。
7.5 equalsIgnoreCase
描述
: 与
equals
函数相似,
equalsIgnoreCase
函数将其主题与String值进行比较,如果两个值忽略大小写的情况下是相同的,则返回
true
。
主题类型
: String ,
参数
:
-
value : 要比较主题的值。
返回值类型
: Boolean
例子
: 如果filename等于"hello.txt"或者 "HELLO.TXT"或者 "HeLLo.TxT",
${filename:equalsIgnoreCase('hello.txt')}
都将返回
true
。
7.6 gt
描述
:
gt
函数用于数字比较。如果主题大于其参数,返回
true
。如果主题或参数无法强制转换为数字,则此函数返回
false
。
主题类型
: Number
参数
:
-
value : 要比较主题的数字。
返回值类型
: Boolean
例子
: 如果的内容大小超过1千字节(1024字节),
${fileSize:gt( 1024 )}
将返回
true
。否则,它将返回
false
。
7.7 ge
描述
:
ge
函数用于数字比较。如果主题大于或等于其参数,返回
true
。如果主题或参数无法强制转换为数字,则此函数返回
false
。
主题类型
: Number
参数
:
-
value : 要比较主题的数字。
返回值类型
: Boolean
例子
: 如果FlowFile内容的大小至少(大于或等于)1千字节(1024字节)。
${fileSize:ge( 1024 )}
将返回
true
。否则,它将返回
false
。
7.8 lt
描述
:
lt
函数用于数字比较,如果主题小于其参数,返回
true
。如果主题或参数无法强制转换为数字,则此函数返回
false
。
主题类型
: Number
参数
:
-
value : 要比较主题的数字。
返回值类型
: Boolean
例子
: 如果数据流内容小于1兆字节(1048576字节),
${fileSize:lt( 1048576 )}
将返回
true
。否则,它将返回
false
。
7.9 le
描述
:
le
函数用于数字比较,如果主题小于或等于其参数,返回
true
。如果主题或参数无法强制转换为数字,则此函数返回
false
。
主题类型
: Number
参数
:
-
value : 要比较主题的数字。
返回值类型
: Boolean
例子
: 如果数据流内容的最多(小于或等于)1兆字节(1048576字节),
${fileSize:le( 1048576 )}
将返回
true
。否则,它将返回
false
。
7.10 and
描述
:
and
函数将布尔值作为单个参数。如果主题和参数都是
true
,则返回
true
。如果主题或参数其中任一个是
false
或者不能强制转换为布尔值,函数返回
false
。通常会与嵌入式表达式一起用作参数。
主题类型
: Boolean
参数
:
-
condition : 'and’表达式右手边内容
返回值类型
: Boolean
例子
: 我们可以通过使用表达式检查文件名是否都是小写的并且至少有5个字符
${filename:toLower():equals( ${filename} ):and(
${filename:length():ge(5)}