QueryRecord Processor
描述
根据 FlowFile 的内容评估一个或多个 SQL 查询。 SQL 查询的结果将成为输出 FlowFile 的内容。 例如,这可用于特定于字段的过滤、转换和行级过滤。 可以重命名列、执行简单的计算和聚合等。处理器配置有Record ReaderController Service和Record Writer服务,以便允许传入和传出数据格式的灵活性。 处理器必须配置至少一个用户定义的属性。 属性的名称是将数据路由到的关系,属性的值是 SQL SELECT 语句,用于指定应如何转换/过滤输入数据。 SQL 语句必须是有效的 ANSI SQL,并且由 Apache Calcite 提供支持。 如果转换失败,则原始 FlowFile 将路由到“失败”关系。 否则,所选数据将被路由到关联关系。 如果Record Writer选择从记录继承模式,请务必注意继承的模式将来自结果集,而不是输入记录。 这允许 QueryRecord 处理器的单个实例具有多个查询,每个查询返回一组不同的列和聚合。 但结果是,派生的模式将没有模式名称,因此,如果从记录继承模式,配置的Record Writer不要尝试将模式名称写入为属性,这一点很重要。 有关详细信息,请参阅处理器使用文档。
标签
使用场景
根据记录字段的值过滤记录
"Record Reader" 应设置为适合你数据的 Record Reader。
"Record Writer" 应设置为以所需格式写出数据的 Record Writer。
应添加一个额外的属性。
该属性的名称应是对要保留数据的简短描述。
其值是一条 SQL 语句,从名为 `FLOW_FILE` 的表中为相关行选择所有列。
WHERE 子句选择要保留的数据。也就是说,它与我们要移除的内容完全相反。
建议始终使用双引号引用列名,以避免与 SQL 关键字冲突。
例如,要移除 name 为 George 或 age 小于 18 的记录,我们将添加一个 名为 "adults not george" 的属性,其值选择 name 不是 George 且 age 大于或等于 18 的记录。 因此该值为 `SELECT * FROM FLOWFILE WHERE "name" <> 'George' AND "age" >= 18`
现在添加此属性会给我们一个新的 Relationship,其名称与属性名相同。因此,"adults not george" Relationship 应连接到我们流程中的下一个 Processor。
仅保留特定记录
"Record Reader" 应设置为适合你数据的 Record Reader。
"Record Writer" 应设置为以所需格式写出数据的 Record Writer。
应添加一个额外的属性。
该属性的名称应是对要保留数据的简短描述。
其值是一条 SQL 语句,从名为 `FLOW_FILE` 的表中为相关行选择所有列。
WHERE 子句选择要保留的数据。
建议始终使用双引号引用列名,以避免与 SQL 关键字冲突。
例如,要仅保留某人为成年人(年龄 18 岁或以上)的记录,添加一个名为 "adults" 的属性, 其值是一条选择 age 至少为 18 的记录的 SQL 语句。 因此该值为 `SELECT * FROM FLOWFILE WHERE "age" >= 18`
现在添加此属性会给我们一个新的 Relationship,其名称与属性名相同。因此,"adults" Relationship 应连接到我们流程中的下一个 Processor。
仅保留记录中的特定字段,其中要保留的字段名称已知
"Record Reader" 应设置为适合你数据的 Record Reader。
"Record Writer" 应设置为以所需格式写出数据的 Record Writer。
应添加一个额外的属性。
该属性的名称应是对要保留数据的简短描述,例如 `relevant fields`。
其值是一条 SQL 语句,从名为 `FLOW_FILE` 的表中为相关行选择所需的列。
没有 WHERE 子句。
建议始终使用双引号引用列名,以避免与 SQL 关键字冲突。
例如,要仅保留 `name`、`age` 和 `address` 字段,添加一个名为 `relevant fields` 的属性, 其值为 `SELECT "name", "age", "address" FROM FLOWFILE`
现在添加此属性会给我们一个新的 Relationship,其名称与属性名相同。因此,`relevant fields` Relationship 应连接到我们流程中的下一个 Processor。
根据内容路由面向记录的数据进行处理
"Record Reader" 应设置为适合你数据的 Record Reader。
"Record Writer" 应设置为以所需格式写出数据的 Record Writer。
对于你想创建的每个路由,添加一个新属性。
该属性的名称应是对应为该路由选择的数据的简短描述。
其值是一条 SQL 语句,从名为 `FLOW_FILE` 的表中选择所有列。WHERE 子句选择应包含在该路由中的数据。
建议始终使用双引号引用列名,以避免与 SQL 关键字冲突。
为添加的每个属性创建一个新的出站关系。该关系的名称与属性名相同。
例如,要根据数据是否为大额交易来路由数据,我们将添加两个属性:
`small transaction` 的值类似于 `SELECT * FROM FLOWFILE WHERE transactionTotal < 100`
`large transaction` 的值为 `SELECT * FROM FLOWFILE WHERE transactionTotal >= 100`
属性
| 名称 | 默认值 | 可选值 | 描述 |
|---|---|---|---|
| Record Reader | | 控制器服务 API:RecordReaderFactorycn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8 | 指定用于解析传入数据并确定数据模式的 Controller Service |
| Record Writer | | 控制器服务 API:RecordSetWriterFactorycn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8 | 指定用于将结果写入 FlowFile 的Controller Service |
| Include Zero Record FlowFiles | true |
| 当对传入的 FlowFile 运行 SQL 语句时,如果结果没有数据,此属性指定是否将 FlowFile 发送到相应的关系。 |
| Default Decimal Precision | 10 | 当 DECIMAL/NUMBER 值作为 'decimal' Avro 逻辑类型写入时,需要特定的 'precision'(表示可用数字的位数)。通常,精度由列数据类型定义或数据库引擎默认值决定。但是,某些数据库引擎可能返回未定义的精度(0)。'默认小数精度' 用于写入这些未定义精度的数字。 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Default Decimal Scale | 0 | 当 DECIMAL/NUMBER 值作为 'decimal' Avro 逻辑类型写入时,需要特定的 'scale'(表示可用小数位数)。通常,小数位数由列数据类型定义或数据库引擎默认值决定。但是,当返回未定义的精度(0)时,某些数据库引擎的小数位数也可能不确定。'默认小数位数' 用于写入这些未定义的数字。如果值的小数位超过指定的小数位数,则值将被四舍五入,例如,小数位数为 0 时 1.53 变为 2,小数位数为 1 时 1.5 保持不变。 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) |
动态属性
| 名称 | 值 | 描述 |
|---|---|---|
| 要将数据路由到的关系名称 | 用于确定应将哪些数据路由到此关系的 SQL SELECT 语句。 | 每个用户定义的属性指定一个要在数据上运行的 SQL SELECT 语句,选择的数据将被路由到名称为属性名的关系 |
关系 (Relationships)
| 名称 | 描述 | 自动终止 |
|---|---|---|
failure | 如果 FlowFile 因任何原因处理失败(例如,SQL 语句包含输入数据中不存在的列),原始 FlowFile 将被路由到此关系。 | 否 |
original | 原始FlowFile被路由到这个关系 | 否 |
写入的 FlowFile 属性
| 名称 | 描述 |
|---|---|
mime.type | 将 mime.type 属性设置为 Record Writer 指定的 MIME 类型 |
record.count | 查询选择的记录数 |
QueryRecord.Route | FlowFile 被路由到的关系 |
状态管理
该组件不存储状态。
受限 (Restricted)
该组件不受限。
输入要求
该组件要求有传入连接。
详细说明
QueryRecord
对流数据执行 SQL
QueryRecord 借助一种广为人知的语法(SQL),在数据流经系统时对其进行路由、过滤、转换和查询, 为用户提供了强大的能力。为了最大程度地保持灵活性,该 Processor 配置了一个负责读取和解析 传入 FlowFile 的 Controller Service,以及一个负责写出结果的 Controller Service。 采用这种范式,用户不必为了查询而把数据从一种格式转换成另一种格式、查询完再转回想要的格式; 只需为相应的数据格式配置合适的 Controller Service 即可使用。
该 Processor 不提供单一的 "SQL SELECT Statement" 式属性,而是使用用户自定义属性。 添加到 Processor 的每个用户自定义属性,其名称会成为 Processor 的一个新 Relationship, 并对应一条将对每个 FlowFile 求值的 SQL 查询。这样,每个 FlowFile 上可以运行多条 SQL 查询。
该 Processor 支持的 SQL 语法是 ANSI SQL,由 Apache Calcite 驱动。请注意,标识符使用 双引号包裹,列名/别名不区分大小写。
举例来说,假设我们有一个包含以下 CSV 数据的 FlowFile:
name, age, title
John Doe, 34, Software Engineer
Jane Doe, 30, Program Manager
Jacob Doe, 45, Vice President
Janice Doe, 46, Vice President
再假设我们给 Processor 添加了以下属性:
| 属性名 | 属性值 |
|---|---|
| Engineers | SELECT * FROM FLOWFILE WHERE title LIKE '%Engineer%' |
| VP | SELECT name FROM FLOWFILE WHERE title = 'Vice President' |
| Younger Than Average | SELECT * FROM FLOWFILE WHERE age < (SELECT AVG(age) FROM FLOWFILE) |
此时该 Processor 会有五个 Relationship:original、failure、Engineers、VP 和
Younger Than Average。如果处理 FlowFile 时失败,原始 FlowFile 会被路由到 failure。
否则,原始 FlowFile 会被路由到 original,并且每个其他 Relationship 上各路由一个
FlowFile,取值如下:
| Relationship 名 | FlowFile 取值 |
|---|---|
| Engineers | name, age, titleJohn Doe, 34, Software Engineer |
| VP | nameJacob DoeJanice Doe |
| Younger Than Average | name, age, titleJohn Doe, 34, Software EngineerJane Doe, 30, Program Manager |
注意,此示例旨在说明 Processor 输入和输出的数据。数据的实际格式可能因所使用的 Record Reader 和 Record Writer 的配置而异。例如,这里假设使用带表头行的 CSV Reader 和 CSV Writer。如果改用 JSON Writer,输出会包含相同的信息,但以 JSON 形式呈现。 用户可以自行选择最适合其用例的输入和输出格式,输入和输出格式不必相同。
还值得注意的是,输出 FlowFile 有两种不同的 schema。Engineers 和 Younger Than Average
的 FlowFile 包含 3 个字段:name、age 和 title,而 VP 的 FlowFile 只包含
name 字段。在大多数情况下,Record Writer 被配置为使用 Record 所提供的
Schema(通常意味着其 Schema Access Strategy 配置为 Inherit Record Schema),
这样运行良好。但如果显式地为 Record Writer 提供了 Schema,就必须确保该 Schema
覆盖了所有字段;否则,Record Writer 的 schema 中缺少的字段将不会出现在输出中。
对层次化数据执行 SQL
在对任意数据流执行 SQL 时,必须考虑的一个重要细节是:如何处理层次化数据,例如 JSON、XML 和 Avro。由于 SQL 最初是为关系型数据库开发的,而关系型数据库表示的是 "扁平"数据,因此很容易理解它如何映射到 CSV 文件等其他"扁平"数据,甚至所有字段都是 原始类型的"扁平" JSON。但在很多情况下,用户希望对由许多嵌套值组成的 JSON 或 Avro 数据执行 SQL。例如,考虑以下 JSON 作为输入:
{
"name": "John Doe",
"title": "Software Engineer",
"age": 40,
"addresses": [
{
"streetNumber": 4820,
"street": "My Street",
"apartment": null,
"city": "New York",
"state": "NY",
"country": "USA",
"label": "work"
},
{
"streetNumber": 327,
"street": "Small Street",
"apartment": 309,
"city": "Los Angeles",
"state": "CA",
"country": "USA",
"label": "home"
}
],
"project": {
"name": "ACAL Lynxus",
"maintainer": {
"id": 28302873,
"name": "Apache Software Foundation"
},
"debutYear": 2014
}
}
考虑一个查询:选出家庭住址所在州与工作地点所在州不同的人的 title 和 name。这里我们
只能选择 name、title、age 和 addresses 字段。在此场景中,addresses
表示一个由复杂对象(即记录)组成的数组。为了适应这种情况,QueryRecord 提供了
用户自定义函数以启用 Record Path。Record Path 是一种简单的 Lynxus 领域专用
语言(DSL),允许用户引用嵌套结构。
主要使用的用户自定义函数名为 RPATH(Record Path 的缩写)。该函数恰好接受两个参数:
要对其求值 RecordPath 的 Record,以及要执行的 RecordPath(按此顺序)。因此,
要选出家庭住址所在州与工作地点所在州不同的人的 title 和 name,可以使用以下
SQL 语句:
SELECT title, name
FROM FLOWFILE
WHERE RPATH(addresses, '/state[/label = ''home'']') <> RPATH(addresses, '/state[/label = ''work'']')
用自然语言解释这条查询:它从 FlowFile 中选出满足以下条件的记录的 "title" 和 "name" 字段:存在一个 "label" 为 "home" 的地址和另一个 "label" 为 "work" 的地址, 且这两个地址的州不同。
类似地,我们可以用以下查询选出 "project" 的维护者是 Apache Software Foundation 的 人的完整记录(所有字段):
SELECT *
FROM FLOWFILE
WHERE RPATH(project, '/maintainer/name') = 'Apache Software Foundation'
不过,使用 RecordPath 有一个注意事项:RPATH 函数返回的是一个 Object,在 JDBC
中表示为 OTHER 类型。像上面那样使用时没有问题,不影响任何行为。但如果我们想对
结果再使用另一个 SQL 函数呢?例如,想使用
SELECT * FROM FLOWFILE WHERE RPATH(project, '/maintainer/name') LIKE 'Apache%'
这样的查询?这将会失败,并产生很长的错误,例如:
3860 [pool-2-thread-1] ERROR cn.org.acal.lynxus.processors.standard.QueryRecord - QueryRecord[id=135e9bc8-0372-4c1e-9c82-9d9a5bfe1261] Unable to query FlowFile[0,174730597574853.mockFlowFile,0B] due to java.lang.RuntimeException: Error while compiling generated Java code: ... Caused by: org.codehaus.commons.compiler.CompileException: Line 21, Column 180: No applicable constructor/method found for actual parameters "java.lang.Object, java.lang.String"; candidates are: "public static boolean org.apache.calcite.runtime.SqlFunctions.like(java.lang.String, java.lang.String)", "public static boolean org.apache.calcite.runtime.SqlFunctions.like(java.lang.String, java.lang.String, java.lang.String)"
这是因为 LIKE 函数期望用它来比较 String 对象,即期望的格式是 String LIKE String,
而我们传入的是 Other LIKE String。为此,还提供了几个其他 RecordPath 函数:
RPATH_STRING、RPATH_INT、RPATH_LONG、RPATH_FLOAT 和 RPATH_DOUBLE,
分别用于让返回类型成为 String、Integer、Long(64 位整数)、Float 或
Double。因此,上面的查询需要改写为
SELECT * FROM FLOWFILE WHERE RPATH_STRING(project, '/maintainer/name') LIKE 'Apache%',
这样就能产生期望的输出。
聚合函数
为了对数据流执行 SQL,该 Processor 把每个单独的 FlowFile 视为一张独立的表。 因此,SUM 和 AVG 等聚合函数会对每个 FlowFile 内的所有记录求值,但不会跨越 FlowFile 边界。举例来说,考虑一个包含以下数据的 CSV 格式输入 FlowFile:
name, age, gender
John Doe, 40, Male
Jane Doe, 39, Female
Jimmy Doe, 4, Male
June Doe, 1, Female
对于这些数据,我们可能想执行带聚合函数(例如 MAX)的查询:
SELECT name
FROM FLOWFILE
WHERE age = (SELECT MAX(age))
上面的查询会选出年龄最大的人的名字,即 John Doe。如果随后到达第二个 FlowFile,其内容会作为一张全新的表来求值。