Lightdash MCP Filter Expressions 全指南:为 run_metric_query 与 search_field_values 编写过滤器表达式
2026/9/18 5:20:24 网站建设 项目流程

Lightdash MCP Filter Expressions 全指南:为 run_metric_query 与 search_field_values 编写过滤器表达式

【免费下载链接】lightdashAgentic BI. Analytics at the speed of code ⚡️项目地址: https://gitcode.com/GitHub_Trending/li/lightdash

导读

Lightdash 的 AI Agent 系统在通过 MCP(Model Context Protocol)调用run_metric_querysearch_field_values等工具时,过滤器不再使用结构化的 JSON 对象,而是采用一种紧凑的**过滤器表达式(Filter Expression)**字符串语法。本文以仓库内置技能文档 SKILL.md 为骨架,深入讲解该语法的放置规则、四类字段操作符、字面量引号规则、硬性限制,并结合packages/commonpackages/backend中的解析器、Zod schema 与测试源码,说明这一套机制在底层是如何被生成、解析和校验的。读完本文,你将能准确为 AI 工具编写可被 Lightdash 后端正确解析的过滤器表达式,并理解其与结构化过滤器对象的边界。


一、这个 Skill 是什么:生成与加载机制

filter-expressions是 Lightdash 内置技能(Built-in Skill)之一,其前端定义位于 filterExpressionSkill.ts,而实际下发到 MCP 的 Markdown 文件是 builtInSkills/filter-expressions/SKILL.md。文件头部的注释明确说明它由命令生成:

pnpm -F backend generate:filter-expression-skill

即该 Markdown 并非手写维护的静态文案,而是由源码模板(filterExpressionSkill.ts拼接MCP_FILTER_EXPRESSION_GUIDANCE_SECTION)程序化生成,保证文档与实现永不脱节。

Skill 的 frontmatter 声明了它的元信息:

--- name: filter-expressions description: Author filter expressions for run_metric_query and search_field_values, including conditional custom metric filters. availability: [mcp] ---

其中availability: [mcp]表示这是一个MCP 专用技能。在 builtInSkills.test.ts 中可以看到两条针对性断言:filter-expressionstable-calculations这两个 skill永远不会被暴露给 AI Agent 运行时,并且 Agent 直接读取也会被拦截(getAiAgentSkill('filter-expressions')返回undefined)。这意味着这套过滤器表达式语法只服务 MCP 场景下的工具调用。

从加载机制看,builtInSkills.ts 中BuiltInSkills类会扫描builtInSkills目录下的每个子目录,读取其中的SKILL.md,解析 frontmatter(matter),并计算sha256内容摘要;每个技能通过skill://lightdash/<name>/SKILL.md这样的命名空间 URI 以 MCP Resource 形式暴露。测试(builtInSkills.test.ts)验证了通过原生资源(getMcpResourceBody(uri))与回退工具(readSkillTool)拿到的内容逐行一致,且skill://index.json中注册了该技能名。

适用边界:本技能只适用于使用表达式语法的工具(如run_metric_querysearch_field_values)。对于 schema 中使用结构化过滤器对象(structured filter objects)的工具,本文语法不适用。写作过滤器前务必先查看每个工具的输入 schema——技能本身不能启用本不可用的工具或过滤模式。


二、核心概念:三个过滤器类别与放置规则

run_metric_query而言,过滤器的入口是queryConfig.filters。其 Zod schema 定义于 expressionSchemas.ts:

export const filterExpressionsSchema = z .object({ dimensions: filterExpressionInputSchema .nullable() .describe('Flat filter expression for dimension fields.'), metrics: filterExpressionInputSchema .nullable() .describe('Flat filter expression for metric fields.'), tableCalculations: filterExpressionInputSchema .nullable() .describe('Flat filter expression for table calculations.'), }) .strict()

