尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Metabase MBQL Library API 变更指南:从 0.50.0 起的版本化查询操作接口(as-returned 与列提取)

Metabase MBQL Library API 变更指南:从 0.50.0 起的版本化查询操作接口(as-returned 与列提取) Metabase MBQL Library API 变更指南从 0.50.0 起的版本化查询操作接口as-returned 与列提取【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabaseMBQLMetabase Business Query LanguageLibrary 是 Metabase 前端用于程序化构建、读取与修改 MBQL 查询的统一 API 层。本文以仓库中的 MBQL Library 变更日志 为主线完整解析 Metabase 0.50.0 引入的as-returned、column-extractions、extract、extraction-expression四个新函数并结合 ClojureScript 导出层、提取实现、drill-thru 交互链路 与前后端测试讲清这些 API 的调用方式、底层逻辑与适用边界。读完本文你将能理解如何在带聚合的查询中安全地追加过滤与自定义表达式以及如何利用列提取从日期、URL、邮箱列中一键派生出星期、月份、域名、主机名等新列。一、MBQL Library 是什么一份被正式版本化的前端查询 APIMBQL Library 是操作 MBQL 查询的库级 API核心实现在metabase.lib.jsCLJS 侧为 src/metabase/lib/js.cljs通过^:export暴露给前端 TypeScript 层使用。它的特点在于主要使用者是 Metabase 自身的前端——查询构建器、汇总编辑、钻取菜单drill-thru等 UI 都通过它来读写查询尽管是自用代码它仍被当作正式的 API 表面来对待按版本演进、集中记录在变更日志中TypeScript 侧有对应的薄封装位于 frontend/src/metabase-lib/query/query.ts、frontend/src/metabase-lib/query/extractions.ts 等文件最终都调用cljs/metabase.lib.js中的同名导出。当前仓库中的最新 API 文档以该变更日志文件docs/developers-guide/mbql-library-changelog.md为权威记录从Metabase 0.50.0开始对这套 API 进行版本化。二、版本化约定变更日志的定位与阅读方式变更日志明确了两条约定本文件记录的是metabase.lib.js中操纵 MBQL 查询的库 API的变更而不是产品功能列表每个 Metabase 版本从 0.50.0 起下新增或修改的函数都会被记录下来新增函数通常附带动机说明与行为描述帮助下游调用方包括未来版本的 Metabase 前端自身以及任何基于该库的集成方判断升级影响。这种库 API 版本化 集中变更日志的做法意味着你在升级 Metabase 版本时可以像对待一个独立 npm 包一样先读这份变更日志确认 API 行为是否发生变化。三、Metabase 0.50.0 新增 API 一览0.50.0 是开始版本化的首个版本新增了以下四个函数函数签名JS 侧作用as-returned(query, stageNumber, cardId)返回{query, stageIndex}必要时把操作目标阶段后移/追加用于基于聚合结果继续加工的场景column-extractions(query, column)根据列类型返回一组候选提取extraction如从日期提取星期、从 URL 提取域名extract(query, stageNumber, extraction)把某个提取应用到查询等价于新增一个自定义表达式列extraction-expression(query, stageNumber, extraction)返回提取对应的表达式MBQL clause供继续编辑或复用于其他位置下面分别深入这四个函数的实现与用法。四、as-returned解决带聚合的查询该如何继续加工这一棘手问题4.1 问题背景聚合前的过滤 vs 聚合后的过滤假设一个查询的最后阶段带有聚合aggregations。此时如果直接向该阶段添加过滤器或自定义表达式该操作会被应用到聚合之前对明细行生效。这在某些场景下确实是我们想要的但当你希望基于聚合结果和分组breakout结果再过滤或再加工时——例如只保留计数大于 100 的分组——原有的 API 就没有好的支持了。as-returned正是为此设计它检查查询与阶段必要时把操作目标切换到更靠后的阶段如果已经处在最后阶段而仍然需要后移则自动追加一个新的空阶段。4.2 源码实现三种分支逻辑在 src/metabase/lib/js.cljs 中as-returned的实现逻辑非常清晰(defn ^:export as-returned [a-query stage-number card-id] (let [{a-query :query, :keys [stage-number]} (lib.core/wrap-native-query-with-mbql a-query stage-number card-id)] (if (and (empty? (lib.core/aggregations a-query stage-number)) (empty? (lib.core/breakouts a-query stage-number))) ;; 情况一没有聚合也没有分组无需后移 #js {:query a-query :stageIndex stage-number} ;; 情况二存在聚合/分组若已有更靠后的阶段则复用 (if-let [next-stage (- (lib.util/canonical-stage-index a-query stage-number) (lib.util/next-stage-number a-query))] #js {:query a-query :stageIndex next-stage} ;; 情况三没有更靠后的阶段追加一个新阶段 #js {:query (lib.core/append-stage a-query) :stageIndex -1}))))三个分支对应三条规则目标阶段既无聚合也无分组原样返回{query, stageIndex}不需要任何改动目标阶段有聚合/分组且已存在更靠后的阶段直接复用该阶段返回其stageIndex目标阶段有聚合/分组且它已是最后阶段通过append-stage追加一个空阶段返回的stageIndex为-1MBQL 中-1表示最后一个阶段。注意一个细节该函数还会先经过wrap-native-query-with-mbql处理第三个参数card-id用于在必要情况下把 Native 查询包装为 MBQL 查询详见 src/metabase/lib/js.cljs因此该 API 也能处理基于某个 Card 继续查询的场景。4.3 TypeScript 侧的封装前端 TypeScript 层在 frontend/src/metabase-lib/query/query.ts 中封装了同名函数export function asReturned( query: Query, stageIndex: number, cardId: CardId | undefined, ): { query: Query; stageIndex: number } { return ML.as_returned(query, stageIndex, cardId); }调用方拿到返回的{query, stageIndex}后即可安全地在目标阶段上继续调用filter、expression等操作而无需自己判断是否需要追加阶段。4.4 测试佐证五种场景的行为矩阵test/metabase/lib/js_test.cljs 中的as-returned-test用一张完整用例矩阵验证了上述三条规则输入场景期望行为无聚合、无分组的简单查询阶段 0 或 -1查询与阶段索引均不变两阶段查询第二阶段无聚合目标为阶段 1/-1查询与阶段索引均不变两阶段查询目标为阶段 0第一阶段有聚合复用已存在的阶段 1单阶段聚合查询目标为最后阶段追加新阶段返回stageIndex -1仅含分组的查询同样遵循复用已有后置阶段 / 无则追加规则这套测试同时覆盖了仅分组仅聚合分组聚合三种构造证明as-returned的核心判断条件就是聚合与分组的有无二者任一存在都会触发阶段后移。五、列提取Column Extractions从现有列派生自定义表达式5.1 概念与动机列提取指根据给定列的类型推导出一组可能的自定义表达式。典型例子从日期/时间列提取小时、日、星期、月份、季度、年份从 URL 列提取域名、子域名、主机名、路径从邮箱列提取域名、主机名。column-extractions返回这组候选extract将其中某个候选真正应用到查询表现为新增一个表达式列extraction-expression则先给出表达式本身便于调用方在加入查询前做进一步编辑。5.2 提取的数据结构Schema每个 extraction 是一个包含四个字段的 map其结构由 src/metabase/lib/schema/extraction.cljc 约束[:map [:lib/type [: :metabase.lib.extraction/extraction]] [:tag [:enum :domain :subdomain :host :path :hour-of-day :day-of-month :day-of-week :month-of-year :quarter-of-year :year]] [:column ::lib.schema.metadata/column] [:display-name :string]]字段含义字段说明:lib/type固定为:metabase.lib.extraction/extraction用于类型判别:tag提取种类标识共 10 种时间/日期 6 种 URL/邮箱 4 种:column被提取的源列column metadata:display-name展示名例如 Month of year、Domain同时作为后续生成表达式列的名称来源TypeScript 侧同样维护了这组 tag 的类型联合见 frontend/src/metabase-lib/query/extractions.ts 中的ColumnExtractionTag。5.3 按列类型给出候选column-extractions的实现核心实现在 src/metabase/lib/extraction.cljc采用按列类型分发的cond(cond (lib.types.isa/temporal? column) (column-extract-temporal-units column) ;; URL/邮箱提取依赖带 lookahead/lookbehind 的正则 ;; 若目标数据库不支持 :regex/lookaheads-and-lookbehinds 特性则返回 nil。 (not (regex-with-lookaheads-and-lookbehinds-available? query)) nil (lib.types.isa/email? column) (email-extractions column) (lib.types.isa/URL? column) (url-extractions column))可以提炼出以下行为规则时间/日期列temporal?→ 产生时间单位或日期单位的提取对时间列type/Time只有:hour-of-day对日期列type/Date:day-of-month、:day-of-week、:month-of-year、:quarter-of-year、:year对日期时间列datetime上述 6 种全部给出见 src/metabase/lib/extraction.cljc 与 test/metabase/lib/extraction_test.cljc 的验证用例URL 列语义类型为:type/URL→:domain、:subdomain、:host、:path邮箱列语义类型为:type/Email→:domain、:host数据库能力约束URL 与邮箱提取的正则表达式实现依赖lookahead / lookbehind能力如果目标数据库不支持:regex/lookaheads-and-lookbehinds特性则column-extractions对这两类列返回nil没有任何候选而不是返回错误结果。这一特性门控逻辑在 test/metabase/lib/extraction_test.cljc 中有专门测试同一份 metadata 下把数据库的:features集合去掉:regex/lookaheads-and-lookbehinds后URL/邮箱列的提取候选即变为空。5.4 每种 tag 对应的表达式extraction-expressionextraction-expression把某个 extraction 翻译成 MBQL 表达式 clause实现在 src/metabase/lib/extraction.cljc本质是一张tag → 表达式函数的映射表tag生成的表达式:hour-of-day(get-hour column):day-of-month(get-day column):day-of-week(day-name (get-day-of-week column)):month-of-year(month-name (get-month column)):quarter-of-year(quarter-name (get-quarter column)):year(get-year column):domain(domain column):subdomain(subdomain column):host(host column):path(path column)这些底层表达式操作符get-hour、get-day、get-day-of-week、day-name、month-name、quarter-name、domain、subdomain、host、path等都注册在 src/metabase/lib/expression.cljc 中是 MBQL 表达式体系的标准函数。测试给出了非常具体的 MBQL 结构test/metabase/lib/extraction_test.cljc;; :month-of-year 的表达式 [:month-name {} [:get-month {} [:field {} (meta/id :orders :created-at)]]] ;; :day-of-week 的表达式 [:day-name {} [:get-day-of-week {} [:field {} (meta/id :orders :created-at)]]] ;; :quarter-of-year 的表达式 [:quarter-name {} [:get-quarter {} [:field {} (meta/id :orders :created-at)]]]也就是说提取本质上是对表达式构建函数的便捷封装extraction-expression生成一个可独立求值、可继续编辑的 MBQL 表达式测试中:day-of-week的[:day-name ... [:get-day-of-week ...]]嵌套结构清晰地展示了先取星期编号、再转星期名的两步表达式。5.5 应用到查询extract与命名去重extract把提取真正写入查询实现在 src/metabase/lib/extraction.cljc。其工作方式取extraction的:display-name作为新表达式的名称为避免与已有列重名会用unique-name-generator对名称做唯一化处理例如第二次提取同列时自动命名为 Day of month_2通过lib.expression/expression把(extraction-expression extraction)作为表达式、目标阶段为stage-number追加进查询。命名去重行为在 test/metabase/lib/extraction_test.cljc 的duplicate-names-test中验证对已含 Day of month 表达式的查询再次提取:day-of-month新表达式名称会自动变为 Day of month_2。extract目前在实现上是简单的用 tag 对应的表达式函数、以源列为唯一参数构造表达式见 src/metabase/lib/extraction.cljc 的注释说明没有更复杂的参数化逻辑——这一点对调用方来说意味着可预期的行为。5.6 JS/TS 侧的完整导出与封装三个函数在 src/metabase/lib/js.cljs 中以^:export导出column-extractions接收(query, column)返回 JS 数组可能为空extract接收(query, stageNumber, extraction)返回更新后的查询extraction-expression接收(query, stageNumber, extraction)返回不透明的表达式 clause——文档注释特别指出如果只是想加入查询直接用extract即可extraction-expression适用于需要进一步编辑表达式的场景例如在 UI 中打开表达式编辑器后再保存。TypeScript 侧在 frontend/src/metabase-lib/query/extractions.ts 中逐一封装并额外提供了extractionsForDrill(drill)从钻取对象中取出候选提取转发到column_extract_drill_extractionsfunctionsUsedByExtraction(query, stageIndex, extraction)递归遍历表达式树返回该提取用到的所有函数名见 frontend/src/metabase-lib/query/extractions.ts可用于判断目标数据库是否支持这些函数。六、前端交互链路column-extract 钻取与阶段追加在 Metabase UI 中列提取以**钻取drill-thru**形式暴露用户点击列头如日期列、URL 列、邮箱列弹出Extract day, month…、Extract domain, host…等菜单项。这条链路实现在 src/metabase/lib/drill_thru/column_extract.cljccolumn-extract-drill仅在目标列无选中值、且是MBQL 阶段时生成它内部先调用column-extractions获取候选再调用prepare-query-for-drill-addition准备一个可安全添加表达式的查询与阶段菜单文案按列类型区分时间列显示 Extract day, month…邮箱列显示 Extract domain, host…URL 列显示 Extract domain, subdomain…src/metabase/lib/drill_thru/column_extract.cljccolumn-extract-drill-extractions导出把钻取对象中的候选提取以 JS 数组形式返回给前端渲染菜单src/metabase/lib/js.cljs。这里与as-returned高度呼应的关键点是prepare-query-for-drill-additionsrc/metabase/lib/drill_thru/column_filter.cljc的追加阶段规则目标列来自聚合:lib/source为:source/aggregations→ 必须切换到更靠后的阶段目标列是分组列且要添加自定义表达式adding :expression→ 也必须切换到更靠后的阶段若已有更靠后的阶段则复用否则append-stage并返回stageIndex -1。可以看到这条规则与as-returned的有聚合/分组就后移、无后置阶段就追加原则是一致的对聚合或分组结果再做过滤/表达式加工必须发生在这些聚合计算之后的阶段。二者构成UI 钻取与纯 API两套入口共享同一套阶段追加语义。七、版本升级与自定义集成注意事项结合以上源码与测试使用这套 0.50.0 API 时应注意明确聚合前 vs 聚合后向含聚合的最后阶段添加 filter/expression 默认作用于聚合前需要基于聚合/分组结果加工时用as-returned或复用prepare-query-for-drill-addition的语义先定位/创建后置阶段。as-returned返回-1表示追加的新阶段stageIndex为-1时意味着查询末尾新增了一个空阶段调用方需按最后一个阶段来理解。URL/邮箱提取不是所有数据库都可用column-extractions会检查:regex/lookaheads-and-lookbehinds数据库特性集成时应对返回值为nil/空数组的情况做好降级处理。提取结果适合先预览再落库extraction-expression与extract分离的设计支持在表达式编辑器中对提取结果二次修改后再提交前者取表达式、后者直接应用。名称自动去重重复对同一列做相同提取时新列名会自动追加序号无需调用方自行处理命名冲突。八、深入阅读指引若要在当前仓库中继续深挖推荐按以下顺序阅读变更日志原文docs/developers-guide/mbql-library-changelog.mdJS 导出层as-returned与提取三函数的^:export定义src/metabase/lib/js.cljs列提取核心实现column-extractions/extraction-expression/extract见 src/metabase/lib/extraction.cljc提取的数据结构约束src/metabase/lib/schema/extraction.cljc钻取链路与阶段追加规则src/metabase/lib/drill_thru/column_extract.cljc、src/metabase/lib/drill_thru/column_filter.cljc前端 TypeScript 封装frontend/src/metabase-lib/query/query.ts、frontend/src/metabase-lib/query/extractions.ts测试验证test/metabase/lib/extraction_test.cljc、test/metabase/lib/js_test.cljs这套 API 的演进体现了 Metabase 对查询构建能力的抽象方式把在哪个阶段操作基于什么列能做什么提取这类易错判断收敛到库内部让前端各入口查询编辑器、钻取菜单、表达式编辑器共享一致且经过测试验证的语义。【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表