1. 页面概览

1.1 是什么

对象查询页面(ObjectQueryPage.vue)让用户以「选择对象 → 勾选字段 → 配置过滤/排序 → 执行」的方式对本体对象做结构化查询,结果以二维表格(行 × 列)渲染。它走的是 POST /objects/:id/query 语义查询接口:

本页与「SQL 工作台」形成互补:SQL 工作台用 FROM 对象名 写自由 SQL,本页则是表单化的 OOL 查询构造器,把对象查询语法(字段/过滤算子/排序/page/page_size/params)显式暴露给用户操作。

1.2 核心价值

维度说明
表单化 OOL无需写 SQL,下拉+勾选+动态行即可构造对象查询
十种过滤算子eq/ne/gt/gte/lt/lte/like/contains/in/is_null 覆盖常规条件
页码分页 + CSV 导出支持 page/page_size 翻页(下一页按满页启发式);「导出 CSV(当前页)」纯前端下载,UTF-8 BOM
安全闸门后端白名单校验 + RLS/CLS 只读注入 + 参数绑定,防注入防越权
编辑态可见未合入的草稿修改会叠加到结果并用「已编辑」标记说明
二维结果行 × 列表格直接呈现,与后端 columns/rows 结构一一对应

1.3 一句话总结

在 Foundry 里「选对象、勾字段、配条件」做解查数,后端负责 OOL 翻译与 RLS/CLS 安全注入,前端把结果渲染成带「已编辑」标注的二维表格。

2. 访问入口

2.1 路由与菜单

2.2 认证与权限

2.3 端口与 API 前缀

3. 界面布局

页面纵向分为 5 个板块(从上到下):

