博客
2020/08/27
使用 Beam SQL 进行模式匹配
简介
SQL 在数据分析领域变得越来越强大和有用。MATCH_RECOGNIZE 是 2016 年引入的一种新的 SQL 组件,它带来了额外的分析功能。这个项目是 Google Summer of Code 的一部分,旨在支持基本的 MATCH_RECOGNIZE 功能。一个基本的 MATCH_RECOGNIZE 查询将类似于以下内容
上面的查询找出具有名称“a”,“b”和“c”的事件的有序集。除了 MATCH_RECOGNIZE 的基本用法之外,我还支持一些其他关键功能,例如量词和行模式导航。我将在后面的部分详细说明。
方法与讨论
该实现强烈依赖于 BEAM 核心转换。具体来说,一个 MATCH_RECOGNIZE 执行将组成以下一系列转换
- 一个
ParDo
转换,然后是一个GroupByKey
转换,用于构建分区 (PARTITION BY)。 - 一个
ParDo
转换,用于对每个分区进行排序 (ORDER BY)。 - 一个
ParDo
转换,用于在每个排序的分区中应用模式匹配。
模式匹配操作最初使用 java 正则表达式库完成。也就是说,我首先将分区中的行转换为字符串,然后应用正则表达式模式匹配例程。如果一行满足某个条件,那么我会输出相应的模式变量。这在模式定义互斥的假设下是可以的。也就是说,像 A AS A.price > 0, B AS b.price < 0
这样的模式定义是允许的,而像 A AS A.price > 0, B AS B.proctime > 0
这样的模式定义可能会导致匹配不完整。对于后一种情况,事件可以同时满足条件 A 和 B。互斥条件使模式匹配确定性:每个事件最多只能属于一个模式类。
如 SQL 2016 文档中所述,MATCH_RECOGNIZE 定义了一组比正则表达式更丰富的表达式。具体来说,它引入了行模式导航操作,例如 PREV
和 NEXT
。这也许是 MATCH_RECOGNIZE 最吸引人的功能之一。正则表达式库将不再满足需要,因为模式定义可能是反向引用 (PREV
) 或正向引用 (NEXT
)。因此,对于第二版实现,我们选择使用 NFA 正则表达式引擎。NFA 在非确定性方面带来了更大的灵活性(有关更详细的讨论,请参阅 SQL 2016 第 5 部分第 6 章)。我提出的 NFA 基于马萨诸塞大学的一篇论文。
这是一个正在进行的项目。许多组件仍未得到支持。我将在未来的工作部分列出一些未实现的工作。
用法
目前,我支持的组件是
- PARTITION BY
- ORDER BY
- MEASURES
- LAST
- FIRST
- ONE ROW PER MATCH/ALL ROWS PER MATCH
- DEFINE
- 条件的左侧
- LAST
- 条件的右侧
- PREV
- 条件的左侧
- 量词
- 克莱尼加
模式定义评估是硬编码的。更准确地说,它期望传入行的列引用位于比较运算符的左侧。此外,PREV 函数只能出现在比较运算符的右侧。
使用这些有限的工具,我们已经可以编写一些稍微复杂的查询。假设我们有以下表格
transTime | price |
---|---|
1 | 3 |
2 | 2 |
3 | 1 |
4 | 5 |
5 | 6 |
此表反映了产品相对于交易时间的价格变化。我们可以编写以下查询
这将找到局部最小价格及其后的价格。对于示例数据集,前 3 行将映射到 A,其余行将映射到 B。因此,我们将得到 (1, 5) 作为结果。
非常重要:对于我的 NFA 实现,它稍微违反了 SQL 标准中的规则。由于缓冲的 NFA 仅在事件与某个模式类匹配时才将事件存储到缓冲区中,因此如果前面的行被丢弃,将无法获取前面的事件。因此,如果使用 PREV,第一行将始终匹配(与标准不同)。
进度
- PR
- 提交
- 分区:commit 064ada7
- 排序:commit 9cd1a82
- 正则表达式模式匹配:commit 8d6ffcc
- 支持量词:commit f529b87
- 度量:commit 8793574
- 添加 NFA 实现:commit fc731f2
- 实现函数 PREV 和 LAST:commit 35323da
未来的工作
- 支持 FINAL/RUNNING 关键字。
- 支持更多量词。
- 对 NFA 添加优化。
- 实现 MATCH_RECOGNIZE 的一种更好的方法可能是拥有 BEAM 核心中的复杂事件处理库(而不是使用 BEAM 转换)。