由此可以提炼出四条铁律:

  1. 无需过滤时:将queryConfig.filters整体设为null;否则dimensionsmetricstableCalculations三个类别各自独立,可以是字符串表达式,也可以是null
  2. 非 null 的类别只包含一条扁平字符串表达式。例如dimensions只能填一个字符串,不能填数组。
  3. 类别由字段元数据决定:一个字段放进dimensions还是metrics,取决于它在探查(field discovery)元数据中的 kind(dimension/metric),绝不能凭字段名或"数字看起来像指标"来臆断。裸的数字维度(raw numeric dimension)应放在dimensions;只有指标和自定义指标(custom metrics)才放在metrics
  4. 每个类别内部是扁平的,只用 AND 或只用 OR,二者不可混用;而三个类别之间隐式地用 AND 组合。例如 schema 描述中给出的语义:dimensionsD1 AND D2metricsM1 OR M2时,整体等价于(D1 AND D2) AND (M1 OR M2)

放置示例

filterGuidance.ts(filterGuidance.ts)中的placementExamplesFILTER_EXPRESSION_PLACEMENT_EXPRESSIONS提供了被直接注入 SKILL.md 的原始示例:

dimensions: orders_status equals=completed,shipped AND orders_order_date inThePast=2{unit:weeks,completed:true} dimensions (alternatives): orders_promo_code startsWith=VIP OR orders_promo_code endsWith=25 metrics: orders_total_order_amount greaterThan=100 tableCalculations: rank lessThanOrEqual=10

这些示例还揭示了几条重要细节:

  • dimensions内部用 AND 连接多个规则(状态等于 completed/shipped并且订单日期在过去两周内);
  • 备选条件用 OR(促销码以 VIP 开头以 25 结尾);
  • 表计算(table calculations)只能出现在tableCalculations类别,即使它的底层字段类型是数字维度,也不能塞进dimensionsmetrics
  • 一个仅用于限制行数、用户并没有要求分组或展示的字段,应当只出现在过滤器表达式里,不要额外加进queryConfig.dimensions——多选的 dimension 会改变聚合粒度和表计算粒度(grain),导致结果错位。

聚合自定义指标的过滤器

对自定义指标(custom metrics)中的聚合类指标(aggregation custom metric),其条件过滤(conditional filter)是扁平的 AND 表达式。这在 expressionSchemas.ts 中有明确实现:

export const aggregationCustomMetricExpressionSchema = aggregationCustomMetricSchema.extend({ filters: filterExpressionInputSchema .nullable() .describe( 'Optional flat AND expression for conditional metric filters.', ), });

filters是一个可空的扁平 AND 表达式字符串,用于给自定义指标附加行级条件。

search_field_values 的过滤器

search_field_values工具:无范围搜索时应省略filters字段;当filters存在时,它是一段扁平且仅含维度(dimension-only)的 AND 表达式,用于收窄候选值的搜索范围。相关指导文本定义在 filterGuidance.ts 的EXPRESSION_SEARCH_FIELD_VALUES_FILTER_GUIDANCE常量中。


三、操作符语法全表:string / number / date / boolean

每个类别中的一条"规则(rule)"遵循统一语法:<field> <operator form>,其中 field 是字段 ID,operator form 由操作符及其参数构成。操作符按字段类型分四组定义,其权威来源是 operators.ts 中的filterExpressionOperatorDefinitions数组,以及 expressionSchemas.ts 中按argumentCountByFilterType生成的语法描述。下表完整覆盖 SKILL.md 中给出的全部操作符:

string(字符串维度)

语法:<field> <operator form>

操作符参数形式值数量
isNull<field> isNull0 个值
notNull<field> notNull0 个值
equals<field> equals=<value>[,<value>...]1+ 个值
notEquals<field> notEquals=<value>[,<value>...]1+ 个值
startsWith<field> startsWith=<value>[,<value>...]1+ 个值
endsWith<field> endsWith=<value>[,<value>...]1+ 个值
include<field> include=<value>[,<value>...]1+ 个值
doesNotInclude<field> doesNotInclude=<value>[,<value>...]1+ 个值

