You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在KQL/Grafana中循环遍历查询提取JSON内目标子串

Grafana 嵌套链接JSON字段解析为表格列的实现方案

场景说明

Grafana表格直接展示原始长嵌套JSON字符串没有实际业务价值,当前需要处理数千条返回结果,从每条结果携带的链接数组JSON中提取两类信息:

  • 每个链接对象url字段末尾的数字工作项ID
  • 每个链接对象attributes.name字段的关联类型值
    最终实现效果为:以关联类型值作为表格列名,对应关联的工作项ID作为列值,结构化展示在表格中。

示例待解析原始数据如下:

[
  {
    "rel": "System.LinkTypes.Hierarchy-Forward",
    "url": "https://fakelink/workItems/247155",
    "attributes": {
      "isLocked": false,
      "name": "Child"
    }
  },
  {
    "rel": "System.LinkTypes.Hierarchy-Forward",
    "url": "https://fakelink/workItems/247154 ",
    "attributes": {
      "isLocked": false,
      "name": "Child"
    }
  },
  {
    "rel": "System.LinkTypes.Hierarchy-Forward",
    "url": "https://fakelink/247160",
    "attributes": {
      "isLocked": false,
      "name": "Child"
    }
  },
  {
    "rel": "System.LinkTypes.Hierarchy-Forward",
    "url": "https://fakelink/247156",
    "attributes": {
      "isLocked": false,
      "name": "Child"
    }
  },
  {
    "rel": "System.LinkTypes.Hierarchy-Forward",
    "url": "https://fakelink/workItems/247159",
    "attributes": {
      "isLocked": false,
      "name": "Child"
    }
  },
  {
    "rel": "System.LinkTypes.Hierarchy-Forward",
    "url": "https://fakelink/workItems/247157",
    "attributes": {
      "isLocked": false,
      "name": "Child"
    }
  },
  {
    "rel": "System.LinkTypes.Hierarchy-Forward",
    "url": "https://fakelink/247158",
    "attributes": {
      "isLocked": false,
      "name": "Child"
    }
  }
]

可选实现方案

方案1:数据源侧预处理(优先选,性能最优)

如果用的是支持JSON处理的数据源(PostgreSQL、MySQL、Elasticsearch、Azure DevOps等),直接在查询阶段完成字段拆分,不要把原始长字符串丢给Grafana前端处理,几千条数据走前端解析很容易出现渲染卡顿。
核心处理逻辑:

  • 先将存储JSON的字段反序列化为结构化数组
  • 遍历数组中每个链接对象:
    1. 读取attributes.name作为分组键,也就是后续的表格列名
    2. 提取url末尾的数字ID:直接用正则/(\d+)\s*$/匹配url结尾的连续数字即可,不需要写死匹配/workItems/路径——样例里近一半的url没有这个路径段,这个正则还能自动兼容url末尾带多余空格的脏数据(比如样例里247154那条结尾的空格)
    3. 同一条主记录下,相同关联类型的多个ID按展示需求聚合,比如用逗号、换行符拼接成单个字符串

以PostgreSQL为例,查询阶段的解析代码参考:

SELECT
  record_id,
  -- 按链接类型聚合ID为键值对
  jsonb_object_agg(
    link_item->'attributes'->>'name',
    array_agg(regexp_replace(link_item->>'url', '^.*?(\d+)\s*$', '\1', 'g'))
  ) AS parsed_link_map
FROM your_data_table,
-- 展开JSON数组为逐行的链接对象
LATERAL jsonb_array_elements(link_json_field::jsonb) AS link_item
GROUP BY record_id, link_json_field

方案2:Grafana前端转换(数据源不支持JSON解析时使用)

Grafana 8.0及以上版本可以在面板的Transform栏添加「Add field from calculation」或自定义JavaScript转换,直接在前端完成字段拆分,不需要改查询逻辑。
转换核心逻辑:

  1. 遍历查询返回的每一行主记录
  2. 对每行存储原始JSON的字段做JSON.parse()转成对象数组,加空值兜底避免解析失败
  3. 初始化空对象存储解析后的列键值对
  4. 循环遍历每个链接对象,提取关联类型名和对应的ID,同类型多ID用分隔符拼接
  5. 将解析出的键值对合并到原行数据,删除冗余的原始长JSON字段即可

可直接复用的转换代码片段:

const parsedResult = rawData.map(row => {
  // 把workItemLinks替换成你自己存储原始JSON的字段名
  let linkList = [];
  try {
    linkList = JSON.parse(row.workItemLinks || '[]');
  } catch (e) {
    // 解析失败直接返回空,避免阻塞整表渲染
    linkList = [];
  }
  const parsedCols = {};
  linkList.forEach(link => {
    const colName = link?.attributes?.name || 'unknown_type';
    const matchRes = link?.url?.match(/(\d+)\s*$/);
    const workItemId = matchRes ? matchRes[1] : null;
    if (!workItemId) return;
    // 同类型多ID用逗号拼接,需要换行的话把逗号改成\n即可
    parsedCols[colName] = parsedCols[colName] 
      ? `${parsedCols[colName]}, ${workItemId}` 
      : workItemId;
  });
  // 移除原始JSON字段,合并解析后的列
  const { workItemLinks, ...baseFields } = row;
  return { ...baseFields, ...parsedCols };
});

return parsedResult;

避坑提示

  • 一定要加异常兜底:JSON格式损坏、url不符合规则、name字段为空这类脏数据在几千条的量级里基本必然存在,不做判断很容易导致整个面板渲染白屏
  • 数据量超过1000条优先选数据源侧预处理,前端JS解析+渲染的速度会比数据源预处理慢一个数量级,数据量大的时候面板加载要等好几秒
  • 如果后续需要支持点击ID跳转到对应工作项页面,直接在Grafana的字段覆盖里给对应ID列加数据链接即可,不需要在解析阶段拼跳转地址

内容的提问来源于stack exchange,提问作者Hudson Carpenter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:01:08