OneUptime API 查询过滤器 Includes 详解:用 JSON 实现多值集合匹配
2026/9/16 22:02:35 网站建设 项目流程

OneUptime API 查询过滤器 Includes 详解:用 JSON 实现多值集合匹配

【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime

本文围绕 OneUptime 官方 API 参考文档中的 Includes 查询代码示例,系统讲解Includes查询过滤器的 JSON 请求格式、字段语义、在数据库层的 SQL 转换原理,以及它在标签(Labels)、监控类型、库存状态等场景下的实战用法。读完本文,你将能够独立构造Includes查询体,理解其与EqualToIncludesAllIncludesNone等兄弟过滤器的差异,并能在 OneUptime 的查询 API 中正确使用多值匹配过滤。

Includes 查询过滤器是什么

在 OneUptime 的 API 查询体系中,Includes是一种"多值包含"查询过滤器:它匹配字段值属于给定数组中任意一个值的所有对象。用集合的语言描述,就是判断field ∈ value[],等价于 SQL 中的IN (...)语义。

官方数据类型注册表在 App/FeatureSet/APIReference/Utils/DataTypes.ts 中将其定义为:

A query filter that matches objects where a field value is included in the specified array of values.

典型的应用场景包括:

  • 按标签集合过滤资源:找出带有某个标签或某几个标签之一的所有监控器、事件、告警;
  • 按枚举集合过滤:找出monitorTypePingAPIPort之一的所有监控器;
  • 按状态集合过滤:找出库存资源中inventoryStatusstalerecent的记录;
  • 按 ID 集合过滤:一次请求内批量按主键或外键筛选多个目标对象。

请求体格式:从官方代码示例说起

官方示例文件 Includes.md 给出了一个完整的查询请求体:

{ "query": { "labels": { "_type": "Includes", "value": [ "aaa00000-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "bbb00000-bbbb-bbbb-bbbb-bbbbbbbbbbbb" ] } } }

该请求体语义为:查询所有labels(标签)字段值为aaa00000-aaaa-aaaa-aaaa-aaaaaaaaaaaabbb00000-bbbb-bbbb-bbbb-bbbbbbbbbbbb中任意一个的对象(注意示例文件中\_type的下划线转义在真实 JSON 中即为_type)。

请求体结构拆解

层级字段类型说明
顶层queryobject查询过滤条件容器,其 key 为要过滤的字段名,value 为过滤器对象
过滤器_typestring过滤器类型标记,此处固定为Includes
过滤器valuearray候选值数组,元素类型支持stringnumber(以及后端支持的ObjectID

这个 "_type判别字段 +value载荷" 的序列化协议是整个 OneUptime 查询层的通用线格式。官方数据类型详情页 App/FeatureSet/APIReference/Service/DataTypeDetail.ts 对Includes的属性描述为:

  • value(必填):Array<string | number>,一个候选值数组,只要字段值等于其中任意一个即匹配。

请求体的加载与渲染

在 API Reference 站点实现中,Service/DataType.ts 通过LocalCache.getOrSetString缓存并读取Includes.md内容,将其渲染到数据类型指南页面,供开发者在浏览参考文档时直接复制使用:

pageData["includesCode"] = await LocalCache.getOrSetString( "data-type", "includes", async () => { return await LocalFile.read( `${CodeExamplesPath}/DataTypes/Includes.md`, ); }, );

这意味着该示例是文档站点上Includes数据类型的官方配套样例,可以直接作为手写查询体的起点。

后端实现:Includes 查询操作符的序列化与反序列化

Includes在类型系统中被建模为一个查询操作符(Query Operator),其核心实现在 Common/Types/BaseDatabase/Includes.ts:

export type IncludesType = Array<string> | Array<ObjectID> | Array<number>; export default class Includes extends QueryOperator<IncludesType> { private _values: IncludesType = []; public get values(): IncludesType { return this._values; } public set values(v: IncludesType) { this._values = v; } public constructor(values: IncludesType) { super(); this.values = values; } public override toJSON(): JSONObject { return { _type: ObjectType.Includes, value: (this as Includes)._values, }; } public static override fromJSON(json: JSONObject): Includes { if (json["_type"] === ObjectType.Includes) { const valuesArray: Array<string> = []; for (const value of (json["value"] as Array<string>) || []) { valuesArray.push(JSONFunctions.deserializeValue(value) as string); } return new Includes(valuesArray); } throw new BadDataException("Invalid JSON: " + JSON.stringify(json)); } }

关键点:

  1. 受支持的元素类型IncludesType明确限定为stringObjectID(UUID 标识符)或number,与文档示例中的两个 UUID 字符串一致;
  2. 线格式对称toJSON()输出{ _type: "Includes", value: [...] }fromJSON()严格校验_type后重建对象,保证请求从浏览器经 HTTP 到达服务端后能够无损还原;
  3. ObjectType.Includes枚举:该判别值定义于 Common/Types/JSON.ts,与IncludesAllIncludesNoneInBetween等过滤器一同被注册在 JSON 序列化类型表中。

底层原理:Includes 如何转换为 SQL

普通标量列的转换:SQLIN

Includes作用于普通标量列时,Common/Server/Types/Database/QueryUtil.ts 会根据列元数据类型做分派:

} else if ( query[key] && query[key] instanceof Includes && tableColumnMetadata ) { if ( tableColumnMetadata.type === TableColumnType.EntityArray || tableColumnMetadata.type === TableColumnType.Entity ) { query[key] = (query[key] as Includes).values as any; } else { query[key] = QueryHelper.any((query[key] as Includes).values) as any; } }
  • 当目标列为Entity / EntityArray 关系列(如labels这种多对多标签关联),直接传入值数组交由 TypeORM 处理关系查询;
  • 当目标列为普通标量列(如monitorType枚举字符串),则交给QueryHelper.any()

QueryHelper.any()与底层私有方法in()实现在 Common/Server/Types/Database/QueryHelper.ts:

public static any( values: Array<string | ObjectID | number>, ): FindWhereProperty<any> { return this.in(values); // any and in are the same } private static in( values: Array<string | ObjectID | number>, ): FindWhereProperty<any> { values = values.map((value) => value.toString()); const rid: string = Text.generateRandomText(10); if (!values || values.length === 0) { return Raw(() => { return `TRUE = FALSE`; // this will always return false }, {}); } return Raw( (alias: string) => { return `(${alias} IN (:...${rid}))`; }, { [rid]: values }, ); }

由此可知:

  • 标量列上的Includes最终编译为参数化 SQL:WHERE "字段" IN (:...参数)
  • 所有元素统一toString()后绑定为参数,天然防 SQL 注入(参数绑定而非字符串拼接);
  • 空数组的特殊语义:若传入value: [],生成的谓词恒为TRUE = FALSE,即匹配不到任何对象——空数组按"匹配零行"处理(fail closed),而非匹配全部。

JSONB 列上的 Includes:展开为 OR 等值判断

Includes作用于customFields这类 JSONB 列时,走的是 Common/Server/Types/Database/JSONColumnQuery.ts 中的专用分支:

if (value instanceof Includes) { const values: Array<ScalarValue> = toScalarArray(value.values); return values.length === 0 ? null : this.anyOf(values); }

anyOf将候选数组展开为若干个"该键的标量值等于 X 或数组包含 X"的谓词,以OR连接。其底层equals谓词同时处理两种存储形态:

  • 键值以标量存储(单选用例):col ->> 'key' = CAST(:v AS TEXT)
  • 键值以数组存储(多选用例):col -> 'key' @> CAST(:v AS JSONB)(jsonb 包含运算)。

这正是 JSONColumnQuery.ts 中注释强调的"value matches whether it is stored as a scalar or inside an array":无论自定义字段被配置为单选还是多选,同一个Includes查询都能得到一致结果。同时,每个值都会被展开成独立谓词,因此该模块设置了MAX_JSON_QUERY_VALUES_PER_KEY = 200的上限,防止无限值列表把一条查询编译成巨型 SQL。

阈值与边界

  • JSONB 列过滤器单键最多200 个候选值,整条 JSON 查询最多50 个键,单个键名最长500 字符,超限即抛出BadDataException(返回 400);
  • 普通 SQL 列上的Includes无此显式上限,但同样遵循数据库参数数量限制,建议按业务面大小控制候选值规模。

使用示例:真实测试用例验证

仓库测试代码可以直接印证上述行为。例如 Common/Tests/Server/API/DashboardPublicResourceListAPI.test.ts 中通过new Includes(...)构造标签与监控类型过滤:

monitorType: new Includes(["Ping"]), labels: new Includes([fixedLabelId]), labels: new Includes([firstLabelId, secondLabelId]),

而 Common/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.ts 展示了库存过滤场景:

expect(query["entityType"]).toEqual(new Includes([EntityType.Service])); expect(query["inventoryStatus"]).toEqual(new Includes(["stale"])); new Includes([EntityType.Host, EntityType.KubernetesPod]),

可以看到,Includes在项目内被广泛用于:

  • 资源列表 API 的标签多选过滤(labels字段,EntityArray 列);
  • 监控器类型过滤(monitorType枚举);
  • 库存资源的实体类型、数据源、存活状态过滤;
  • 遥测指标按host.name等属性多值过滤(如 DashboardPublicMetricsAggregateAPI.test.ts 中的attributes: { "host.name": new Includes(["web-1", "web-2"]) })。

与其他查询过滤器的对比与选型

Includes并非唯一的集合型过滤器。理解它与其他操作符的差异有助于写出正确的查询:

过滤器语义典型 JSON说明
Includes字段值 ∈ 候选集合(OR){ "_type": "Includes", "value": ["a", "b"] }本文主角,匹配任一值
IncludesAll数组字段同时包含集合中所有值(AND){ "_type": "IncludesAll", "value": ["a", "b"] }要求数组字段同时含 a 与 b,见 Common/Types/BaseDatabase/IncludesAll.ts
IncludesNone数组字段不含集合中任意值(NOT IN){ "_type": "IncludesNone", "value": ["a"] }排除型过滤,见 Common/Types/BaseDatabase/IncludesNone.ts
EqualTo字段值等于单值{ "_type": "EqualTo", "value": "a" }单值等值比较,见 EqualTo.md
EqualToOrNull字段值等于单值或为 null{ "_type": "EqualToOrNull", "value": "a" }等值 + 空值兜底
InBetween字段值落在闭区间 [start, end]{ "_type": "InBetween", ... }数值/日期区间过滤

实现层面,IncludesAllIncludesNone的完整行为同样定义在 JSONColumnQuery.ts 的build()分支中(allOf用 AND 连接、IncludesNone对整个 OR 谓词取反),而 JSON 序列化判别枚举集中在 Common/Types/JSON.ts。当需要"任选其一"时用Includes;需要"全部满足"时用IncludesAll;需要"排除若干值"时用IncludesNone

使用建议与注意事项

  1. 标签过滤的首选方案:多对多标签(labels)过滤请直接使用Includes,请求体会被正确路由到 EntityArray 关系查询路径;
  2. 空数组行为value: []表示"匹配不到任何对象",若想表达"不限制",应省略该过滤器字段而不是传空数组;
  3. 候选值类型一致性value元素应使用与字段类型匹配的字符串(如 UUID)、数字或枚举文本;ObjectID在序列化后即为 UUID 字符串,可安全混用;
  4. JSONB 自定义字段:作用于customFields时,值既可以匹配标量存储(单选)也可以匹配数组存储(多选),无需关心字段在界面上的配置形态;
  5. 规模控制:JSONB 列单键最多 200 个候选值;普通列虽无硬性上限,仍建议控制规模以保证 SQL 执行性能。

小结

Includes是 OneUptime 查询体系中实现"多值集合匹配"的核心过滤器。从 官方代码示例 出发,我们梳理了其{ _type, value }的线格式协议、Includes.ts 中的类型实现、QueryUtil.ts 与 QueryHelper.ts 中的 SQLIN转换,以及 JSONColumnQuery.ts 中针对 jsonb 列的 OR 展开语义,并通过仓库测试用例验证了其在标签、监控类型、库存状态等场景的真实用法。掌握Includes及其兄弟过滤器,即可高效构造 OneUptime API 的复杂列表查询。

【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime

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

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

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

立即咨询