number(数字维度/指标)

语法:<field> <operator form>

操作符参数形式值数量
isNull/notNull同 string0 个值
equals/notEquals<field> equals=<value>[,<value>...]1+ 个值
lessThan<field> lessThan=<value>1 个值
lessThanOrEqual<field> lessThanOrEqual=<value>1 个值
greaterThan<field> greaterThan=<value>1 个值
greaterThanOrEqual<field> greaterThanOrEqual=<value>1 个值
inBetween<field> inBetween=<first>,<second>2 个值
notInBetween<field> notInBetween=<first>,<second>2 个值

date(日期维度)

语法:<field> <operator form>

操作符参数形式值数量
isNull/notNull同 string0 个值
equals/notEquals<field> equals=<value>[,<value>...]1+ 个值
lessThan/lessThanOrEqual/greaterThan/greaterThanOrEqual各 1 个值1 个值
inThePast<field> inThePast=<count>{unit:<unit>,completed:<bool>}1 个 count;settings 必填
notInThePast同上1 个 count;settings 必填
inTheNext同上1 个 count;settings 必填
inTheCurrent<field> inTheCurrent=<unit>1 个 unit
notInTheCurrent<field> notInTheCurrent=<unit>1 个 unit
inBetween<field> inBetween=<first>,<second>2 个值

日期单位(units)限定为:daysweeksmonthsquartersyears(常量filterExpressionDateUnits定义于 operators.ts)。其中completed语义为:completed=false表示包含未满的、部分经过的周期(如"过去 2 周"含当前这一周),completed=true表示只统计完整周期。

boolean(布尔维度)

语法:<field> <operator form>

操作符参数形式值数量
isNull/notNull同 string0 个值
equals<field> equals=<value>1 个值
notEquals<field> notEquals=<value>1 个值

从 operators.ts 可以看到布尔类型的equals/notEqualsargumentCount被固定为1(不像 string/number/date 那样是oneOrMore),且布尔值直接写成true/false即可。

操作符 × 类型矩阵的实现依据

上述矩阵并非文档臆造,而是由argumentCountByFilterType这张表驱动生成的:

  • isNull/notNull(presence 类)对四种类型全部可用,参数个数为 0;
  • equals/notEquals四种类型都可用,但布尔仅限 1 值;
  • startsWith/endsWith/include/doesNotInclude通过stringOperators数组映射,仅 string 类型可用,其余类型在unsupportedTypes中被置为null
  • 四个比较操作符(lessThan等)经comparisonOperators映射,仅 number 与 date 可用,且固定 1 个值;
  • inThePast等相对日期操作符、inTheCurrent等当前周期操作符,仅 date 可用
  • inBetween仅 number/date(各 2 值),notInBetween仅 number(2 值)。

getFilterTypeGrammar(expressionSchemas.ts)正是遍历该定义数组,把上述表格逐条渲染成 SKILL.md 中的### string/### number/### date/### boolean小节——文档与代码由同一份数据驱动,这也是"以代码为唯一事实源"的体现。


四、字面量规则:裸标量、引号与转义

表达式中的字段 ID 与值(标量)遵循一套格式规则,其精确实现位于 examples.ts:

