一种受SQL启发、专为事件溯源设计的查询语言(2025年)

Hacker News Top 工具

摘要

EventQL是一种受SQL启发、专为事件溯源设计的查询语言,它提供对事件属性、主题层级结构的一流支持,并具备类似SQL的表达能力,以便高效查询事件流。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/05/16 00:35

# EventQL: 一种专为事件溯源设计的类SQL查询语言 来源:https://yoeight.github.io/blog/2025/12/21/EventQL_A_SQL_Inspired_Query_Language_Designed_For_Event_Sourcing.html ## 诺曼底的代码 我的个人博客 --- 项目由YoEight(https://github.com/YoEight)维护 托管于GitHub Pages — 主题来自mattgraham(https://twitter.com/mattgraham) 事件溯源已逐渐成为一种流行的架构模式,但高效查询事件流仍然是个挑战。尽管事件是只追加且不可变的,但要在成千上万的事件中定位特定事件或分析模式,仍需精心设计查询。这正是EventQL的优势所在。 ## 事件查询的核心难题 在处理事件溯源系统时,你所面对的数据结构与传统数据库截然不同: - 事件拥有丰富的元数据:类型、主体、时间戳、数据负载 - 事件通过主体层次结构组织(例如,`/books/42`、`/users/123/orders/456`) - 你需要对事件流进行过滤、聚合和转换 - 性能在很大程度上依赖于合理利用索引 传统的NoSQL查询接口往往力不从心,因为它们并非针对这类事件特有特性而设计。 ## EventQL 登场 EventQL 是一种查询语言,最初由 The Native Web 为其 EventSourcingDB(https://www.thenativeweb.io/products/eventsourcingdb)设计。它之所以特别,是因为在保留 SQL 熟悉的表达力的同时,完美捕捉了事件查询的精髓。 下面是一个简单示例: `` FROM e IN events WHERE e.type == "io.eventsourcingdb.library.book-acquired" AND e.data.price > 20 PROJECT INTO { id: e.id, title: e.data.title, price: e.data.price } `` 如果你写过 SQL,会觉得这个语法非常亲切。但请注意,它是为事件量身定制的:我们按事件类型过滤,访问嵌套的数据负载,并重塑输出结构。 ## 为何 EventQL 的设计至关重要 ### 1. **事件属性作为一等公民** EventQL 将事件元数据视为查询语言中的一等公民: - `e.type` — 按事件类型过滤 - `e.subject` — 按主体层次结构查询 - `e.id` — 引用特定事件 - `e.time` — 基于时间的排序和过滤 - `e.data.*` — 深层访问事件负载 这些属性中的每一个都代表着一个**索引优化机会**。一个设计良好的事件存储可以为类型、主体和时间戳创建索引,使这些查询速度飞快。 ### 2. **主体层次结构实现智能范围限定** EventQL 最强大的特性之一是主体模式匹配: `` FROM e IN events WHERE e.subject == "/books/42" ORDER BY e.time DESC TOP 100 PROJECT INTO e `` 像 `/books/42` 或 `/users/123/orders/456` 这样的主体层次结构在事件溯源中很自然。它们代表了聚合边界,允许你: - 将查询范围限定到特定聚合 - 创建基于主体的索引以实现快速查找 - 构建感知层次结构的查询(虽然在基本示例中未展示) 这使得浏览事件数据变得直观:“显示这本书的所有事件”或“这个用户的订单发生了什么?” ### 3. **类SQL的表达力** EventQL 借鉴了 SQL 成熟的模式: - **WHERE 子句**,支持完整的布尔逻辑(AND、OR、NOT) - **ORDER BY**,支持升序/降序排序 - **GROUP BY**,用于聚合 - **TOP/SKIP**,用于分页 - **嵌套子查询**,用于复杂转换 这意味着你可以精确地表达复杂的查询: `` FROM e IN ( FROM e IN events WHERE e.type == "order-placed" PROJECT INTO { orderId: e.id, total: e.data.total } ) WHERE e.total > 100 ORDER BY e.total DESC PROJECT INTO e `` ### 4. **投影作为一等概念** 与 SQL 中可选的 SELECT 不同,EventQL 要求使用 `PROJECT INTO` 进行显式投影。这一设计选择对于事件查询来说很有道理,因为通常你需要: - 重塑嵌套的事件数据 - 从负载中提取特定字段 - 构建聚合或计算值 `` FROM e IN events WHERE e.type == "book-acquired" PROJECT INTO { year: YEAR(e.time), revenue: SUM(e.data.price) } `` 投影语法支持任意对象构造,使得构建你所需的输出形状变得轻而易举。 ## 天生对索引友好 我特别欣赏 EventQL 的一点是,它自然地引导你编写可索引的查询。考虑 WHERE 子句中常用的属性: - **事件类型** — 几乎总是被索引 - **主体** — 自然的分区键 - **时间戳** — 时间范围查询必不可少 - **数据字段** — 可以有选择地索引 像这样的查询: `` FROM e IN events WHERE e.type == "user-registered" AND e.time > "2025-01-01" AND e.subject == "/users" ORDER BY e.time DESC TOP 1000 PROJECT INTO e `` 完美地映射到 (type, time, subject) 的复合索引上。语言的结构清晰地指明了索引将如何发挥作用。 ## 让解析器达到生产就绪 我最初在 GethDB 数据库项目中编写了一个 EventQL 解析器。它能工作,但与那个具体用例紧密耦合。最近,我决定将其打造成一个独立的库,达到生产就绪。 最终成果是一个健壮的 Rust 解析器,它: - 提供详细的错误信息,包括行号和列号 - 构建适合查询优化的强类型抽象语法树(AST) - 支持完整的 EventQL 语法 - 包含全面的测试覆盖 - 可嵌入任何基于 Rust 的事件存储中 ### 类型推断:即将推出 GethDB 版本包含一个类型推断系统,我计划也将它移植到这个库中。类型推断器会尽可能多地从查询中收集类型信息,并尽早捕获不一致之处。 例如,它会拒绝像这样的查询: `` FROM e IN events WHERE e.data.price == "expensive" -- price 被当作字符串 AND e.data.price > 100 -- price 被当作数字 PROJECT INTO e `` 通过追踪字段在查询中的使用方式,类型推断器可以在查询到达数据库之前就排除无意义的查询。这能提供更好的错误消息,并防止因类型不匹配导致的运行时故障。 你可以在 GitHub 上找到这个解析器:eventql-parser(https://github.com/YoEight/eventql-parser) ## 这为什么重要 事件溯源功能强大,但需要好的工具。一个设计良好的查询语言决定了事件存储是令人痛苦还是愉悦易用。 EventQL 把握住了基本原则: - **熟悉**(类 SQL 语法) - **表达力强**(可以编写复杂查询) - **事件感知**(围绕事件属性设计) - **索引友好**(自然的优化机会) 如果你正在构建一个事件溯源系统,你需要一种有效查询事件的方法。EventQL 展示了如何做得正确。 ## 亲自尝试 该解析器以 Rust crate 的形式提供。下面是入门示例: `` use event_query_lang::parse_query; let query = r#" FROM e IN events WHERE e.type == "order-placed" ORDER BY e.time DESC TOP 100 PROJECT INTO e "#; match parse_query(query) { Ok(ast) => { // 使用 AST 执行查询 println!("解析成功!"); } Err(e) => { eprintln!("解析错误:{}", e); } } `` ## 结论 好的语言设计在于深入理解领域,并创建自然的抽象。EventQL 在事件溯源方面做到了这一点。 它证明了你不需要重新发明轮子——SQL 已有的模式,在经过对事件流的深思熟虑的改造后,效果惊人。最终形成的是一种既强大又好用的查询语言。 如果你正在使用事件溯源,不妨看看 EventQL。如果你需要一个生产就绪的解析器,Rust 实现已经可以用了。 --- *这个解析器是为了支持我的 GethDB 项目和其他事件溯源系统而构建的。如果你有反馈或想贡献代码,欢迎在 GitHub 上提交 issue 或 PR。*

相似文章

QO-Bench:诊断类型化事件元组上的查询算子保留检索

arXiv cs.CL

QO-Bench 是一个针对类型化事件元组上查询算子问答的诊断性基准测试,涵盖 22,984 篇新闻文章和 614 个企业事件,涉及 18 种查询模板。该基准对 RAG、ReAct RAG、GraphRAG 以及抽取转 SQL 系统进行评估,发现算子执行——而非仅仅是检索——才是核心瓶颈,单纯使用更强的模型并不能解决这一问题。

CQL:范畴数据库

Hacker News Top

CQL是一个开源工具,利用范畴论执行数据库操作,如查询、迁移和集成数据,并通过定理证明提供内置的正确性保证。

ggsql:面向 SQL 的图形语法

Lobsters Hottest

ggsql 是一款 Alpha 版本工具,它将图形语法的可视化能力引入 SQL,允许用户在 Quarto、Jupyter、Positron 和 VS Code 中利用 SQL 语法构建结构化、模块化的可视化图表。