┌──────────────────────────────────────────────────────────────┐
│ ① 页头:标题「对象语义查询」                                    │
│ ② 操作结果提示条(alert,失败红,可关闭)                        │
│ ③ 选择对象卡片                                                 │
│    ├─ 对象类型 * 下拉(显示名(name · #id))                   │
│    └─ 对象信息(display_name · 表 xxx · 数据源 #N)             │
│ ④ 查询配置卡片(选中对象后出现)                               │
│    ├─ 查询字段 fields 多选(不选=全部列,计数标签)             │
│    ├─ 过滤条件 filters(+ 过滤条件 动态行)                     │
│    └─ 排序字段 order_by / 每页行数 page_size / 降序 order_desc   │
│    └─ [查询] 按钮                                              │
│ ⑤ 查询结果卡片(本页 N 行 × M 列 + 已编辑列标记                │
│    + [导出 CSV(当前页)] + 翻页条[上一页/下一页])            │
└──────────────────────────────────────────────────────────────┘

各板块职责:

4. 交互元素详解

4.1 对象类型下拉

属性
控件<select v-model.number=selectedObjectId>
数据来源GET /api/v1/ontology/objectsonMounted(fetchObjects) 拉取,data.data 数组)
占位项「选择对象类型」(value=null,disabled)
选项格式display_name || name(name · #id),value 为 object_type.id
交互效果@change="loadDetail" 选中即加载详情;切换对象时清空上次结果与错误

4.2 对象信息展示

选中对象后 loadDetailGET /ontology/objects/:iddata.data 存为 detail,展示「display_name || name · 表 base_table || '-' · 数据源 #data_source_id」;未选中时显示灰色「选择对象后自动加载属性清单...」。loadDetail 还会把 queryForm.page 重置为 1、并把 queryForm.page_size 兜底为 100(page_size || 100),同时清空上次结果与错误。

4.3 查询字段 fields 多选

属性
控件每个属性一个 checkbox(.row-check),标签为 p.name + 灰色 p.data_type
数据来源detail.properties(对象详情属性数组)
计数标题右侧蓝色计数 {{ queryForm.fields.length }}
语义不选 = 全部列(请求体传空数组 fields: []);选中的属性名进入 fields
空态对象无属性时显示「该对象暂无属性。」

4.4 过滤条件 filters 动态行

属性
添加按钮「+ 过滤条件」(.sub-title 右侧小按钮),push 一条 {field:'', op:'eq', value:''}
行结构字段下拉 + 算子下拉 + 值输入框 + 「删除」按钮
字段下拉选项为 detail.properties[].name,占位「字段」(disabled 空值)
算子下拉filterOps 十种:eq, ne, gt, gte, lt, lte, like, contains, in, is_null
值输入框placeholder「值(in 用逗号分隔)」;算子为 is_null 时输入框禁用(无需值)
删除按钮「删除」调 removeRow(filters, i) 从数组移除该行
空态无过滤条件时显示「暂无过滤条件。」
提交处理只提交 f.field 非空的行;op !== 'is_null' 时带 valueis_null 省略 value

4.5 排序与分页

字段控件说明
排序字段 order_by<select>选项为属性名,空值「不排序」;建议指定以保证翻页稳定
每页行数 page_size<select>选项 20/50/100/200/500,默认 100;上限 500(后端钳制,前端 clampPageSize 同步兜底:非正回退 20、超限钳 500);切换即回第 1 页并自动重查
降序 order_desccheckbox勾选后请求体 order_desc: true

配置区下方有提示文案,说明「页码式分页:page 从 1 起,page_size 上限 500;后端不返回总数,下一页按满页启发式判断;建议指定排序字段」。

4.6 「查询」按钮

属性
按钮文字「查询」(查询中变「查询中...」并禁用)
前置校验未选对象时提示「请先选择对象类型」
请求体{fields, filters, order_by, order_desc, page, page_size, params: {}}不再传 limit/offset
失败queryError 红条显示「查询失败:<error>」,结果清空
成功queryResult = {columns, rows, editedFields}exec = {page, page_size}queried = true
空页回退若返回 0 行且当前页 > 1,提示「第 N 页无数据(可能已翻过头),已回退到第 N-1 页」并自动回退重查(仅回退一次)

4.7 查询结果表格

元素说明
标题「查询结果」+「本页 N 行 × M 列 · 第 P 页」
「导出 CSV(当前页)」按钮结果卡片右上角;纯前端导出当前页(UTF-8 BOM 防 Excel 中文乱码),文件名 <对象名>_page<P>.csv;无数据时禁用
表头queryResult.columns 逐列渲染;列在 editedFields 中时追加「已编辑」黄色小标签(title「该列叠加了编辑态未合并的值」)
单元格formatCell(cell)null 显示空串,对象/数组转 JSON,其余 String()
空行rows.length === 0 时渲染跨列灰字「无查询结果」
翻页条「上一页」「第 P 页」「下一页」;exec.page > 1 才可上一页,下一页要求「本页行数 == 请求 page_size」(满页启发式);旁边常驻提示「后端不返回总数,下一页按满页判断(本页 N / page_size 行)」
未查询态!queryResult && queried 时显示「请先选择对象并配置查询条件,然后点击"查询"。」

5. 后端关联

5.1 API 客户端

5.2 端点表

方法路径请求体说明
GET/ontology/objects对象类型列表(data.data 数组,含 object_type
GET/ontology/objects/:id对象详情(data.data,含 object_type/properties
POST/objects/:id/queryOOL 查询请求体(支持 page/page_size执行对象语义查询(路径不带 /ontology);响应 datatotal
POST/ontology/objects/:id/query同左语义等价别名路径

查询请求体(后端 objectQueryRequest):

{
  "fields": ["amount", "status"],
  "filters": [
    { "field": "amount", "op": "gt", "value": 1000 },
    { "field": "status", "op": "in", "value": ["paid", "shipped"] },
    { "field": "deleted_at", "op": "is_null" }
  ],
  "order_by": "created_at",
  "order_desc": true,
  "page": 1,
  "page_size": 100,
  "traverse": [],
  "params": {}
}

分页字段口径:后端 objectQueryRequest 同时支持 limit/offset(既有契约)与 page/page_size(页码式便捷参数,D-B5-01);两者同给时 page/page_size 优先。page 从 1 起(≤0 回退第 1 页),page_size 上限 500(MaxPageSize,超出钳制为 500),非法/缺省回退默认 20;页码式会映射为 LIMIT page_size OFFSET (page-1)*page_sizesemantic/query.go applyPaging)。本页已从 limit/offset 切换为 page/page_size 响应体为 domain.QueryResult{columns, rows, edited_fields}不含 total/总数,故前端无法按总数计算总页数,只能用「满页」启发式决定下一页是否可用。

5.3 响应结构

统一包装 {code: 0, data: ...}datadomain.QueryResult

{
  "code": 0,
  "data": {
    "columns": ["id", "amount", "status", "created_at"],
    "rows": [
      [1, 2500.0, "paid", "2026-08-29T10:00:00Z"],
      [2, 300.0, "shipped", "2026-08-28T09:00:00Z"]
    ],
    "edited_fields": ["status"]
  }
}

5.4 关联模块表

后端包职责
foundry/semanticSemanticQueryService.Execute:OOL 翻译(白名单校验 + RLS/CLS 注入)→ 连接器真实执行 → 编辑态叠加
foundry/server/semantic_handlers.gohandleObjectQuery:解析对象 id、注入 params.user_id、调用 Execute
foundry/ontology对象类型/属性/链接元数据、GetLatestEditValues 编辑态读取
platform/connector数据源连接器执行物理查询
platform/authRLS/CLS 注入与对象级 ontology:<name>:read 授权

5.5 关键机制

OOL 过滤算子:后端语义包定义算子全集为 eq / ne / not_eq(ne 别名)/ gt / gte / lt / lte / like / contains / starts_with / in / is_null / and / or / not;前端页面暴露其中十种(eq/ne/gt/gte/lt/lte/like/contains/in/is_null)。in 的值可为标量/切片,页面提示「in 用逗号分隔」。值一律参数化绑定,不拼接进 SQL。

安全注入handleObjectQueryreq.Params["user_id"] = currentUserID(c);语义服务 Translate 阶段做属性白名单校验(字段必须是本体属性集合成员,含 linkName.propName 关联属性),再经 ApplyRLS/ApplyCLS 注入只读安全口径。

对象级可见性:查询入口经 authorizeObjectRead 校验 ontology:<对象名>:read,无读权限返回 403 明确错误(不静默放行);内部调度路径或 apps 组件加载(APP-06 已知路径)可跳过。

编辑态叠加applyEditOverlay 按主对象行 id 批量取 LoadEditValues 最新值覆盖源表列(仅主对象 t0 属性,traverse/link 关联对象列为后续层);被覆盖的属性写入 edited_fields 并剥离注入的主键列,保证输出列契约稳定。

Traverse 遍历式扩展:请求体 traverse 支持沿 link 进入相邻对象再过滤(then_filters/select 限相邻对象属性,禁点号引用,可嵌套 ≤3 层)。本页已在「查询配置」中提供「遍历 traverse」编辑区(扁平步骤 + 父步骤表达嵌套,含 link 选择、select 属性勾选、then_filters 过滤行;3 层上限硬约束),提交时组装为后端嵌套结构;API 调用方亦可直接构造该字段。

6. 核心流程详解

6.1 主流程:执行一次对象查询

  1. 页面加载 onMounted(fetchObjects)GET /ontology/objects 填充对象下拉;
  2. 选择对象 → loadDetailGET /ontology/objects/:id → 渲染属性多选/过滤字段/排序字段,page 置 1、page_size 兜底 100;
  3. 勾选查询字段(不选=全部列),按需点「+ 过滤条件」添加过滤行并选算子填值;
  4. 选择排序字段、勾选降序、选择每页行数(page_size);
  5. 点「查询」→ POST /objects/:id/query(body 含 page/page_size,见 5.2)→ 后端翻译+注入+执行;
  6. 结果渲染二维表格,edited_fields 中的列显示「已编辑」标签;点翻页条「上一页 / 下一页」按 page±1 重查;点「导出 CSV(当前页)」下载当前页。

6.1.1 分页与「满页」启发式

后端查询响应体是 domain.QueryResult{columns, rows, edited_fields}不含 total,前端无法据此算总页数。因此「下一页」采用满页启发式:请求 page_size 条,返回行数 == page_size 时认为「可能还有下一页」并允许翻页,否则禁用下一页。若某页返回 0 行且页码 > 1,回退上一页并提示(仅回退一次,避免死循环)。翻页时 order_by 建议显式指定,否则无序结果的跨页边界可能不稳定。

6.2 分支流程:is_null 过滤

算子选 is_null 时值输入框禁用且提交时省略 value(后端 is_null 不需要值)。例:过滤已删除行用 {field: "deleted_at", op: "is_null"}

6.3 分支流程:in 多值过滤

算子选 in 时值按逗号分隔输入,如 paid, shipped。注意:前端把值作为字符串提交(out.value = f.value),若需数值列表由后端语义解析处理;逗号分隔是页面提示约定。

6.4 结果导出(导出 CSV)

本页已提供内置「导出 CSV(当前页)」按钮(结果卡片右上角),导出当前页结果为 CSV:

其它替代途径仍可选用:手动复制表格内容、SQL 工作台「导出 CSV」(写 SELECT ... FROM 对象名 ... 导出全量)、API 直接调用 POST /objects/:id/query 自行序列化(可配合 page/page_size 循环拉全量)。

6.5 编辑态叠加语义

当对象存在未合入的草稿修改(如 Action 测试/应用写入的编辑态),查询结果会叠加最新编辑值:被覆盖的属性列带「已编辑」标签;未 SELECT 该属性的列不受影响;聚合场景下编辑态叠加会回退为行级+内存聚合(超 10000 行标 approximate),保证行级与聚合一致。

7. 权限与安全

8. 常见问题与排错

8.1 点「查询」提示「查询失败:...」

8.2 「已编辑」列标记出现了但值「不新」

8.3 查询结果只有一行「无查询结果」

8.4 字段下拉/过滤字段为空

8.5 翻页「下一页」灰掉 / 看不到总页数

8.6 page/page_size 被钳制或回退

9. 已知缺陷与边界

说明
分页无总数(满页启发式)后端响应不含 total,前端「下一页」按「本页行数 == page_size」判断、无法显示总页数与精确页码跳转(如实边界,非缺陷
导出仅当前页「导出 CSV」为纯前端、仅导出当前页结果;导出全量需调大 page_size(≤500)或翻页多次,或走 SQL 工作台 / API(可配合 page 循环拉取)
traverse 已提供编辑 UI(≤3 层)请求体透传 traverse(空数组时后端视为无 traverse)。页面新增「遍历 traverse」编辑区:以「扁平步骤 + 父步骤 id」表达嵌套;每步从当前层对象的链接定义下拉选择 link(含目标对象 id 与基数),可勾选相邻对象属性作为 select(输出列别名 linkName.propName),可添加 then_filters(字段限相邻对象属性、禁止点号引用)。最多 3 层(后端 maxTraverseDepth=3semantic/traverse.go:22/46-49);到 3 层后「+ 子遍历」按钮禁用并提示「已达 3 层上限」;父 link 变更会清空其子级步骤。提交时组装为后端嵌套 TraverseQuerysemantic/query.go:116-121,请求体透传见 server/semantic_handlers.go:62/136)。覆盖范围then_filters 复用页面过滤算子下拉(eq/ne/not_eq/gt/gte/lt/lte/like/contains/starts_with/in/is_null),不含 and/or/not 布尔组合算子(需嵌套 Filter 结构,表单未表达)。边界不变:关联列 linkName.propName 显示与「编辑态仅主对象 t0 属性叠加」(traverse/link 关联对象列不叠加)维持既有语义
过滤值统一字符串前端把过滤 value 作为字符串提交,数值语义由后端按列类型解析;例外:in 算子(顶层 filters 与 traverse then_filters)的值由前端按逗号分隔或 JSON 数组转为数组后提交(后端 toAnySlice 拒绝字符串,semantic/query.go:955-964
编辑态仅主对象叠加traverse/link 关联对象列的编辑态为后续层,不叠加
关联列显示linkName.propName 关联字段若勾选会出现在 columns,单元格对象/数组转 JSON 文本

10. 2026-09-10 安全与行为修订

11. 2026-09-13 分页与导出补全

后端文件

项目文档

相邻页面链接