裸标量(bare scalar)可用。一个值若匹配正则/^[^\s,{}=()\\'"]+$/(不含空白、逗号、花括号、等号、括号、反斜杠、引号)且不是保留字andornull(不区分大小写),就可以不带引号直接书写。

需要加双引号的场景

  • 包含保留字的值(如字段或值恰好叫and/or/null);
  • 包含空白、标点的字符串(典型如撇号'、括号()),例如 SKILL.md 中给出的示例:orders_product_name equals="Coffee Filters (100pk)"(该示例由常量FILTER_EXPRESSION_PUNCTUATED_STRING_EXAMPLE生成于 examples.ts);
  • 不确定时,一律加引号

引号与转义语义

  • 双引号内的逗号、花括号按字面量处理(不再作为参数分隔符或 settings 结构解析);
  • 反斜杠\用于转义(如\"\\);
  • 字段 ID 同理:若字段名包含特殊字符或恰好是and/or,用反引号包裹并转义(见formatFieldId,examples.ts)。

四种操作符参数形态(argumentSyntax)

在 expressionSchemas.ts 中,参数形态按语法类型区分为四种:

形态输出示例说明
none<operator> [0 values]isNull,不带=
values<operator>=<value>[,<value>...]普通值列表,=连接
relativeDate<operator>=<count>{unit:<unit>,completed:<bool>} [1 count; settings required]相对日期,count 必填、settings 必填
currentDate<operator>=<unit> [1 unit]当前周期,只填一个单位

例如inThePast=2{unit:weeks,completed:true}中:2是 count,{unit:weeks,completed:true}是 settings(花括号结构)。settings 中的值会与 arguments 一起计入规则值总数(见下节限制)。


五、硬性限制:四条边界

parse.ts 定义了四条防失控边界,SKILL.md 中的 "Limits" 一行即来源于此:

export const FILTER_EXPRESSION_MAX_LENGTH = 16_384; // 整个表达式最长 16384 字符 export const FILTER_EXPRESSION_MAX_RULES = 256; // 最多 256 条规则 export const FILTER_EXPRESSION_MAX_VALUES_PER_RULE = 256; // 每条规则最多 256 个值(含 settings 值) export const FILTER_EXPRESSION_MAX_LITERAL_LENGTH = 256; // 每个字面量(字段名/值/setting 名/值)最长 256 字符

这些限制在validateParsedFilterExpression(parse.ts)中被逐一强制执行,任何超限都会返回带span(出错位置)的FILTER_EXPRESSION_BOUNDS_EXCEEDED错误;表达式整体超长则会在解析前直接拦截(parse.ts)。此外,输入 schema 层也做了z.string().min(1).max(FILTER_EXPRESSION_MAX_LENGTH)的预校验(expressionSchemas.ts)。


六、底层解析:PEG 语法、AST 与连接符约束

解析器与 AST

过滤器表达式不是正则硬匹配,而是由PEG(Parsing Expression Grammar)语法生成的解析器解析。语法源文件为 grammar.ts(filterExpressionGrammar,共 219 行),配合 parser.ts 使用,AST 类型定义于 ast.ts。

解析入口parseFilterExpression(input)(parse.ts)的流程为:

  1. 检查整体长度是否超过FILTER_EXPRESSION_MAX_LENGTH
  2. 调用生成的 PEG 解析器,捕获语法错误并映射为FILTER_EXPRESSION_SYNTAX错误(带行号/列号定位,getPositionAtOffset会把偏移量换算为{line, column});
  3. 对解析出的 AST 执行四条边界校验;
  4. 返回{ success: true, expression }或带错误码、消息、span 的失败结果。

连接符约束:"AND 与 OR 不可混用"

PEG 语法中EXPRESSION产生式(grammar.ts)在遍历规则间的连接符时,一旦发现前后连接符不一致,就立即返回专用错误:

FILTER_EXPRESSION_MIXED_CONNECTORS A flat filter expression cannot mix AND and OR connectors.

因此dimensions: a equals=1 OR b equals=2 AND c equals=3这类混合写法必然解析失败。同时需要注意:schema 对不同的工具/类别有**连接符策略(connector policy)**差异——FilterExpressionConnectorPolicy分为andOnlyandOr两种(expressionSchemas.ts):

  • andOr:规则可用 AND 或 OR 连接,但同一表达式内不可混用run_metric_querydimensions/metrics/tableCalculations即此策略,对应FILTER_EXPRESSION_GRAMMAR_DESCRIPTION);
  • andOnly只允许 AND,OR 不受支持(对应FILTER_EXPRESSION_AND_ONLY_GRAMMAR_DESCRIPTION,适用于search_field_values.filters等 AND-only 场景)。

两条语法描述字符串分别由getFilterExpressionGrammarDescription('andOr')getFilterExpressionGrammarDescription('andOnly')生成,SKILL.md 中写入的是 andOr 版本。

错误信息的工程化

所有解析错误都携带span(起止 offset、行、列),便于 MCP 客户端把错误定位回表达式原文;语法错误统一归并为FILTER_EXPRESSION_SYNTAX,配合生成器原生错误信息(parse.ts)一起返回,方便 LLM 在下一轮修正自己的输出。


七、运行时差异:MCP 与 Agent 的指导内容并不相同

filterGuidance.ts(filterGuidance.ts)中定义了filterExpressionGuidanceByRuntime,为agentmcp两种运行时生成不同的指导段落:

维度agent 运行时mcp 运行时(本 SKILL.md)
适用工具generateVisualizationrun_metric_query
额外规则"聚合自定义指标过滤器是扁平 AND 表达式"
searchFieldValuessearchFieldValues.filters(驼峰)search_field_values.filters(下划线)

此外,agent 运行时还额外附加了时间过滤(Time-based filtering)指导(filterGuidance.ts 与结构化过滤器版本的STRUCTURED_FILTER_GUIDANCE_SECTION):只要用户提到时间窗口("last 3 months"、"this quarter"、"since March"),就必须在维度表达式里显式加入日期规则,描述性文字、排序、limit 或结果数据中观察到的日期都不能替代真正的过滤器;相对窗口用inThePast,显式区间用inBetween;相对窗口以提示词顶部声明的"今天"为基准解析,绝不能锚定字段元数据或查询结果里的日期;多个粒度高对齐的周期(如 2025-03 与 2025-05)优先用一条多值equals规则,让所有条件保持在 AND 之下;limit只能用于用户明确要求的 "top N" 场景,不能用来近似时间窗口。MCP 版本的 SKILL.md 虽未内联这段长文,但inThePast/inBetween的操作符语义与之一致。


八、实战要点速查

  1. 先看工具 schemarun_metric_queryqueryConfig.filterssearch_field_values用顶层filters;若工具 schema 是结构化过滤器对象,则本语法不适用。
  2. 无过滤就写nullqueryConfig.filters: nullsearch_field_values直接省略filters)。
  3. 类别归属看元数据 kind:维度进dimensions,指标/自定义指标进metrics,表计算进tableCalculations
  4. 每类别一条扁平字符串;内部统一 AND 或统一 OR;类别间隐式 AND。
  5. 值写法:裸标量优先;含保留字(and/or/null)、空白或标点时用双引号;不确定就加引号;引号内逗号、花括号为字面量,\转义。
  6. 日期:相对窗口inThePast=2{unit:weeks,completed:true}(settings 必填),当前周期inTheCurrent=month,显式区间inBetween=2025-01-01,2025-01-31
  7. 别越界:≤256 条规则、每条 ≤256 值(含 settings)、每个字面量 ≤256 字符、整个表达式 ≤16384 字符;不要混用 AND/OR。
  8. 修错看 span:解析错误会带行/列定位,据此精确修改表达式,而不是整段重写。

延伸阅读

  • 技能文档本体:filter-expressions/SKILL.md
  • 生成该文档的模板与指导段落:filterExpressionSkill.ts、filterGuidance.ts
  • 操作符定义与语法渲染:operators.ts、expressionSchemas.ts
  • 解析与校验:parse.ts、grammar.ts、ast.ts
  • 字面量格式与示例生成:examples.ts
  • 技能加载与 MCP 资源暴露:builtInSkills.ts、builtInSkills.test.ts

【免费下载链接】lightdashAgentic BI. Analytics at the speed of code ⚡️项目地址: https://gitcode.com/GitHub_Trending/li/lightdash

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询