关键词补全接口实用教程
yiask_keywords:complete 用于搜索当前用户有权访问的业务关键词,可用于搜索框联想、筛选器候选值和问句输入提示。接口支持中文、全拼和拼音首字母匹配,也可以按原始业务表中的字段进一步筛选结果。
调用接口时还需要提供符合许可证要求的 Origin 或 Referer。本文的 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 是字段类型或关键词类型。
请求参数
filter 和 dataFilter 的作用不同:
普通分页
不传 dataFilter 时,接口使用页码分页,并返回精确的 count 和 totalPage。
读取第二页:
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 >= totalPage 或 data 为空时,不需要再请求下一页。
按原始业务表筛选
假设原始业务表中有以下记录:
{
"名称": "张三",
"部门": "生产部"
}
学习结果中可能只保存了“张三来自名称字段”,并没有复制整行数据。要搜索生产部员工的名称,可以传入 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 自动分组,再分批回查对应的原始业务表,只保留同时满足以下条件的候选值:
- 候选值与
keyword 匹配。
- 原始业务表中的记录满足
dataFilter。
- 候选值确实存在于该记录的来源字段中。
- 当前用户对模型、来源字段、筛选字段和数据行都有访问权限。
filter 不是必填条件。模型或字段范围明确时,可以分别在 schema 和 field 下使用 $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 游标分页,不返回精确的 count 和 totalPage。
首次请求不要传 cursor。响应示例:
{
"data": [
{
"value": "张三",
"type": "dimension",
"schema": "employees",
"field": "名称"
}
],
"pageSize": 20,
"hasNext": true,
"nextCursor": "eyJ2ZXJzaW9uIjoxLCJvZmZzZXQiOjUwMCwiZmluZ2VycHJpbnQiOiIuLi4ifQ"
}
当 hasNext 为 true 时,把 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 外,后续请求的 keyword、pageSize、sort、filter 和 dataFilter 必须与首次请求保持一致。
hasNext 为 false 时停止请求,此时 nextCursor 为 null。
- 用户修改搜索词或筛选条件后,应丢弃旧游标并重新发起首次请求。
- 用户的可访问空间或模型范围变化后,旧游标也不能继续使用。
前端“加载更多”的简化实现如下:
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 手工设置 Origin 或 Referer。请确保页面的实际来源同时符合许可证域名和跨域白名单配置。
实际页面通常在用户点击“加载更多”或滚动到底部时请求一次,不建议在页面打开后立即循环拉取所有结果。
为什么一页可能少于 pageSize
底表筛选模式会持续分批扫描候选,尽量填满当前页。但以下情况可能导致返回不足 pageSize:
- 剩余候选已经全部扫描完,此时
hasNext 为 false。
- 单次请求已扫描 10,000 个候选,但满足底表条件的结果仍不足一页,此时
hasNext 为 true,客户端可使用 nextCursor 继续扫描。
- 候选因模型、字段或行级权限被过滤。
- 候选没有来源字段,无法回查原始业务表。
接口每批最多扫描 500 个学习候选,单次请求最多扫描 10,000 个。批量扫描可以控制内存占用,但筛选条件命中率很低或搜索范围很大时,仍建议使用 filter 限定模型和来源字段。
权限与结果边界
接口会应用当前请求上下文中的权限:
- 只搜索
x-spaces 指定且当前用户有权访问的空间。
- 只返回当前用户有权访问的业务模型和字段。
- 使用
dataFilter 时,用户必须能访问来源字段及筛选条件涉及的字段。
- 回查原始业务表时会应用行级
defaultAuthQuery。
- 指标和没有来源字段的学习候选不会出现在
dataFilter 模式的结果中。
因此,不同用户使用相同参数,得到的结果和 hasNext 可能不同。
常见错误
接入建议
- 搜索输入框应做防抖,并在关键词变化时清空结果和游标。
- 普通模式使用
page;底表筛选模式只使用 cursor,不要混用两种翻页状态。
- 将
filter.schema 和 filter.field 下的 $in 作为性能优化项,而不是底表筛选的必填项;请求体统一使用嵌套对象形式。
- 不要依赖游标内部结构,也不要把游标长期保存为业务数据。
- 生产环境应使用实际服务地址,并妥善保管 Token,避免写入前端源码或日志。