关键词补全接口实用教程

yiask_keywords:complete 用于搜索当前用户有权访问的业务关键词,可用于搜索框联想、筛选器候选值和问句输入提示。接口支持中文、全拼和拼音首字母匹配,也可以按原始业务表中的字段进一步筛选结果。

调用接口时还需要提供符合许可证要求的 OriginReferer。本文的 curl 示例使用 Referer,完整规则请参考 API 请求来源与许可证校验

接口地址:

POST /api/yiask_keywords:complete

快速开始

下面的请求搜索包含“销售”的关键词,每页返回 20 条:

curl -X POST 'http://localhost:13000/api/yiask_keywords:complete' \
  -H 'Authorization: Bearer <token>' \
  -H 'Referer: http://localhost:13000/' \
  -H 'x-spaces: default' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "keyword": "销售",
    "page": 1,
    "pageSize": 20,
    "sort": ["value"]
  }'

响应示例:

{
  "data": [
    {
      "value": "销售一部",
      "type": "dimension",
      "schema": "employees",
      "field": "部门"
    }
  ],
  "count": 36,
  "page": 1,
  "pageSize": 20,
  "totalPage": 2
}

其中:

  • value 是建议展示或写入查询条件的值。
  • schema 是关键词所属的业务模型。
  • field 是该值在原始业务表中的来源字段。
  • type 是字段类型或关键词类型。

请求参数

参数类型默认值说明
keywordstring-必填。待补全的关键词;支持文本、全拼和拼音首字母匹配。空字符串返回空结果。
pagenumber1普通模式的页码,从 1 开始。传入 dataFilter 后不使用此参数。
pageSizenumber20每次最多返回多少条,取值范围为 1 至 50。
sortstring[]-排序字段。支持 valuetypeschemafield,字段名前加 - 表示降序,例如 ["-value"]
filterobject-筛选 yiask_learned 中的关键词元数据。
dataFilterobject-筛选原始业务表中的数据。传入后接口自动切换为游标分页。
cursorstring-dataFilter 模式的续查游标。首次请求不传,后续原样传入上次响应的 nextCursor

filterdataFilter 的作用不同:

参数筛选对象示例用途
filter已学习关键词的模型、来源字段等元数据只搜索 employees 模型的“名称”字段
dataFilter原始业务表中的行只返回“部门等于生产部”的员工名称

普通分页

不传 dataFilter 时,接口使用页码分页,并返回精确的 counttotalPage

读取第二页:

curl -X POST 'http://localhost:13000/api/yiask_keywords:complete' \
  -H 'Authorization: Bearer <token>' \
  -H 'Referer: http://localhost:13000/' \
  -H 'x-spaces: default' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "keyword": "销售",
    "page": 2,
    "pageSize": 20,
    "sort": ["value"]
  }'

page >= totalPagedata 为空时,不需要再请求下一页。

按原始业务表筛选

假设原始业务表中有以下记录:

{
  "名称": "张三",
  "部门": "生产部"
}

学习结果中可能只保存了“张三来自名称字段”,并没有复制整行数据。要搜索生产部员工的名称,可以传入 dataFilter

curl -X POST 'http://localhost:13000/api/yiask_keywords:complete' \
  -H 'Authorization: Bearer <token>' \
  -H 'Referer: http://localhost:13000/' \
  -H 'x-spaces: default' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "keyword": "张",
    "pageSize": 20,
    "sort": ["value"],
    "dataFilter": {
      "部门": {
        "$eq": "生产部"
      }
    }
  }'

服务端会根据学习结果中的 schema + field 自动分组,再分批回查对应的原始业务表,只保留同时满足以下条件的候选值:

  1. 候选值与 keyword 匹配。
  2. 原始业务表中的记录满足 dataFilter
  3. 候选值确实存在于该记录的来源字段中。
  4. 当前用户对模型、来源字段、筛选字段和数据行都有访问权限。

filter 不是必填条件。模型或字段范围明确时,可以分别在 schemafield 下使用 $in,提前缩小扫描范围:

{
  "keyword": "张",
  "pageSize": 20,
  "filter": {
    "schema": {
      "$in": ["employees"]
    },
    "field": {
      "$in": ["名称"]
    }
  },
  "dataFilter": {
    "部门": {
      "$eq": "生产部"
    }
  }
}

dataFilter 使用 NocoBase filter 格式,可以组合 $eq$in$and$or 等操作符。例如筛选生产部或仓储部的在职员工:

{
  "$and": [
    {
      "部门": {
        "$in": ["生产部", "仓储部"]
      }
    },
    {
      "状态": {
        "$eq": "在职"
      }
    }
  ]
}

使用 nextCursor 加载更多

传入 dataFilter 后,筛选需要回查原始业务表。为了避免一次把全部候选加载到内存,接口改用 hasNext + nextCursor 游标分页,不返回精确的 counttotalPage

首次请求不要传 cursor。响应示例:

{
  "data": [
    {
      "value": "张三",
      "type": "dimension",
      "schema": "employees",
      "field": "名称"
    }
  ],
  "pageSize": 20,
  "hasNext": true,
  "nextCursor": "eyJ2ZXJzaW9uIjoxLCJvZmZzZXQiOjUwMCwiZmluZ2VycHJpbnQiOiIuLi4ifQ"
}

hasNexttrue 时,把 nextCursor 原样放进下一次请求的 cursor

curl -X POST 'http://localhost:13000/api/yiask_keywords:complete' \
  -H 'Authorization: Bearer <token>' \
  -H 'Referer: http://localhost:13000/' \
  -H 'x-spaces: default' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "keyword": "张",
    "pageSize": 20,
    "cursor": "eyJ2ZXJzaW9uIjoxLCJvZmZzZXQiOjUwMCwiZmluZ2VycHJpbnQiOiIuLi4ifQ",
    "sort": ["value"],
    "dataFilter": {
      "部门": {
        "$eq": "生产部"
      }
    }
  }'

使用游标时需要遵守以下规则:

  • nextCursor 是不透明字符串,客户端不要解析、修改或自行生成。
  • cursor 外,后续请求的 keywordpageSizesortfilterdataFilter 必须与首次请求保持一致。
  • hasNextfalse 时停止请求,此时 nextCursornull
  • 用户修改搜索词或筛选条件后,应丢弃旧游标并重新发起首次请求。
  • 用户的可访问空间或模型范围变化后,旧游标也不能继续使用。

前端“加载更多”的简化实现如下:

const endpoint = 'http://localhost:13000/api/yiask_keywords:complete';
const baseRequest = {
  keyword: '张',
  pageSize: 20,
  sort: ['value'],
  dataFilter: {
    部门: { $eq: '生产部' },
  },
};

let nextCursor;
let hasNext = true;
const suggestions = [];

while (hasNext) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: {
      Authorization: 'Bearer <token>',
      'x-spaces': 'default',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      ...baseRequest,
      ...(nextCursor ? { cursor: nextCursor } : {}),
    }),
  });

  if (!response.ok) {
    throw new Error(`关键词补全失败:${response.status}`);
  }

  const result = await response.json();
  suggestions.push(...result.data);
  hasNext = result.hasNext;
  nextCursor = result.nextCursor;
}

浏览器会自动发送 Origin,不能通过 JavaScript 手工设置 OriginReferer。请确保页面的实际来源同时符合许可证域名和跨域白名单配置。

实际页面通常在用户点击“加载更多”或滚动到底部时请求一次,不建议在页面打开后立即循环拉取所有结果。

为什么一页可能少于 pageSize

底表筛选模式会持续分批扫描候选,尽量填满当前页。但以下情况可能导致返回不足 pageSize

  • 剩余候选已经全部扫描完,此时 hasNextfalse
  • 单次请求已扫描 10,000 个候选,但满足底表条件的结果仍不足一页,此时 hasNexttrue,客户端可使用 nextCursor 继续扫描。
  • 候选因模型、字段或行级权限被过滤。
  • 候选没有来源字段,无法回查原始业务表。

接口每批最多扫描 500 个学习候选,单次请求最多扫描 10,000 个。批量扫描可以控制内存占用,但筛选条件命中率很低或搜索范围很大时,仍建议使用 filter 限定模型和来源字段。

权限与结果边界

接口会应用当前请求上下文中的权限:

  • 只搜索 x-spaces 指定且当前用户有权访问的空间。
  • 只返回当前用户有权访问的业务模型和字段。
  • 使用 dataFilter 时,用户必须能访问来源字段及筛选条件涉及的字段。
  • 回查原始业务表时会应用行级 defaultAuthQuery
  • 指标和没有来源字段的学习候选不会出现在 dataFilter 模式的结果中。

因此,不同用户使用相同参数,得到的结果和 hasNext 可能不同。

常见错误

现象或错误原因处理方式
dataFilter must be an object.dataFilter 传成了数组、字符串等非对象值改为 JSON 对象;不需要底表筛选时删除该参数
Invalid or expired keyword completion cursor.游标格式错误,或续查时改变了查询条件、权限范围丢弃旧游标,使用当前条件重新发起不带 cursor 的首次请求
返回空数组没有匹配值,或用户无模型、字段、数据行权限检查 keyword、空间 Header、筛选字段名称和当前用户权限
结果不足一页但 hasNexttrue本次已达到 10,000 个候选的扫描上限继续传入 nextCursor 请求下一批
传了 dataFilter 却没有 count底表筛选使用游标分页,不计算精确总数使用 hasNext 判断是否还能加载更多

接入建议

  • 搜索输入框应做防抖,并在关键词变化时清空结果和游标。
  • 普通模式使用 page;底表筛选模式只使用 cursor,不要混用两种翻页状态。
  • filter.schemafilter.field 下的 $in 作为性能优化项,而不是底表筛选的必填项;请求体统一使用嵌套对象形式。
  • 不要依赖游标内部结构,也不要把游标长期保存为业务数据。
  • 生产环境应使用实际服务地址,并妥善保管 Token,避免写入前端源码或日志。