SQL 与语义搜索
用只读 SQL 连表、算比率;用向量近邻按意思找相似对象,候选不超过 10,000 个。

本课目标
读完这一课,你将能够
- 判断一个问题该用对象集、聚合还是 SQL
- 写出连接一对多和多对多视图的只读 SQL,并用参数传值
- 用向量近邻按意思找相似的对象,并说出它的上限
什么时候用 SQL
前两节用对象集和聚合提问。对象集读取返回对象,可以接着沿关系走;聚合返回指标和分组结果。Ontology SQL 返回表格的行,能做连表、比率、窗口函数这类对象集做不了的事。两者读的是同一份当前值,人刚改的值马上就看得见。
| 对象集与聚合 | SQL | |
|---|---|---|
| 返回什么 | 对象集读取返回对象,能沿关系走、带派生属性;聚合返回指标和分组结果 | 表格的行 |
| 擅长 | 沿关系筛选,按接口跨类型取,分页读对象 | 连表、比率、窗口函数、任意组合 |
| 上限 | 一页最多 1,000 个对象;一次分组最多 10,000 组 | 一条 SELECT,最多 10,000 行,20 秒 |
| 在 Nexus 应用里 | 用 semantic.ontology() | 应用票据不走 SQL,不能用 |
选哪个,先问三句:
- 要对象,还是要行?
要拿到对象、接着沿关系走,或者交给应用去展示,用对象集。只要一张表,用 SQL。
- 需要连表、比率或窗口函数吗?
需要就用 SQL。只是按一个属性分组求和,聚合更省事。
- 在哪里跑?
Nexus 应用里用 SDK。分析师、BI 工具、智能体和开发者可以用 SQL。
SQL 只读,写入永远走 Action。
示例工厂里的 SQL
下面使用 Postgres 方言。符合条件的 Object Type 才有视图:部门级、受 Markings 或安全策略保护、名称超过 63 字节、没有存储属性的类型不建视图。表名就是 API name,列名是属性的 API name,含大写字母要加双引号。一对多的关系直接用外键连表。多对多的关系单独一个视图,两端各一列,还带着关系自己的属性:BOM 里每台的用量 quantityPerUnit 就是这样读出来的。接口也有自己的视图,多两列 __objectType 和 __primaryKey,一次就能跨类型查。(表与列的完整规则见实操课的 SQL 一节。)
-- 各产线近 7 天完工工单的报废率:连表拿产线名,再算比率,一条查询出结果
SELECT l.name AS line,
sum(w."goodQty") AS good, sum(w."scrapQty") AS scrap,
round(100.0 * sum(w."scrapQty") / nullif(sum(w."goodQty") + sum(w."scrapQty"), 0), 2) AS scrap_pct
FROM "workOrder" w
JOIN "productionLine" l ON l."lineCode" = w."lineCode"
WHERE w.status = 'done' AND w."dueAt" >= now() - interval '7 days'
GROUP BY l.name
ORDER BY scrap_pct DESC;
-- BOM 缺料:productMaterials 是多对多视图,列名是 <对象类型>_<关系>,值是对面对象的主键
SELECT p.name AS product, m.name AS material,
l."quantityPerUnit", m."onHandQty", m."safetyStockQty"
FROM "productMaterials" l
JOIN product p ON p.sku = l.material_products
JOIN material m ON m."materialCode" = l.product_materials
WHERE m."onHandQty" < m."safetyStockQty"
ORDER BY p.name;
不确定哪一列对着哪一端时,用 aidc semantic database 看表与列。值要放进位置参数 $1、$2,不要拼进 SQL;--explain 只看执行计划,不真正执行。
aidc semantic database
aidc semantic sql --file scrap-by-line.sql --csv
aidc semantic sql 'SELECT status, count(*) FROM "workOrder" WHERE "lineCode" = $1 GROUP BY 1' --param L1
aidc semantic sql --file bom-shortage.sql --explain
Postgres 方言和数据库直连按身份选 reader 或 public,只读,每次查询 20 秒超时。标准 Spark 方言用 aidc semantic sql "…" --dialect spark,按调用者执行行、列和 Markings 权限:不可见对象不返回,不可见属性为 NULL。SQL 里没有派生属性,要用就自己连表再计算。
按意思找:向量与最近邻
关键词搜索找的是字面。「壳体划痕」和「外壳刮伤」说的是同一件事,字面却几乎不重合。向量搜索的做法是:用模型把每段文字变成一个向量(一串数),意思相近的文字,向量也相近;找相似,就是找最近的向量。
在 Semantic 里,向量是一个属性,类型是 vector,定义里写 vectorDimension,最大 4,096。示例工厂给 defect 加一个 descriptionVector,再用 nearestNeighbors 从一个已有缺陷出发,找出最像它的几个:
{ "name": "descriptionVector", "type": "vector", "vectorDimension": 1024, "title": "描述向量" }
查询时,先取出一个缺陷的向量,再找它的近邻:
const seed = await client.objects("defect").fetchOne("D-2031", { $select: ["descriptionVector"] });
const similar = await client.objects("defect")
.where({ severity: { $in: ["major", "critical"] } }) // 先缩小候选
.nearestNeighbors(seed.descriptionVector, 5, "descriptionVector")
.fetchPage(); // 按相似度从高到低
- 相似度用余弦,在内存里算:候选对象(
nearestNeighbors前面的那个对象集)超过 10,000 个就报错,要先用filter缩小。 - 邻居数
numNeighbors取 1 到 100;查询向量的维数要和属性一致,对不上的对象不会出现在结果里。第一名通常就是你拿来查的那个对象自己。 - 向量很长。读对象时用
$select只取要的属性,别把每个对象的向量都拉回来。上线之前,拿两个你已经知道相似的缺陷试一试,看它们是不是互为近邻。
向量怎么来,是你自己的流程:用嵌入模型把文字算成向量,再写进属性。长文档要先切成块,一块一个对象、各带一个向量,再用近邻找最相关的几块。
不过先别急着上向量。要读的文字如果不多、放得进模型的一次输入里,直接给模型读,往往比先检索再读更简单,也更准。向量搜索留给「文字太多、放不下」的时候。
到这里,数据已经进来,也能被问了。下一门课讲动词:怎样用 Action 安全地改这些数据。
要点
- 对象集读取返回对象,聚合返回指标和分组结果,Ontology SQL 返回行;连表、比率、窗口函数用 SQL。
- 表名是 Object Type 的 API name;多对多关系是单独的视图,列名是
objectType_relation,还带着关系自己的属性。 - SQL 只收一条 SELECT,最多 10,000 行、20 秒;值用位置参数传,
--explain只看计划。 - Nexus 应用里不走 SQL,用
semantic.ontology()。 - 向量近邻按意思找相似对象:候选不能超过 10,000 个,先用
filter缩小;文字查询目前还没有。
练一练
用 SQL 与向量再问示例工厂
SQL 用 aidc semantic sql 跑;向量部分先在纸上设计。
先用 aidc semantic database 看 productMaterials 的列,再写出「哪些产品缺料、每台还差多少」的 SQL 并运行。
把报废率查询里的产线改成 $1,用 --param 分别查 L1 和 L2;再加上 --explain 看计划。
为 defect 写出向量属性的定义(维数取决于你的嵌入模型,不超过 4,096)和一条 nearestNeighbors 查询,并写出你用什么条件把候选缩到 10,000 个以内。
小测
选一个答案,马上看解析。
Q1示例工厂的一个 Nexus 应用要读工单数据,能用 SQL 吗?
SQL 给开发者、分析师、BI 和智能体用;应用只用 SDK 读,权限跟着访客的身份走。
Q2对 15,000 个缺陷直接做 nearestNeighbors 会怎样?
余弦相似度在内存里算,候选上限是 10,000 个。超过就报错,而不是悄悄少比一部分。
Q3想直接用一句「壳体有划痕」找相似缺陷(传文字,不传向量),现在能做到吗?
文字查询需要给属性配置嵌入模型,目前还没有。containsAllTerms 找的是字面,找不到意思相近的说法。